package handlers import ( "encoding/json" "io" "net/http" "strings" "aggios-app/backend/internal/domain" "aggios-app/backend/internal/service" ) // AuthHandler handles authentication endpoints type AuthHandler struct { authService *service.AuthService } // NewAuthHandler creates a new auth handler func NewAuthHandler(authService *service.AuthService) *AuthHandler { return &AuthHandler{ authService: authService, } } // Register handles user registration func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req domain.CreateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } user, err := h.authService.Register(req) if err != nil { switch err { case service.ErrEmailAlreadyExists: http.Error(w, err.Error(), http.StatusConflict) case service.ErrWeakPassword: http.Error(w, err.Error(), http.StatusBadRequest) default: http.Error(w, "Internal server error", http.StatusInternalServerError) } return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(user) } // Login handles user login func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } bodyBytes, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "Failed to read request body", http.StatusBadRequest) return } defer r.Body.Close() // Trim whitespace to avoid decode errors caused by BOM or stray chars sanitized := strings.TrimSpace(string(bodyBytes)) var req domain.LoginRequest if err := json.Unmarshal([]byte(sanitized), &req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } response, err := h.authService.Login(req) if err != nil { if err == service.ErrInvalidCredentials { http.Error(w, err.Error(), http.StatusUnauthorized) } else { http.Error(w, "Internal server error", http.StatusInternalServerError) } return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } // ChangePasswordRequest represents a password change request type ChangePasswordRequest struct { CurrentPassword string `json:"currentPassword"` NewPassword string `json:"newPassword"` } // ChangePassword handles password change func (h *AuthHandler) ChangePassword(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Get user ID from context (set by auth middleware) userID, ok := r.Context().Value("userID").(string) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req ChangePasswordRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.CurrentPassword == "" || req.NewPassword == "" { http.Error(w, "Current password and new password are required", http.StatusBadRequest) return } // Call auth service to change password if err := h.authService.ChangePassword(userID, req.CurrentPassword, req.NewPassword); err != nil { if err == service.ErrInvalidCredentials { http.Error(w, "Current password is incorrect", http.StatusUnauthorized) } else if err == service.ErrWeakPassword { http.Error(w, "New password is too weak", http.StatusBadRequest) } else { http.Error(w, "Error changing password", http.StatusInternalServerError) } return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "message": "Password changed successfully", }) }