Initial commit

This commit is contained in:
2026-05-13 19:42:49 -07:00
commit cfa01bd4ef
54 changed files with 11718 additions and 0 deletions

101
internal/handlers/users.go Normal file
View File

@@ -0,0 +1,101 @@
package handlers
import (
"fmt"
"net/http"
"strings"
"veola/internal/auth"
"veola/internal/models"
"veola/templates"
)
func (a *App) renderSettingsWithUserMsg(w http.ResponseWriter, r *http.Request, msg, errMsg string) {
d, err := a.settingsData(r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
d.UserMsg = msg
d.UserError = errMsg
render(w, r, templates.Settings(d))
}
func (a *App) PostCreateUser(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "bad form", http.StatusBadRequest)
return
}
username := strings.TrimSpace(r.PostFormValue("username"))
password := r.PostFormValue("password")
role := strings.TrimSpace(r.PostFormValue("role"))
if role != string(models.RoleAdmin) {
role = string(models.RoleUser)
}
switch {
case username == "":
a.renderSettingsWithUserMsg(w, r, "", "Username is required")
return
case len(password) < auth.MinPasswordLen:
a.renderSettingsWithUserMsg(w, r, "", fmt.Sprintf("Password must be at least %d characters", auth.MinPasswordLen))
return
}
existing, _ := a.Store.GetUserByUsername(r.Context(), username)
if existing != nil {
a.renderSettingsWithUserMsg(w, r, "", "User already exists")
return
}
hash, err := auth.HashPassword(password)
if err != nil {
a.renderSettingsWithUserMsg(w, r, "", "hash error")
return
}
if _, err := a.Store.CreateUser(r.Context(), username, hash, models.Role(role)); err != nil {
a.renderSettingsWithUserMsg(w, r, "", err.Error())
return
}
a.renderSettingsWithUserMsg(w, r, "Created user "+username, "")
}
func (a *App) PostDeleteUser(w http.ResponseWriter, r *http.Request) {
id := intParam(r, "id")
cur := auth.CurrentUserFromRequest(r)
if cur != nil && cur.ID == id {
a.renderSettingsWithUserMsg(w, r, "", "You cannot delete your own account")
return
}
if err := a.Store.DeleteUser(r.Context(), id); err != nil {
a.renderSettingsWithUserMsg(w, r, "", err.Error())
return
}
a.renderSettingsWithUserMsg(w, r, "User removed", "")
}
func (a *App) PostResetPassword(w http.ResponseWriter, r *http.Request) {
id := intParam(r, "id")
if err := r.ParseForm(); err != nil {
http.Error(w, "bad form", http.StatusBadRequest)
return
}
next := r.PostFormValue("new_password")
if len(next) < auth.MinPasswordLen {
a.renderSettingsWithUserMsg(w, r, "", fmt.Sprintf("Password must be at least %d characters", auth.MinPasswordLen))
return
}
u, err := a.Store.GetUserByID(r.Context(), id)
if err != nil || u == nil {
a.renderSettingsWithUserMsg(w, r, "", "User not found")
return
}
hash, err := auth.HashPassword(next)
if err != nil {
a.renderSettingsWithUserMsg(w, r, "", "hash error")
return
}
if err := a.Store.UpdateUserPassword(r.Context(), id, hash); err != nil {
a.renderSettingsWithUserMsg(w, r, "", err.Error())
return
}
a.renderSettingsWithUserMsg(w, r, "Password reset for "+u.Username, "")
}