| package auth |
|
|
| import ( |
| "encoding/json" |
| "errors" |
| "log/slog" |
| "net/http" |
| ) |
|
|
| type loginRequest struct { |
| Email string `json:"email"` |
| Password string `json:"password"` |
| } |
|
|
| type refreshRequest struct { |
| RefreshToken string `json:"refresh_token"` |
| } |
|
|
| type APIResponse struct { |
| Status string `json:"status"` |
| Message string `json:"message"` |
| Data any `json:"data,omitempty"` |
| } |
|
|
| type Handler struct { |
| svc *Service |
| } |
|
|
| func NewHandler(svc *Service) *Handler { |
| return &Handler{svc: svc} |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { |
| var req loginRequest |
| if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| writeJSON(w, http.StatusBadRequest, APIResponse{Status: "error", Message: "invalid request body"}) |
| return |
| } |
| if req.Email == "" || req.Password == "" { |
| writeJSON(w, http.StatusBadRequest, APIResponse{Status: "error", Message: "email and password are required"}) |
| return |
| } |
|
|
| result, err := h.svc.Login(r.Context(), req.Email, req.Password) |
| if err != nil { |
| h.writeAuthError(w, err) |
| return |
| } |
|
|
| writeJSON(w, http.StatusOK, APIResponse{ |
| Status: "success", |
| Message: "login successful", |
| Data: result, |
| }) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (h *Handler) Refresh(w http.ResponseWriter, r *http.Request) { |
| var req refreshRequest |
| if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| writeJSON(w, http.StatusBadRequest, APIResponse{Status: "error", Message: "invalid request body"}) |
| return |
| } |
| if req.RefreshToken == "" { |
| writeJSON(w, http.StatusBadRequest, APIResponse{Status: "error", Message: "refresh_token is required"}) |
| return |
| } |
| result, err := h.svc.Refresh(r.Context(), req.RefreshToken) |
| if err != nil { |
| h.writeAuthError(w, err) |
| return |
| } |
| writeJSON(w, http.StatusOK, APIResponse{Status: "success", Message: "token refreshed", Data: result}) |
| } |
|
|
| func (h *Handler) writeAuthError(w http.ResponseWriter, err error) { |
| switch { |
| case errors.Is(err, ErrUserNotFound): |
| writeJSON(w, http.StatusNotFound, APIResponse{Status: "error", Message: "user not found"}) |
| case errors.Is(err, ErrInactive): |
| writeJSON(w, http.StatusForbidden, APIResponse{Status: "error", Message: "user account is inactive"}) |
| case errors.Is(err, ErrWrongPassword): |
| writeJSON(w, http.StatusUnauthorized, APIResponse{Status: "error", Message: "invalid credentials"}) |
| case errors.Is(err, ErrInvalidRefreshToken): |
| writeJSON(w, http.StatusUnauthorized, APIResponse{Status: "error", Message: "invalid refresh token"}) |
| case errors.Is(err, ErrTokenServiceUnavailable): |
| writeJSON(w, http.StatusInternalServerError, APIResponse{Status: "error", Message: "token service unavailable"}) |
| default: |
| slog.Error("auth request failed", "err", err) |
| writeJSON(w, http.StatusInternalServerError, APIResponse{Status: "error", Message: "internal server error"}) |
| } |
| } |
|
|
| func writeJSON(w http.ResponseWriter, status int, v any) { |
| w.Header().Set("Content-Type", "application/json") |
| w.WriteHeader(status) |
| _ = json.NewEncoder(w).Encode(v) |
| } |
|
|