package handlers import ( "errors" "net/http" "github.com/andyleap/hnh-map/internal/app" "github.com/andyleap/hnh-map/internal/app/apperr" "github.com/andyleap/hnh-map/internal/app/services" ) // Handlers holds HTTP handlers and their dependencies. type Handlers struct { Auth *services.AuthService Map *services.MapService Admin *services.AdminService Client *services.ClientService Export *services.ExportService } // New creates Handlers with the given dependencies. func New( auth *services.AuthService, mapSvc *services.MapService, admin *services.AdminService, client *services.ClientService, export *services.ExportService, ) *Handlers { return &Handlers{ Auth: auth, Map: mapSvc, Admin: admin, Client: client, Export: export, } } // requireAdmin returns session if admin, or writes 401 and returns nil. func (h *Handlers) requireAdmin(rw http.ResponseWriter, req *http.Request) *app.Session { s := h.Auth.GetSession(req.Context(), req) if s == nil || !s.Auths.Has(app.AUTH_ADMIN) { JSONError(rw, http.StatusUnauthorized, "Unauthorized", "UNAUTHORIZED") return nil } return s } // canAccessMap returns true if session has map or admin auth. func (h *Handlers) canAccessMap(s *app.Session) bool { return s != nil && (s.Auths.Has(app.AUTH_MAP) || s.Auths.Has(app.AUTH_ADMIN)) } // HandleServiceError maps service-level errors to HTTP responses. func HandleServiceError(rw http.ResponseWriter, err error) { switch { case errors.Is(err, apperr.ErrNotFound): JSONError(rw, http.StatusNotFound, err.Error(), "NOT_FOUND") case errors.Is(err, apperr.ErrUnauthorized): JSONError(rw, http.StatusUnauthorized, err.Error(), "UNAUTHORIZED") case errors.Is(err, apperr.ErrForbidden): JSONError(rw, http.StatusForbidden, err.Error(), "FORBIDDEN") case errors.Is(err, apperr.ErrBadRequest): JSONError(rw, http.StatusBadRequest, err.Error(), "BAD_REQUEST") case errors.Is(err, apperr.ErrOAuthOnly): JSONError(rw, http.StatusUnauthorized, err.Error(), "OAUTH_ONLY") case errors.Is(err, apperr.ErrProviderUnconfigured): JSONError(rw, http.StatusServiceUnavailable, err.Error(), "PROVIDER_UNCONFIGURED") case errors.Is(err, apperr.ErrStateExpired), errors.Is(err, apperr.ErrStateMismatch): JSONError(rw, http.StatusBadRequest, err.Error(), "BAD_REQUEST") case errors.Is(err, apperr.ErrExchangeFailed), errors.Is(err, apperr.ErrUserInfoFailed): JSONError(rw, http.StatusBadGateway, err.Error(), "OAUTH_ERROR") default: JSONError(rw, http.StatusInternalServerError, "internal error", "INTERNAL_ERROR") } }