package handlers import ( "encoding/json" "net/http" "time" "git.nakama.town/fmartingr/hako/internal/model" "git.nakama.town/fmartingr/hako/internal/server/webcontext" ) // UserHandler handles user administration requests type UserHandler struct { deps model.Dependencies } // NewUserHandler creates a new UserHandler func NewUserHandler(deps model.Dependencies) *UserHandler { return &UserHandler{deps: deps} } // AdminUserResponse represents a user in admin API responses type AdminUserResponse struct { ID string `json:"id"` Email string `json:"email"` Role string `json:"role"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } func userToAdminResponse(u *model.User) AdminUserResponse { return AdminUserResponse{ ID: u.ID.String(), Email: u.Email, Role: u.Role, CreatedAt: u.CreatedAt.Format(time.RFC3339), UpdatedAt: u.UpdatedAt.Format(time.RFC3339), } } // writeJSON writes a JSON response with the given status code func writeJSON(w http.ResponseWriter, status int, data any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(data) } // HandleListUsers handles GET /system/users func (h *UserHandler) HandleListUsers(w http.ResponseWriter, r *http.Request) { users, err := h.deps.Domains().Auth().ListUsers() if err != nil { http.Error(w, "Failed to list users", http.StatusInternalServerError) return } response := make([]AdminUserResponse, len(users)) for i, u := range users { response[i] = userToAdminResponse(u) } writeJSON(w, http.StatusOK, response) } // CreateUserRequest represents a request to create a user type CreateUserRequest struct { Email string `json:"email"` Password string `json:"password"` Role string `json:"role"` } // HandleCreateUser handles POST /system/users func (h *UserHandler) HandleCreateUser(w http.ResponseWriter, r *http.Request) { var req CreateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.Email == "" || req.Password == "" { http.Error(w, "Email and password are required", http.StatusBadRequest) return } user, err := h.deps.Domains().Auth().CreateUser(req.Email, req.Password, req.Role) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } writeJSON(w, http.StatusCreated, userToAdminResponse(user)) } // UpdateUserRequest represents a request to update a user type UpdateUserRequest struct { Email string `json:"email"` Role string `json:"role"` } // HandleUpdateUser handles PUT /system/users/{id} func (h *UserHandler) HandleUpdateUser(w http.ResponseWriter, r *http.Request) { c := webcontext.NewWebContext(w, r) id := r.PathValue("id") var req UpdateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.Email == "" || req.Role == "" { http.Error(w, "Email and role are required", http.StatusBadRequest) return } // Self-protection: prevent admin from removing their own admin role if id == c.GetUserID() && req.Role != "admin" { http.Error(w, "Cannot remove your own admin role", http.StatusBadRequest) return } user, err := h.deps.Domains().Auth().UpdateUser(id, req.Email, req.Role) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } writeJSON(w, http.StatusOK, userToAdminResponse(user)) } // UpdatePasswordRequest represents a request to change a user's password type UpdatePasswordRequest struct { Password string `json:"password"` } // HandleUpdatePassword handles PUT /system/users/{id}/password func (h *UserHandler) HandleUpdatePassword(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") var req UpdatePasswordRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.Password == "" { http.Error(w, "Password is required", http.StatusBadRequest) return } if err := h.deps.Domains().Auth().UpdateUserPassword(id, req.Password); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.WriteHeader(http.StatusNoContent) } // HandleDeleteUser handles DELETE /system/users/{id} func (h *UserHandler) HandleDeleteUser(w http.ResponseWriter, r *http.Request) { c := webcontext.NewWebContext(w, r) id := r.PathValue("id") // Self-protection: prevent admin from deleting themselves if id == c.GetUserID() { http.Error(w, "Cannot delete your own account", http.StatusBadRequest) return } if err := h.deps.Domains().Auth().DeleteUser(id); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.WriteHeader(http.StatusNoContent) }