shelves/backend/routes/setup.go
2024-11-15 13:34:42 -05:00

142 lines
3.2 KiB
Go

package routes
import (
"crypto/rand"
"database/sql"
"html/template"
"net/http"
"net/url"
"shelves/backend/auth"
"shelves/backend/errorsx"
"shelves/backend/forms"
"shelves/backend/httpx"
"shelves/backend/templates"
)
func setupRenderView(f setupForm, e setupFormErrors) template.HTML {
body := templates.Form{
Action: "/setup",
Fields: []templates.Field{
templates.Field{
Label: "Display name",
Name: "displayName",
Placeholder: "Jane Doe",
Value: f.displayName,
Error: errorsx.String(e.displayName),
},
templates.Field{
Label: "Password",
Type: "password",
Placeholder: "Password",
Name: "password",
Error: errorsx.String(e.password),
},
templates.Field{
Label: "Confirm password",
Placeholder: "Confirm Password",
Type: "password",
Name: "passwordConfirmation",
},
},
}
return templates.PageBase{Title: "Set up", Body: body.HTML()}.HTML()
}
func queryGetOwnerSettings(db *sql.DB) (setupForm, error) {
form := setupForm{}
err := db.QueryRow(`select display_name from owner_settings`).Scan(&form.displayName)
if err == sql.ErrNoRows {
err = nil
}
return form, err
}
func SetupGet(w http.ResponseWriter, r *http.Request) {
ctx := httpx.GetCtx(r)
if !ctx.Auth.IsAdmin && !ctx.NeedsOwnerSetup {
httpx.LoginRedirect(w, *r.URL)
return
}
form, err := queryGetOwnerSettings(ctx.DB)
if err != nil {
httpx.InternalServerError(w, err)
return
}
html(w, setupRenderView(form, setupFormErrors{}))
}
type setupForm struct {
password string
displayName string
}
type setupFormErrors struct {
password error
displayName error
}
func parseForm(f *setupForm, e *setupFormErrors, vs url.Values, v *forms.Validator) {
f.password = vs.Get("password")
passwordConfirmation := vs.Get("passwordConfirmation")
f.displayName = vs.Get("displayName")
e.password = v.MinLength(f.password, 1)
if e.password == nil && f.password != passwordConfirmation {
e.password = v.Fail("Passwords did not match")
}
e.displayName = v.MinLength(f.displayName, 1)
}
func queryUpdateOwnerSettings(db *sql.DB, display_name string, password_salt []byte, password_hash []byte) error {
_, err := db.Exec(`
insert or replace into owner_settings (display_name, password_salt, password_hash)
values (?, ?, ?)
`, display_name, password_salt, password_hash)
return err
}
func updateOwnerSettings(db *sql.DB, f setupForm) error {
salt := make([]byte, 32)
_, err := rand.Read(salt)
if err != nil {
return err
}
hash := auth.HashPassword([]byte(f.password), salt)
return queryUpdateOwnerSettings(db, f.displayName, salt, hash)
}
func SetupPost(w http.ResponseWriter, r *http.Request) {
ctx := httpx.GetCtx(r)
if !ctx.Auth.IsAdmin && !ctx.NeedsOwnerSetup {
httpx.Unauthorized(w)
return
}
form := setupForm{}
errs := setupFormErrors{}
failed := forms.ParseFormData(r, func(vs url.Values, v *forms.Validator) {
parseForm(&form, &errs, vs, v)
})
if failed {
w.WriteHeader(http.StatusBadRequest)
html(w, setupRenderView(form, errs))
return
}
err := updateOwnerSettings(ctx.DB, form)
if err != nil {
httpx.InternalServerError(w, err)
} else {
httpx.SeeOther(w, "/")
}
}