committtttt
This commit is contained in:
453
backend/internal/handlers/user.go
Normal file
453
backend/internal/handlers/user.go
Normal file
@@ -0,0 +1,453 @@
|
||||
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",
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user