454 lines
11 KiB
Go
454 lines
11 KiB
Go
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",
|
|
})
|
|
}
|