F-6 from t-paliad-074 architecture audit. The Gitea repo was renamed m/patholo → mAi/paliad → m/paliad, but go.mod still declared `mgit.msbls.de/m/patholo` and every internal import echoed the pre-rebrand name. Sweep: - go.mod: module path → mgit.msbls.de/m/paliad - All *.go files: imports rewritten via sed - README.md, docs/design-kanzlai-integration.md: mAi/paliad → m/paliad - Frontend issue-reference comments (mAi/paliad#N → m/paliad#N) in i18n.ts, theme.ts, sidebar.ts, app.ts, Sidebar.tsx, PWAHead.tsx, global.css Verified: go build/vet/test ./... clean, bun run build clean, no remaining mgit.msbls.de/m/patholo or mAi/paliad references outside docs that intentionally describe the rename history.
116 lines
3.6 KiB
Go
116 lines
3.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"mgit.msbls.de/m/paliad/internal/auth"
|
|
"mgit.msbls.de/m/paliad/internal/services"
|
|
)
|
|
|
|
// GET /api/me — returns the caller's paliad.users row (or 404 if onboarding
|
|
// hasn't happened yet). The frontend uses this to gate role-specific UI
|
|
// (partner/admin-only delete, partner-only firm_wide_visible checkbox, etc.).
|
|
//
|
|
// The 404 body includes the caller's JWT email so the onboarding form can
|
|
// pre-fill the display name from the email prefix without a second request.
|
|
func handleGetMe(w http.ResponseWriter, r *http.Request) {
|
|
if !requireDB(w) {
|
|
return
|
|
}
|
|
uid, ok := requireUser(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
u, err := dbSvc.users.GetByID(r.Context(), uid)
|
|
if err != nil {
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"})
|
|
return
|
|
}
|
|
if u == nil {
|
|
body := map[string]string{
|
|
"error": "no paliad.users row — onboarding required",
|
|
}
|
|
if claims, ok := auth.ClaimsFromContext(r.Context()); ok {
|
|
body["email"] = claims.Email
|
|
}
|
|
writeJSON(w, http.StatusNotFound, body)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, u)
|
|
}
|
|
|
|
// PATCH /api/me — mutates the caller's paliad.users row. The settings page
|
|
// sends only the fields the user touched; other fields stay as-is. Email is
|
|
// *not* updatable here — auth.users owns the email and any attempt to set it
|
|
// via this endpoint is ignored (the decode silently drops unknown fields).
|
|
func handleUpdateMe(w http.ResponseWriter, r *http.Request) {
|
|
if !requireDB(w) {
|
|
return
|
|
}
|
|
uid, ok := requireUser(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Decode into a permissive map first so we can detect (and reject) a
|
|
// client that *tried* to change their email — keeping behaviour explicit
|
|
// is friendlier than a silent no-op.
|
|
var peek map[string]json.RawMessage
|
|
if err := json.NewDecoder(r.Body).Decode(&peek); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
|
|
return
|
|
}
|
|
if _, tryingEmail := peek["email"]; tryingEmail {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
"error": "email cannot be changed — contact an administrator",
|
|
})
|
|
return
|
|
}
|
|
var input services.UpdateProfileInput
|
|
// Re-serialise and decode into the typed struct so strict field mapping
|
|
// applies without a second body read.
|
|
if raw, err := json.Marshal(peek); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
|
|
return
|
|
} else if err := json.Unmarshal(raw, &input); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
|
|
return
|
|
}
|
|
|
|
u, err := dbSvc.users.UpdateProfile(r.Context(), uid, input)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, services.ErrUserNotOnboarded):
|
|
writeJSON(w, http.StatusNotFound, map[string]string{
|
|
"error": "no paliad.users row — onboarding required",
|
|
})
|
|
default:
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, u)
|
|
}
|
|
|
|
// GET /api/users — minimal user list for the collaborator picker. Only callable
|
|
// by authenticated users. Response is the full models.User list (email +
|
|
// display_name + office + role).
|
|
func handleListUsers(w http.ResponseWriter, r *http.Request) {
|
|
if !requireDB(w) {
|
|
return
|
|
}
|
|
if _, ok := requireUser(w, r); !ok {
|
|
return
|
|
}
|
|
users, err := dbSvc.users.List(r.Context())
|
|
if err != nil {
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, users)
|
|
}
|
|
|
|
// Removed — superseded by handleListProjectEvents in projects.go.
|