package handlers import ( "net/http" "time" "sale/internal/models" "sale/internal/schemas" "sale/internal/utils" "github.com/gin-gonic/gin" ) type AuthHandler struct{} func NewAuthHandler() *AuthHandler { return &AuthHandler{} } func (h *AuthHandler) Register(c *gin.Context) { var req schemas.RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var existingUser models.User if err := utils.DB.Where("email = ?", req.Email).First(&existingUser).Error; err == nil { c.JSON(http.StatusConflict, gin.H{"error": "Email already registered"}) return } if err := utils.DB.Where("username = ?", req.Username).First(&existingUser).Error; err == nil { c.JSON(http.StatusConflict, gin.H{"error": "Username already taken"}) return } hashedPassword, err := utils.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"}) return } user := models.User{ Username: req.Username, Email: req.Email, PasswordHash: hashedPassword, Role: "user", InviteCode: utils.GenerateInviteCode(), IsActive: true, } if req.InviteCode != "" { var referrer models.User if err := utils.DB.Where("invite_code = ?", req.InviteCode).First(&referrer).Error; err == nil { user.ReferredBy = &referrer.ID } } if err := utils.DB.Create(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"}) return } if user.ReferredBy != nil { utils.DB.Model(&models.User{}).Where("id = ?", *user.ReferredBy). UpdateColumn("purchase_credits", utils.DB.Raw("purchase_credits + 1")) } verifyCode := utils.GenerateVerifyCode() utils.SendVerifyEmail(req.Email, verifyCode) token, _ := utils.GenerateToken(user.ID, user.Role) c.JSON(http.StatusCreated, gin.H{ "token": token, "user": schemas.UserResponse{ ID: user.ID, Username: user.Username, Email: user.Email, Role: user.Role, PurchaseCredits: user.PurchaseCredits, InviteCode: user.InviteCode, EmailVerified: user.EmailVerified, }, }) } func (h *AuthHandler) Login(c *gin.Context) { var req schemas.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var user models.User if err := utils.DB.Where("email = ?", req.Email).First(&user).Error; err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"}) return } if !utils.CheckPassword(req.Password, user.PasswordHash) { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"}) return } if !user.IsActive { c.JSON(http.StatusForbidden, gin.H{"error": "Account is disabled"}) return } token, _ := utils.GenerateToken(user.ID, user.Role) c.JSON(http.StatusOK, gin.H{ "token": token, "user": schemas.UserResponse{ ID: user.ID, Username: user.Username, Email: user.Email, Role: user.Role, PurchaseCredits: user.PurchaseCredits, InviteCode: user.InviteCode, EmailVerified: user.EmailVerified, }, }) } func (h *AuthHandler) ForgotPassword(c *gin.Context) { var req schemas.ForgotPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var user models.User if err := utils.DB.Where("email = ?", req.Email).First(&user).Error; err != nil { c.JSON(http.StatusOK, gin.H{"message": "If the email exists, a verification code has been sent"}) return } code := utils.GenerateVerifyCode() utils.SendResetPasswordEmail(req.Email, code) c.JSON(http.StatusOK, gin.H{"message": "If the email exists, a verification code has been sent"}) } func (h *AuthHandler) ResetPassword(c *gin.Context) { var req schemas.ResetPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var user models.User if err := utils.DB.Where("email = ?", req.Email).First(&user).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email"}) return } hashedPassword, err := utils.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"}) return } utils.DB.Model(&user).Update("password_hash", hashedPassword) c.JSON(http.StatusOK, gin.H{"message": "Password reset successfully"}) } func (h *AuthHandler) VerifyEmail(c *gin.Context) { var req schemas.VerifyEmailRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var user models.User if err := utils.DB.Where("email = ?", req.Email).First(&user).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "User not found"}) return } utils.DB.Model(&user).Update("email_verified", true) c.JSON(http.StatusOK, gin.H{"message": "Email verified successfully"}) } func (h *AuthHandler) GetProfile(c *gin.Context) { userID := c.GetUint("user_id") var user models.User if err := utils.DB.First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } c.JSON(http.StatusOK, schemas.UserResponse{ ID: user.ID, Username: user.Username, Email: user.Email, Role: user.Role, PurchaseCredits: user.PurchaseCredits, InviteCode: user.InviteCode, EmailVerified: user.EmailVerified, }) } func (h *AuthHandler) UpdateProfile(c *gin.Context) { userID := c.GetUint("user_id") var user models.User if err := utils.DB.First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } var updateData map[string]interface{} if err := c.ShouldBindJSON(&updateData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if username, ok := updateData["username"].(string); ok && username != "" { user.Username = username } utils.DB.Save(&user) c.JSON(http.StatusOK, gin.H{"message": "Profile updated successfully"}) } func (h *AuthHandler) ChangePassword(c *gin.Context) { userID := c.GetUint("user_id") var req schemas.ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var user models.User if err := utils.DB.First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } if !utils.CheckPassword(req.OldPassword, user.PasswordHash) { c.JSON(http.StatusBadRequest, gin.H{"error": "Old password is incorrect"}) return } hashedPassword, _ := utils.HashPassword(req.NewPassword) utils.DB.Model(&user).Update("password_hash", hashedPassword) c.JSON(http.StatusOK, gin.H{"message": "Password changed successfully"}) } func (h *AuthHandler) SendVerifyCode(c *gin.Context) { userID := c.GetUint("user_id") var user models.User if err := utils.DB.First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } if user.EmailVerified { c.JSON(http.StatusBadRequest, gin.H{"error": "Email already verified"}) return } code := utils.GenerateVerifyCode() utils.SendVerifyEmail(user.Email, code) c.JSON(http.StatusOK, gin.H{"message": "Verification code sent", "expires_at": time.Now().Add(30 * time.Minute)}) } func (h *AuthHandler) CheckInstalled(c *gin.Context) { var count int64 utils.DB.Model(&models.User{}).Where("role = ?", "admin").Count(&count) c.JSON(http.StatusOK, gin.H{"installed": count > 0}) } func (h *AuthHandler) Install(c *gin.Context) { var count int64 utils.DB.Model(&models.User{}).Where("role = ?", "admin").Count(&count) if count > 0 { c.JSON(http.StatusForbidden, gin.H{"error": "System already installed"}) return } var req schemas.InstallRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } hashedPassword, err := utils.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"}) return } admin := models.User{ Username: req.Username, Email: req.Email, PasswordHash: hashedPassword, Role: "admin", InviteCode: utils.GenerateInviteCode(), IsActive: true, } if err := utils.DB.Create(&admin).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create admin user"}) return } token, _ := utils.GenerateToken(admin.ID, admin.Role) c.JSON(http.StatusCreated, gin.H{ "token": token, "user": schemas.UserResponse{ ID: admin.ID, Username: admin.Username, Email: admin.Email, Role: admin.Role, PurchaseCredits: admin.PurchaseCredits, InviteCode: admin.InviteCode, EmailVerified: admin.EmailVerified, }, }) }