package http import ( "encoding/json" "io" "log" "net/http" "strings" "datarush/internal/auth/domain" "datarush/internal/auth/service" ) type SignUpRequest struct { Email string `json:"email" binding:"required,email"` Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` } type SignInRequest struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` } type TokenResponse struct { Token string `json:"token"` } type UserResponse struct { ID string `json:"id"` Email string `json:"email"` Username string `json:"username"` CreatedAt string `json:"created_at"` } type ErrorResponse struct { Detail string `json:"detail"` } type AuthHandler struct { authService *service.AuthService } func NewAuthHandler(authService *service.AuthService) *AuthHandler { return &AuthHandler{ authService: authService, } } func (h *AuthHandler) SignUp(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req SignUpRequest body, err := io.ReadAll(r.Body) if err != nil { log.Printf("read body error: %v", err) w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"}) return } defer r.Body.Close() if err := json.Unmarshal(body, &req); err != nil { log.Printf("signup validation error: %v", err) w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"}) return } if req.Email == "" || req.Username == "" || req.Password == "" { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"}) return } token, err := h.authService.SignUp(r.Context(), req.Email, req.Username, req.Password) if err != nil { log.Printf("signup error: %v", err) switch err { case domain.ErrUserAlreadyExists: w.WriteHeader(http.StatusConflict) json.NewEncoder(w).Encode(ErrorResponse{Detail: "user already exists"}) default: w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(ErrorResponse{Detail: "internal error"}) } return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(TokenResponse{Token: token}) } func (h *AuthHandler) SignIn(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req SignInRequest body, err := io.ReadAll(r.Body) if err != nil { log.Printf("read body error: %v", err) w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"}) return } defer r.Body.Close() if err := json.Unmarshal(body, &req); err != nil { log.Printf("signin validation error: %v", err) w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"}) return } if req.Email == "" || req.Password == "" { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"}) return } token, err := h.authService.SignIn(r.Context(), req.Email, req.Password) if err != nil { log.Printf("signin error: %v", err) switch err { case domain.ErrInvalidPassword, domain.ErrUserNotFound: w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid credentials"}) default: w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(ErrorResponse{Detail: "internal error"}) } return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(TokenResponse{Token: token}) } func (h *AuthHandler) GetMe(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Extract token from Authorization header authHeader := r.Header.Get("Authorization") if authHeader == "" { w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(ErrorResponse{Detail: "unauthorized"}) return } parts := strings.Split(authHeader, " ") if len(parts) != 2 || parts[0] != "Bearer" { w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(ErrorResponse{Detail: "unauthorized"}) return } token := parts[1] user, err := h.authService.ValidateToken(r.Context(), token) if err != nil { log.Printf("validate token error: %v", err) w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(ErrorResponse{Detail: "unauthorized"}) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(UserResponse{ ID: user.ID.String(), Email: user.Email, Username: user.Username, CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), }) }