Files
apocapoc-api/internal/infrastructure/http/auth_handlers.go
T
david 52ecc2fab5 Add OpenAPI/Swagger documentation
- Add Swagger dependencies to go.mod
- Annotate all API endpoints with Swagger comments
- Add Swagger UI at /api/v1/docs endpoint
- Auto-generate Swagger docs in Dockerfile build
- Update README with API documentation link
2025-11-26 18:38:26 +01:00

143 lines
3.6 KiB
Go

package http
import (
"encoding/json"
"net/http"
"apocapoc-api/internal/application/commands"
"apocapoc-api/internal/application/queries"
"apocapoc-api/internal/infrastructure/auth"
"apocapoc-api/internal/shared/errors"
)
type AuthHandlers struct {
registerHandler *commands.RegisterUserHandler
loginHandler *queries.LoginUserHandler
jwtService *auth.JWTService
}
func NewAuthHandlers(
registerHandler *commands.RegisterUserHandler,
loginHandler *queries.LoginUserHandler,
jwtService *auth.JWTService,
) *AuthHandlers {
return &AuthHandlers{
registerHandler: registerHandler,
loginHandler: loginHandler,
jwtService: jwtService,
}
}
type RegisterRequest struct {
Email string `json:"email"`
Password string `json:"password"`
Timezone string `json:"timezone"`
}
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
type AuthResponse struct {
Token string `json:"token"`
UserID string `json:"user_id"`
}
// Register godoc
// @Summary Register a new user
// @Description Create a new user account
// @Tags auth
// @Accept json
// @Produce json
// @Param request body RegisterRequest true "Registration data"
// @Success 201 {object} AuthResponse
// @Failure 400 {object} ErrorResponse
// @Failure 409 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /auth/register [post]
func (h *AuthHandlers) Register(w http.ResponseWriter, r *http.Request) {
var req RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondError(w, http.StatusBadRequest, "Invalid request body")
return
}
cmd := commands.RegisterUserCommand{
Email: req.Email,
Password: req.Password,
Timezone: req.Timezone,
}
userID, err := h.registerHandler.Handle(r.Context(), cmd)
if err != nil {
if err == errors.ErrInvalidInput {
respondError(w, http.StatusBadRequest, "Invalid email or password (min 8 characters)")
return
}
if err == errors.ErrAlreadyExists {
respondError(w, http.StatusConflict, "Email already registered")
return
}
respondError(w, http.StatusInternalServerError, "Failed to register user")
return
}
token, err := h.jwtService.GenerateToken(userID, req.Email)
if err != nil {
respondError(w, http.StatusInternalServerError, "Failed to generate token")
return
}
respondJSON(w, http.StatusCreated, AuthResponse{
Token: token,
UserID: userID,
})
}
// Login godoc
// @Summary Login user
// @Description Authenticate user and get JWT token
// @Tags auth
// @Accept json
// @Produce json
// @Param request body LoginRequest true "Login credentials"
// @Success 200 {object} AuthResponse
// @Failure 400 {object} ErrorResponse
// @Failure 401 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /auth/login [post]
func (h *AuthHandlers) Login(w http.ResponseWriter, r *http.Request) {
var req LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondError(w, http.StatusBadRequest, "Invalid request body")
return
}
query := queries.LoginUserQuery{
Email: req.Email,
Password: req.Password,
}
result, err := h.loginHandler.Handle(r.Context(), query)
if err != nil {
if err == errors.ErrNotFound || err == errors.ErrInvalidInput {
respondError(w, http.StatusUnauthorized, "Invalid email or password")
return
}
respondError(w, http.StatusInternalServerError, "Failed to login")
return
}
token, err := h.jwtService.GenerateToken(result.UserID, result.Email)
if err != nil {
respondError(w, http.StatusInternalServerError, "Failed to generate token")
return
}
respondJSON(w, http.StatusOK, AuthResponse{
Token: token,
UserID: result.UserID,
})
}