package handler import ( "net/http" "time" "sql-platform/internal/middleware" "sql-platform/internal/model" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" ) // POST /api/v1/auth/login func Login(c *gin.Context) { var req model.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": "username and password required"}) return } var user model.User if err := model.DB.Where("username = ?", req.Username).First(&user).Error; err != nil { c.JSON(http.StatusUnauthorized, gin.H{"detail": "Invalid username or password"}) return } if user.IsActive != nil && !*user.IsActive { c.JSON(http.StatusForbidden, gin.H{"detail": "Account is disabled"}) return } if err := bcrypt.CompareHashAndPassword([]byte(user.HashedPassword), []byte(req.Password)); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"detail": "Invalid username or password"}) return } token, err := middleware.GenerateToken(&user) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": "Failed to generate token"}) return } now := time.Now() user.LastLogin = &now model.DB.Save(&user) c.JSON(http.StatusOK, model.LoginResponse{ AccessToken: token, TokenType: "bearer", User: user, }) } // GET /api/v1/auth/me func GetMe(c *gin.Context) { userID, _, _ := middleware.GetCurrentUser(c) var user model.User if err := model.DB.First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"detail": "User not found"}) return } c.JSON(http.StatusOK, user) } // GET /api/v1/users func ListUsers(c *gin.Context) { var users []model.User model.DB.Order("id").Find(&users) c.JSON(http.StatusOK, users) } // POST /api/v1/users func CreateUser(c *gin.Context) { var input struct { Username string `json:"username" binding:"required"` DisplayName string `json:"display_name" binding:"required"` Password string `json:"password" binding:"required"` Email *string `json:"email"` Phone *string `json:"phone"` Role string `json:"role" binding:"required"` } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) return } hashed, err := bcrypt.GenerateFromPassword([]byte(input.Password), bcrypt.DefaultCost) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": "Failed to hash password"}) return } isActive := true user := model.User{ Username: input.Username, DisplayName: input.DisplayName, HashedPassword: string(hashed), Email: input.Email, Phone: input.Phone, Role: input.Role, IsActive: &isActive, } if err := model.DB.Create(&user).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": "Username already exists"}) return } c.JSON(http.StatusCreated, user) } // PUT /api/v1/users/:id func UpdateUser(c *gin.Context) { id := c.Param("id") var user model.User if err := model.DB.First(&user, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"detail": "User not found"}) return } var input map[string]interface{} if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) return } if pw, ok := input["password"].(string); ok && pw != "" { hashed, _ := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost) input["hashed_password"] = string(hashed) } delete(input, "password") if err := model.DB.Model(&user).Updates(input).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": err.Error()}) return } model.DB.First(&user, id) c.JSON(http.StatusOK, user) } // DELETE /api/v1/users/:id func DeleteUser(c *gin.Context) { id := c.Param("id") if err := model.DB.Delete(&model.User{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "User deleted"}) }