package handlers import ( "fmt" "net/http" "strconv" "github.com/gin-gonic/gin" "customer-support-system/internal/auth" "customer-support-system/internal/database" "customer-support-system/internal/models" "customer-support-system/pkg/logger" ) // UserHandler handles user-related HTTP requests type UserHandler struct { authService *auth.AuthService } // NewUserHandler creates a new user handler func NewUserHandler() *UserHandler { return &UserHandler{ authService: auth.NewAuthService(database.GetDB()), } } // Register handles user registration func (h *UserHandler) Register(c *gin.Context) { var req models.CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { logger.WithError(err).Error("Failed to parse register request") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid request body", }) return } // Validate request if err := validateRegisterRequest(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": err.Error(), }) return } // Create user user, err := h.authService.Register(&req) if err != nil { logger.WithError(err).Error("Failed to register user") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to register user", }) return } c.JSON(http.StatusCreated, gin.H{ "success": true, "message": "User registered successfully", "data": gin.H{ "user": user, }, }) } // Login handles user login func (h *UserHandler) Login(c *gin.Context) { var req models.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { logger.WithError(err).Error("Failed to parse login request") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid request body", }) return } // Validate request if err := validateLoginRequest(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": err.Error(), }) return } // Get client IP clientIP := c.ClientIP() // Authenticate user response, err := h.authService.Login(req.Username, req.Password, clientIP) if err != nil { logger.WithError(err).Error("Failed to login user") c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "message": "Invalid username or password", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "User logged in successfully", "data": gin.H{ "user": response.User, "token": response.Token, }, }) } // GetProfile handles getting user profile func (h *UserHandler) GetProfile(c *gin.Context) { // Get user ID from context userID, exists := c.Get("userID") if !exists { logger.Error("Failed to get user ID from context") c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "message": "Unauthorized", }) return } // Get user profile user, err := h.authService.GetUserByID(userID.(uint)) if err != nil { logger.WithError(err).Error("Failed to get user profile") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to get user profile", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ "user": user.ToSafeUser(), }, }) } // UpdateProfile handles updating user profile func (h *UserHandler) UpdateProfile(c *gin.Context) { // Get user ID from context userID, exists := c.Get("userID") if !exists { logger.Error("Failed to get user ID from context") c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "message": "Unauthorized", }) return } var req models.UpdateUserRequest if err := c.ShouldBindJSON(&req); err != nil { logger.WithError(err).Error("Failed to parse update profile request") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid request body", }) return } // Update user profile user, err := h.authService.UpdateUser(userID.(uint), &req) if err != nil { logger.WithError(err).Error("Failed to update user profile") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to update user profile", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "User profile updated successfully", "data": gin.H{ "user": user, }, }) } // ChangePassword handles changing user password func (h *UserHandler) ChangePassword(c *gin.Context) { // Get user ID from context userID, exists := c.Get("userID") if !exists { logger.Error("Failed to get user ID from context") c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "message": "Unauthorized", }) return } var req models.ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { logger.WithError(err).Error("Failed to parse change password request") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid request body", }) return } // Validate request if err := validateChangePasswordRequest(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": err.Error(), }) return } // Change password err := h.authService.ChangePassword(userID.(uint), &req) if err != nil { logger.WithError(err).Error("Failed to change password") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to change password", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Password changed successfully", }) } // validateRegisterRequest validates the register request func validateRegisterRequest(req *models.CreateUserRequest) error { if req.Username == "" { return fmt.Errorf("username is required") } if req.Password == "" { return fmt.Errorf("password is required") } if req.Email == "" { return fmt.Errorf("email is required") } return nil } // validateLoginRequest validates the login request func validateLoginRequest(req *models.LoginRequest) error { if req.Username == "" { return fmt.Errorf("username is required") } if req.Password == "" { return fmt.Errorf("password is required") } return nil } // validateChangePasswordRequest validates the change password request func validateChangePasswordRequest(req *models.ChangePasswordRequest) error { if req.CurrentPassword == "" { return fmt.Errorf("current password is required") } if req.NewPassword == "" { return fmt.Errorf("new password is required") } return nil } // AdminGetUsers handles getting all users (admin only) func (h *UserHandler) AdminGetUsers(c *gin.Context) { // Parse query parameters page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 10 } // Get users var users []models.User var total int64 db := database.GetDB() // Get total count if err := db.Model(&models.User{}).Count(&total).Error; err != nil { logger.WithError(err).Error("Failed to count users") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to count users", }) return } // Get paginated results offset := (page - 1) * pageSize if err := db.Offset(offset).Limit(pageSize).Find(&users).Error; err != nil { logger.WithError(err).Error("Failed to get users") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to get users", }) return } // Convert to safe users safeUsers := make([]models.SafeUser, len(users)) for i, user := range users { safeUsers[i] = user.ToSafeUser() } c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ "users": safeUsers, "total": total, "page": page, "pageSize": pageSize, }, }) } // AdminGetUser handles getting a user by ID (admin only) func (h *UserHandler) AdminGetUser(c *gin.Context) { // Get target user ID from URL params targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { logger.WithError(err).Error("Failed to parse user ID") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid user ID", }) return } // Get user user, err := h.authService.GetUserByID(uint(targetUserID)) if err != nil { logger.WithError(err).Error("Failed to get user") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to get user", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ "user": user.ToSafeUser(), }, }) } // AdminUpdateUser handles updating a user (admin only) func (h *UserHandler) AdminUpdateUser(c *gin.Context) { // Get target user ID from URL params targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { logger.WithError(err).Error("Failed to parse user ID") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid user ID", }) return } var req models.UpdateUserRequest if err := c.ShouldBindJSON(&req); err != nil { logger.WithError(err).Error("Failed to parse update user request") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid request body", }) return } // Update user updatedUser, err := h.authService.UpdateUser(uint(targetUserID), &req) if err != nil { logger.WithError(err).Error("Failed to update user") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to update user", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "User updated successfully", "data": gin.H{ "user": updatedUser, }, }) } // AdminDeleteUser handles deleting a user (admin only) func (h *UserHandler) AdminDeleteUser(c *gin.Context) { // Get target user ID from URL params targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { logger.WithError(err).Error("Failed to parse user ID") c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Invalid user ID", }) return } // Get current user ID from context currentUserID, exists := c.Get("userID") if !exists { logger.Error("Failed to get current user ID from context") c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "message": "Unauthorized", }) return } // Prevent self-deletion if currentUserID.(uint) == uint(targetUserID) { c.JSON(http.StatusBadRequest, gin.H{ "success": false, "message": "Cannot delete your own account", }) return } // Delete user db := database.GetDB() if err := db.Delete(&models.User{}, uint(targetUserID)).Error; err != nil { logger.WithError(err).Error("Failed to delete user") c.JSON(http.StatusInternalServerError, gin.H{ "success": false, "message": "Failed to delete user", }) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "User deleted successfully", }) }