diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-01 22:16:11 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-01 22:16:11 +0300 |
| commit | feafe31716cdbac304dbcd0bce6fe7b205747f0a (patch) | |
| tree | 646a3330be1090ee78cd64166b76cd221ec8f847 /internal/api/handlers_auth.go | |
| parent | af29deb33ee25800976b7122236bf7895a5ff39e (diff) | |
Task 1: Create AuthService and route handleBootstrap/handleLogin through it
Introduce service.AuthService interface with Bootstrap and Login methods,
a concrete authService implementation, and a MockAuthService for testing.
Wire AuthService into api.Server and update cmd/mediaplayer/main.go to use it.
This removes direct store access from handleBootstrap and handleLogin,
fixing the DIP violation. Sentinel errors (ErrAlreadyBootstrapped,
ErrInvalidCredentials) are added to the service package so the API layer
can map them to the correct HTTP status codes without leaking DB details.
Files created:
- internal/service/auth.go
Files modified:
- internal/service/service.go
- internal/service/media.go
- internal/service/mock.go
- internal/repository/repository.go
- internal/repository/mock.go
- internal/api/server.go
- internal/api/handlers_auth.go
- internal/api/handlers_test.go
- internal/api/handlers_more_test.go
- cmd/mediaplayer/main.go
Diffstat (limited to 'internal/api/handlers_auth.go')
| -rw-r--r-- | internal/api/handlers_auth.go | 69 |
1 files changed, 23 insertions, 46 deletions
diff --git a/internal/api/handlers_auth.go b/internal/api/handlers_auth.go index 96991e6..5bf5f39 100644 --- a/internal/api/handlers_auth.go +++ b/internal/api/handlers_auth.go @@ -1,10 +1,11 @@ package api import ( + "errors" "net/http" "time" - "codeberg.org/snonux/player/internal/model" + "codeberg.org/snonux/player/internal/service" ) type bootstrapRequest struct { @@ -22,6 +23,9 @@ type loginRequest struct { // ------------------------------------------------------------------ func (s *Server) handleBootstrap(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.authSvc) { + return + } var req bootstrapRequest if err := readJSON(r, &req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) @@ -32,41 +36,24 @@ func (s *Server) handleBootstrap(w http.ResponseWriter, r *http.Request) { return } - ctx := r.Context() - count, err := s.store.CountUsers(ctx) + res, err := s.authSvc.Bootstrap(r.Context(), req.Username, req.Password) if err != nil { + if errors.Is(err, service.ErrAlreadyBootstrapped) { + writeJSON(w, http.StatusForbidden, map[string]string{"error": "bootstrap already complete"}) + return + } writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal server error"}) return } - if count > 0 { - writeJSON(w, http.StatusForbidden, map[string]string{"error": "bootstrap already complete"}) - return - } - hash, err := s.hasher.Hash(req.Password) - if err != nil { - writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal server error"}) - return - } - - user := &model.User{Username: req.Username, PasswordHash: hash, IsAdmin: true, CreatedAt: time.Now()} - id, err := s.store.CreateUser(ctx, user) - if err != nil { - writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal server error"}) - return - } - user.ID = id - - sessID, err := s.sm.CreateSession(ctx, id) - if err != nil { - writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal server error"}) - return - } - s.setSessionCookie(w, sessID) - writeJSON(w, http.StatusOK, map[string]interface{}{"id": user.ID, "username": user.Username, "is_admin": user.IsAdmin}) + s.setSessionCookie(w, res.SessionID) + writeJSON(w, http.StatusOK, map[string]interface{}{"id": res.User.ID, "username": res.User.Username, "is_admin": res.User.IsAdmin}) } func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) { + if !requireService(w, s.authSvc) { + return + } var req loginRequest if err := readJSON(r, &req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) @@ -77,28 +64,18 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) { return } - ctx := r.Context() - user, err := s.store.GetUserByUsername(ctx, req.Username) - if err != nil { - writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"}) - return - } - if user == nil { - writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"}) - return - } - if err := s.hasher.Compare(user.PasswordHash, req.Password); err != nil { - writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"}) - return - } - - sessID, err := s.sm.CreateSession(ctx, user.ID) + res, err := s.authSvc.Login(r.Context(), req.Username, req.Password) if err != nil { + if errors.Is(err, service.ErrInvalidCredentials) { + writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"}) + return + } writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal server error"}) return } - s.setSessionCookie(w, sessID) - writeJSON(w, http.StatusOK, map[string]interface{}{"id": user.ID, "username": user.Username, "is_admin": user.IsAdmin}) + + s.setSessionCookie(w, res.SessionID) + writeJSON(w, http.StatusOK, map[string]interface{}{"id": res.User.ID, "username": res.User.Username, "is_admin": res.User.IsAdmin}) } func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) { |
