Files
restic-manager/internal/server/http/ui_users.go
T
2026-05-05 10:57:25 +01:00

77 lines
2.2 KiB
Go

// ui_users.go — Settings → Users HTML handlers (admin-only).
//
// Routes (wired in server.go's admin band):
//
// GET /settings/users → handleUIUsersList (this task)
// GET /settings/users/new → F2
// POST /settings/users/new → F2
// GET /settings/users/{id}/edit → F3
// POST /settings/users/{id}/edit → F3
// GET /settings/users/{id}/setup-link → F2
// POST /settings/users/{id}/disable → F3
// POST /settings/users/{id}/enable → F3
// POST /settings/users/{id}/regenerate-setup → F3
// POST /settings/users/{id}/force-logout → F3
package http
import (
"log/slog"
stdhttp "net/http"
)
type usersPage struct {
Users []userRow
ShowDisabled bool
}
type userRow struct {
ID string
Username string
Email string
Role string
LastLoginAt string // pre-formatted "2006-01-02 15:04:05" or "never"
Disabled bool
MustChangePassword bool
}
func (s *Server) handleUIUsersList(w stdhttp.ResponseWriter, r *stdhttp.Request) {
u := s.requireUIUser(w, r)
if u == nil {
return
}
showDisabled := r.URL.Query().Get("show_disabled") == "1"
users, err := s.deps.Store.ListUsers(r.Context())
if err != nil {
slog.Error("ui users: list", "err", err)
stdhttp.Error(w, "internal", stdhttp.StatusInternalServerError)
return
}
rows := make([]userRow, 0, len(users))
for _, ux := range users {
if !showDisabled && ux.DisabledAt != nil {
continue
}
em := ""
if ux.Email != nil {
em = *ux.Email
}
ll := "never"
if ux.LastLoginAt != nil {
ll = ux.LastLoginAt.UTC().Format("2006-01-02 15:04:05")
}
rows = append(rows, userRow{
ID: ux.ID, Username: ux.Username, Email: em,
Role: string(ux.Role), LastLoginAt: ll,
Disabled: ux.DisabledAt != nil,
MustChangePassword: ux.MustChangePassword,
})
}
view := s.baseView(r, u)
view.Title = "Users · restic-manager"
view.Active = "settings"
view.Page = usersPage{Users: rows, ShowDisabled: showDisabled}
if err := s.deps.UI.Render(w, "users", view); err != nil {
slog.Error("ui users: render", "err", err)
}
}