package handlers import ( "errors" "net/http" "goyco/internal/dto" "goyco/internal/repositories" "goyco/internal/validation" "github.com/go-chi/chi/v5" ) type UserHandler struct { userRepo repositories.UserRepository authService AuthServiceInterface } func NewUserHandler(userRepo repositories.UserRepository, authService AuthServiceInterface) *UserHandler { return &UserHandler{ userRepo: userRepo, authService: authService, } } type UserResponse = CommonResponse // @Summary List users // @Description Retrieve a paginated list of users // @Tags users // @Accept json // @Produce json // @Security BearerAuth // @Param limit query int false "Number of users to return" default(20) // @Param offset query int false "Number of users to skip" default(0) // @Success 200 {object} UserResponse "Users retrieved successfully" // @Failure 401 {object} UserResponse "Authentication required" // @Failure 500 {object} UserResponse "Internal server error" // @Router /users [get] func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) { limit, offset := parsePagination(r) users, err := h.userRepo.GetAll(limit, offset) if err != nil { SendErrorResponse(w, "Failed to fetch users", http.StatusInternalServerError) return } userDTOs := dto.ToSanitizedUserDTOs(users) SendSuccessResponse(w, "Users retrieved successfully", map[string]any{ "users": userDTOs, "count": len(userDTOs), "limit": limit, "offset": offset, }) } // @Summary Get user // @Description Retrieve a specific user by ID // @Tags users // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "User ID" // @Success 200 {object} UserResponse "User retrieved successfully" // @Failure 400 {object} UserResponse "Invalid user ID" // @Failure 401 {object} UserResponse "Authentication required" // @Failure 404 {object} UserResponse "User not found" // @Failure 500 {object} UserResponse "Internal server error" // @Router /users/{id} [get] func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) { userID, ok := ParseUintParam(w, r, "id", "User") if !ok { return } user, err := h.userRepo.GetByID(userID) if !HandleRepoError(w, err, "User") { return } userDTO := dto.ToSanitizedUserDTO(user) SendSuccessResponse(w, "User retrieved successfully", userDTO) } // @Summary Create user // @Description Create a new user account // @Tags users // @Accept json // @Produce json // @Security BearerAuth // @Param request body RegisterRequest true "User data" // @Success 201 {object} UserResponse "User created successfully" // @Failure 400 {object} UserResponse "Invalid request data or validation failed" // @Failure 401 {object} UserResponse "Authentication required" // @Failure 409 {object} UserResponse "Username or email already exists" // @Failure 500 {object} UserResponse "Internal server error" // @Router /users [post] func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) { var req struct { Username string `json:"username"` Email string `json:"email"` Password string `json:"password"` } if !DecodeJSONRequest(w, r, &req) { return } if err := validation.ValidateUsername(req.Username); err != nil { SendErrorResponse(w, err.Error(), http.StatusBadRequest) return } if err := validation.ValidateEmail(req.Email); err != nil { SendErrorResponse(w, err.Error(), http.StatusBadRequest) return } if err := validation.ValidatePassword(req.Password); err != nil { SendErrorResponse(w, err.Error(), http.StatusBadRequest) return } result, err := h.authService.Register(req.Username, req.Email, req.Password) if err != nil { var validationErr *validation.ValidationError if errors.As(err, &validationErr) { SendErrorResponse(w, err.Error(), http.StatusBadRequest) return } if !HandleServiceError(w, err, "Failed to create user", http.StatusInternalServerError) { return } } SendCreatedResponse(w, "User created successfully. Verification email sent.", map[string]any{ "user": result.User, "verification_sent": result.VerificationSent, }) } // @Summary Get user posts // @Description Retrieve posts created by a specific user // @Tags users // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "User ID" // @Param limit query int false "Number of posts to return" default(20) // @Param offset query int false "Number of posts to skip" default(0) // @Success 200 {object} UserResponse "User posts retrieved successfully" // @Failure 400 {object} UserResponse "Invalid user ID or pagination parameters" // @Failure 401 {object} UserResponse "Authentication required" // @Failure 500 {object} UserResponse "Internal server error" // @Router /users/{id}/posts [get] func (h *UserHandler) GetUserPosts(w http.ResponseWriter, r *http.Request) { userID, ok := ParseUintParam(w, r, "id", "User") if !ok { return } limit, offset := parsePagination(r) posts, err := h.userRepo.GetPosts(userID, limit, offset) if err != nil { SendErrorResponse(w, "Failed to fetch user posts", http.StatusInternalServerError) return } postDTOs := dto.ToPostDTOs(posts) SendSuccessResponse(w, "User posts retrieved successfully", map[string]any{ "posts": postDTOs, "count": len(postDTOs), "limit": limit, "offset": offset, }) } func (h *UserHandler) MountRoutes(r chi.Router, config RouteModuleConfig) { protected := r if config.AuthMiddleware != nil { protected = r.With(config.AuthMiddleware) } if config.GeneralRateLimit != nil { protected = config.GeneralRateLimit(protected) } protected.Get("/users", h.GetUsers) protected.Post("/users", h.CreateUser) protected.Get("/users/{id}", h.GetUser) protected.Get("/users/{id}/posts", h.GetUserPosts) }