package handlers import ( "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/nutcas3/telecom-platform/apps/api-server/internal/models" "github.com/nutcas3/telecom-platform/apps/api-server/internal/services" ) // AuthHandler handles authentication endpoints type AuthHandler struct { authService *services.AuthService } // NewAuthHandler creates a new auth handler func NewAuthHandler(authService *services.AuthService) *AuthHandler { return &AuthHandler{ authService: authService, } } // LoginRequest represents a login request type LoginRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` } // LoginResponse represents a login response type LoginResponse struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` User *models.User `json:"user"` } // RegisterRequest represents a registration request type RegisterRequest struct { Username string `json:"username" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,min=6"` FirstName string `json:"first_name" binding:"required"` LastName string `json:"last_name" binding:"required"` Role string `json:"role" binding:"required,oneof=admin operator viewer"` } // ChangePasswordRequest represents a password change request type ChangePasswordRequest struct { OldPassword string `json:"old_password" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=6"` } // Login handles user login func (h *AuthHandler) Login(c *gin.Context) { var req LoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } accessToken, refreshToken, user, err := h.authService.Login(req.Username, req.Password) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } response := LoginResponse{ AccessToken: accessToken, RefreshToken: refreshToken, User: user, } c.JSON(http.StatusOK, response) } // Register handles user registration func (h *AuthHandler) Register(c *gin.Context) { var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } user, err := h.authService.Register(req.Username, req.Email, req.Password, req.FirstName, req.LastName, req.Role) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Don't return password hash user.Password = "" c.JSON(http.StatusCreated, gin.H{ "message": "User registered successfully", "user": user, }) } // RefreshToken handles token refresh func (h *AuthHandler) RefreshToken(c *gin.Context) { var req struct { RefreshToken string `json:"refresh_token" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } accessToken, refreshToken, err := h.authService.GetJWTService().RefreshToken(req.RefreshToken) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "access_token": accessToken, "refresh_token": refreshToken, }) } // Logout handles user logout func (h *AuthHandler) Logout(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Authorization header required"}) return } // Extract token from "Bearer " format tokenParts := strings.Split(authHeader, " ") if len(tokenParts) != 2 || tokenParts[0] != "Bearer" { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid authorization header format"}) return } token := tokenParts[1] if err := h.authService.GetJWTService().InvalidateSession(token); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to logout"}) return } c.JSON(http.StatusOK, gin.H{"message": "Logged out successfully"}) } // GetProfile handles getting the current user's profile func (h *AuthHandler) GetProfile(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } user, err := h.authService.GetUserByID(userID.(uint)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } // Don't return password hash user.Password = "" c.JSON(http.StatusOK, user) } // ChangePassword handles password change func (h *AuthHandler) ChangePassword(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } var req ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.authService.ChangePassword(userID.(uint), req.OldPassword, req.NewPassword); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Password changed successfully"}) } // GetUsers handles listing users (admin only) func (h *AuthHandler) GetUsers(c *gin.Context) { // Parse pagination parameters page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) if page < 1 { page = 1 } if limit < 1 || limit > 100 { limit = 20 } offset := (page - 1) * limit var users []models.User var total int64 // Get total count if err := h.authService.GetDB().Model(&models.User{}).Count(&total).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to count users"}) return } // Get users with pagination if err := h.authService.GetDB().Offset(offset).Limit(limit).Find(&users).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users"}) return } // Remove password hashes for i := range users { users[i].Password = "" } c.JSON(http.StatusOK, gin.H{ "users": users, "pagination": gin.H{ "page": page, "limit": limit, "total": total, "total_pages": (total + int64(limit) - 1) / int64(limit), }, }) } // CreateUser handles creating a new user (admin only) func (h *AuthHandler) CreateUser(c *gin.Context) { var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } user, err := h.authService.Register(req.Username, req.Email, req.Password, req.FirstName, req.LastName, req.Role) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Don't return password hash user.Password = "" c.JSON(http.StatusCreated, gin.H{ "message": "User created successfully", "user": user, }) } // UpdateUser handles updating a user (admin only) func (h *AuthHandler) UpdateUser(c *gin.Context) { userIDStr := c.Param("id") userID, err := strconv.ParseUint(userIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) return } var req struct { FirstName *string `json:"first_name"` LastName *string `json:"last_name"` Email *string `json:"email"` Role *string `json:"role"` IsActive *bool `json:"is_active"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Get user var user models.User if err := h.authService.GetDB().First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } // Update fields if provided if req.FirstName != nil { user.FirstName = *req.FirstName } if req.LastName != nil { user.LastName = *req.LastName } if req.Email != nil { user.Email = *req.Email } if req.Role != nil { user.Role = *req.Role } if req.IsActive != nil { user.IsActive = *req.IsActive } if err := h.authService.GetDB().Save(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user"}) return } // Don't return password hash user.Password = "" c.JSON(http.StatusOK, user) } // DeleteUser handles deleting a user (admin only) func (h *AuthHandler) DeleteUser(c *gin.Context) { userIDStr := c.Param("id") userID, err := strconv.ParseUint(userIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) return } // Get current user currentUserID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } // Prevent self-deletion if currentUserID.(uint) == uint(userID) { c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot delete your own account"}) return } // Delete user if err := h.authService.GetDB().Delete(&models.User{}, userID).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete user"}) return } c.JSON(http.StatusOK, gin.H{"message": "User deleted successfully"}) }