- go.mod module path (and every internal/vibetree import) inventree -> vibetree - cmd/inventree/ -> cmd/vibetree/, deploy/inventree.service -> vibetree.service - Default site name (seed data + the siteName template func's fallback) is now "vibetree", shown in the navbar, <title>, and login page - Session cookie renamed inventree_session -> vibetree_session - Default DB path data/inventree.db -> data/vibetree.db, CLI/log text, Dockerfile, docker-compose.yml, README, .gitignore/.dockerignore all updated to match Existing installs: this changes the session cookie name (users will be signed out once) and the default DB filename (point -db at your existing file, or rename it, to keep your data).
178 lines
5.0 KiB
Go
178 lines
5.0 KiB
Go
package web
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"vibetree/internal/models"
|
|
)
|
|
|
|
// parseRole maps a form's role value to a models.Role, defaulting to
|
|
// RoleUser for anything unrecognized.
|
|
func parseRole(s string) models.Role {
|
|
switch s {
|
|
case "admin":
|
|
return models.RoleAdmin
|
|
case "contact":
|
|
return models.RoleContact
|
|
default:
|
|
return models.RoleUser
|
|
}
|
|
}
|
|
|
|
// randomPassword generates an unguessable password for contact accounts,
|
|
// which can never sign in (see models.User.CanLogin) but still need some
|
|
// non-empty password_hash in storage.
|
|
func randomPassword() (string, error) {
|
|
b := make([]byte, 24)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return "", err
|
|
}
|
|
return hex.EncodeToString(b), nil
|
|
}
|
|
|
|
type usersListData struct {
|
|
User *models.User
|
|
Users []models.User
|
|
}
|
|
|
|
func (a *App) handleUsersList(w http.ResponseWriter, r *http.Request) {
|
|
list, err := a.Users.List(r.Context())
|
|
if err != nil {
|
|
writeErr(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
a.Render(w, http.StatusOK, "users_list", usersListData{User: currentUser(r), Users: list})
|
|
}
|
|
|
|
// handleUserOptions returns enabled users as JSON, for pickers like the
|
|
// asset checkout modal (a currently-assigned but since-disabled user still
|
|
// displays correctly elsewhere; they just won't appear as a new choice).
|
|
func (a *App) handleUserOptions(w http.ResponseWriter, r *http.Request) {
|
|
list, err := a.Users.List(r.Context())
|
|
if err != nil {
|
|
jsonErr(w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
type option struct {
|
|
ID int64 `json:"id"`
|
|
Username string `json:"username"`
|
|
}
|
|
out := make([]option, 0, len(list))
|
|
for _, u := range list {
|
|
if u.Enabled {
|
|
out = append(out, option{ID: u.ID, Username: u.Username})
|
|
}
|
|
}
|
|
jsonOK(w, out)
|
|
}
|
|
|
|
type userFormData struct {
|
|
User *models.User
|
|
EditUser *models.User
|
|
Error string
|
|
}
|
|
|
|
func (a *App) handleUserNewForm(w http.ResponseWriter, r *http.Request) {
|
|
a.Render(w, http.StatusOK, "users_form", userFormData{User: currentUser(r)})
|
|
}
|
|
|
|
func (a *App) handleUserCreate(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.ParseForm(); err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
role := parseRole(r.FormValue("role"))
|
|
u := &models.User{Username: r.FormValue("username"), Role: role, Enabled: r.FormValue("enabled") == "on"}
|
|
password := r.FormValue("password")
|
|
if password == "" {
|
|
if role != models.RoleContact {
|
|
a.Render(w, http.StatusBadRequest, "users_form", userFormData{User: currentUser(r), EditUser: u, Error: "Password is required."})
|
|
return
|
|
}
|
|
generated, err := randomPassword()
|
|
if err != nil {
|
|
writeErr(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
password = generated
|
|
}
|
|
if err := a.Users.Create(r.Context(), u, password); err != nil {
|
|
a.Render(w, http.StatusBadRequest, "users_form", userFormData{User: currentUser(r), EditUser: u, Error: err.Error()})
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/settings/users", http.StatusSeeOther)
|
|
}
|
|
|
|
func (a *App) handleUserEditForm(w http.ResponseWriter, r *http.Request) {
|
|
id, err := pathID(r, "id")
|
|
if err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
u, err := a.Users.Get(r.Context(), id)
|
|
if err != nil {
|
|
writeErr(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
a.Render(w, http.StatusOK, "users_form", userFormData{User: currentUser(r), EditUser: u})
|
|
}
|
|
|
|
func (a *App) handleUserUpdate(w http.ResponseWriter, r *http.Request) {
|
|
id, err := pathID(r, "id")
|
|
if err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := r.ParseForm(); err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
u, err := a.Users.Get(r.Context(), id)
|
|
if err != nil {
|
|
writeErr(w, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
u.Username = r.FormValue("username")
|
|
u.Role = parseRole(r.FormValue("role"))
|
|
u.Enabled = r.FormValue("enabled") == "on"
|
|
if err := a.Users.Update(r.Context(), u); err != nil {
|
|
a.Render(w, http.StatusBadRequest, "users_form", userFormData{User: currentUser(r), EditUser: u, Error: err.Error()})
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/settings/users", http.StatusSeeOther)
|
|
}
|
|
|
|
func (a *App) handleUserPassword(w http.ResponseWriter, r *http.Request) {
|
|
id, err := pathID(r, "id")
|
|
if err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := r.ParseForm(); err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
password := r.FormValue("password")
|
|
if password != "" {
|
|
if err := a.Users.SetPassword(r.Context(), id, password); err != nil {
|
|
a.Log.Warn("set password failed", "error", err)
|
|
}
|
|
}
|
|
http.Redirect(w, r, "/settings/users/"+strconv.FormatInt(id, 10)+"/edit", http.StatusSeeOther)
|
|
}
|
|
|
|
func (a *App) handleUserDelete(w http.ResponseWriter, r *http.Request) {
|
|
id, err := pathID(r, "id")
|
|
if err != nil {
|
|
writeErr(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := a.Users.Delete(r.Context(), id); err != nil {
|
|
a.Log.Warn("delete user failed", "error", err)
|
|
}
|
|
http.Redirect(w, r, "/settings/users", http.StatusSeeOther)
|
|
}
|