From af29deb33ee25800976b7122236bf7895a5ff39e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 1 May 2026 21:10:43 +0300 Subject: refactor(api): split monolithic handlers.go into domain-specific files (task 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split internal/api/handlers.go (1045 lines) to improve KISS/SRP: - handlers_auth.go – bootstrap, login, logout, health, session cookies - handlers_media.go – sets, media CRUD, tags, favorites, notes, progress - handlers_share.go – create/list/revoke shares, share page, share stream - handlers_admin.go – trash, rescan, users, permissions - handlers_file.go – stream, download, thumbnail, regenerate thumbnail Shared helpers (writeJSON, readJSON, pathID, serveFileResult, mimeTypeForFilename, etc.) remain in handlers.go. All tests pass: go test ./... -race -cover. --- internal/api/handlers_admin.go | 135 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 internal/api/handlers_admin.go (limited to 'internal/api/handlers_admin.go') diff --git a/internal/api/handlers_admin.go b/internal/api/handlers_admin.go new file mode 100644 index 0000000..246a466 --- /dev/null +++ b/internal/api/handlers_admin.go @@ -0,0 +1,135 @@ +package api + +import ( + "net/http" + + "codeberg.org/snonux/player/internal/model" +) + +// ------------------------------------------------------------------ +// Admin +// ------------------------------------------------------------------ + +func (s *Server) handleListTrash(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + items, err := s.adminSvc.ListTrash(r.Context()) + if err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, items) +} + +func (s *Server) handleRescan(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + if err := s.adminSvc.TriggerRescan(r.Context()); err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) +} + +func (s *Server) handleListUsers(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + users, err := s.adminSvc.ListUsers(r.Context()) + if err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, users) +} + +func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + var req struct { + Username string `json:"username"` + Password string `json:"password"` + IsAdmin bool `json:"is_admin"` + } + if err := readJSON(r, &req); err != nil || req.Username == "" || req.Password == "" { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"}) + return + } + user, err := s.adminSvc.CreateUser(r.Context(), req.Username, req.Password, req.IsAdmin) + if err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, user) +} + +func (s *Server) handleDeleteUser(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + id := pathID(r, "id") + adminUser, _ := r.Context().Value(userCtxKey).(*model.User) + if adminUser != nil && adminUser.ID == id { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "cannot delete self"}) + return + } + if err := s.adminSvc.DeleteUser(r.Context(), id); err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) +} + +func (s *Server) handleListPermissions(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + perms, err := s.adminSvc.ListPermissions(r.Context()) + if err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, perms) +} + +func (s *Server) handleGrantPermission(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + var req struct { + SetID int64 `json:"set_id"` + UserID int64 `json:"user_id"` + Role model.Role `json:"role"` + } + if err := readJSON(r, &req); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"}) + return + } + if err := s.adminSvc.GrantPermission(r.Context(), req.SetID, req.UserID, req.Role); err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) +} + +func (s *Server) handleRevokePermission(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.adminSvc) { + return + } + var req struct { + SetID int64 `json:"set_id"` + UserID int64 `json:"user_id"` + } + if err := readJSON(r, &req); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"}) + return + } + if err := s.adminSvc.RevokePermission(r.Context(), req.SetID, req.UserID); err != nil { + writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) +} -- cgit v1.2.3