package handler import ( "net/http" "strconv" "strings" "time" "api-server/internal/auth" "api-server/internal/db" "github.com/gin-gonic/gin" ) // AuthHandler 认证处理器 type AuthHandler struct { db *db.DB cookieSecure bool } // NewAuthHandler 创建认证处理器 func NewAuthHandler(database *db.DB, cookieSecure bool) *AuthHandler { return &AuthHandler{db: database, cookieSecure: cookieSecure} } // RegisterRoutes 注册路由 func (h *AuthHandler) RegisterRoutes(r *gin.RouterGroup) { r.POST("/auth/register", h.Register) r.POST("/auth/login", h.Login) r.POST("/auth/logout", h.Logout) r.GET("/auth/me", auth.RequireAuth(h.db), h.Me) r.PUT("/auth/profile", auth.RequireAuth(h.db), h.UpdateProfile) r.PUT("/auth/password", auth.RequireAuth(h.db), h.UpdatePassword) } // Register 用户注册 func (h *AuthHandler) Register(c *gin.Context) { // 检查是否开放注册 regEnabled, _ := h.db.GetSystemSetting("registration_enabled") if regEnabled == "0" || regEnabled == "" { c.JSON(http.StatusForbidden, gin.H{"code": 1, "msg": "注册已关闭"}) return } // 检查维护模式 mm, _ := h.db.GetSystemSetting("maintenance_mode") if mm == "1" { c.JSON(http.StatusServiceUnavailable, gin.H{"code": 1, "msg": "系统维护中,请稍后再试"}) return } var req struct { Email string `json:"email"` Password string `json:"password"` Name string `json:"name"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } // 密码规则校验 pwdRule := auth.PasswordRuleFromSettings( h.db.GetSystemSettingOrDefault("password_min_length", "6"), h.db.GetSystemSettingOrDefault("password_require_upper", "0"), h.db.GetSystemSettingOrDefault("password_require_lower", "0"), h.db.GetSystemSettingOrDefault("password_require_digit", "0"), h.db.GetSystemSettingOrDefault("password_require_special", "0"), ) if ok, msg := auth.ValidatePassword(req.Password, pwdRule); !ok { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": msg}) return } req.Email = strings.TrimSpace(strings.ToLower(req.Email)) hash, err := auth.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "密码加密失败"}) return } user, err := h.db.CreateUser(req.Email, hash, req.Name) if err != nil { if strings.Contains(err.Error(), "已注册") { c.JSON(http.StatusConflict, gin.H{"code": 1, "msg": "该邮箱已注册"}) return } c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } expiryHours, _ := h.db.GetSystemSetting("auth_token_expiry_hours") expiry := 7 * 24 * time.Hour if h, err := strconv.Atoi(expiryHours); err == nil && h > 0 { expiry = time.Duration(h) * time.Hour } token, _ := auth.GenerateTokenWithExpiry(user, expiry) expirySec := int(expiry.Seconds()) c.SetCookie("token", token, expirySec, "/", "", h.cookieSecure, true) c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "注册成功", "data": gin.H{ "token": token, "user": userResponse(user), }, }) } // Login 用户登录 func (h *AuthHandler) Login(c *gin.Context) { var req struct { Email string `json:"email" binding:"required"` Password string `json:"password" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "请提供邮箱和密码"}) return } req.Email = strings.TrimSpace(strings.ToLower(req.Email)) user, err := h.db.GetUserByEmail(req.Email) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "邮箱或密码错误"}) return } if !auth.CheckPassword(req.Password, user.PasswordHash) { c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "邮箱或密码错误"}) return } // 检查是否维护模式(仅阻止非管理员) mm, _ := h.db.GetSystemSetting("maintenance_mode") if mm == "1" && user.Role != "admin" { c.JSON(http.StatusServiceUnavailable, gin.H{"code": 1, "msg": "系统维护中,请稍后再试"}) return } // 检查是否需要邮箱验证 verifyReq, _ := h.db.GetSystemSetting("email_verification_required") if verifyReq == "1" && !user.EmailVerified { c.JSON(http.StatusForbidden, gin.H{"code": 403, "msg": "请先验证邮箱后再登录"}) return } // 按设置生成 token 过期时间 expiryHours, _ := h.db.GetSystemSetting("auth_token_expiry_hours") expiry := 7 * 24 * time.Hour if h, err := strconv.Atoi(expiryHours); err == nil && h > 0 { expiry = time.Duration(h) * time.Hour } token, _ := auth.GenerateTokenWithExpiry(user, expiry) expirySec := int(expiry.Seconds()) c.SetCookie("token", token, expirySec, "/", "", h.cookieSecure, true) c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "登录成功", "data": gin.H{ "token": token, "user": userResponse(user), }, }) } // Logout 退出登录 func (h *AuthHandler) Logout(c *gin.Context) { c.SetCookie("token", "", -1, "/", "", h.cookieSecure, true) c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "已退出"}) } // Me 当前用户信息 func (h *AuthHandler) Me(c *gin.Context) { user := auth.GetUser(c) if user == nil { c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "未登录"}) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "data": userResponse(user), }) } func (h *AuthHandler) UpdateProfile(c *gin.Context) { user := auth.GetUser(c) var req struct { Name string `json:"name"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if err := h.db.UpdateUser(user.ID, req.Name); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } updated, err := h.db.GetUserByID(user.ID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "data": userResponse(updated), "msg": "已更新"}) } func (h *AuthHandler) UpdatePassword(c *gin.Context) { user := auth.GetUser(c) var req struct { OldPassword string `json:"old_password" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=6"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "请提供原密码和新密码"}) return } // 密码规则校验 pwdRule := auth.PasswordRuleFromSettings( h.db.GetSystemSettingOrDefault("password_min_length", "6"), h.db.GetSystemSettingOrDefault("password_require_upper", "0"), h.db.GetSystemSettingOrDefault("password_require_lower", "0"), h.db.GetSystemSettingOrDefault("password_require_digit", "0"), h.db.GetSystemSettingOrDefault("password_require_special", "0"), ) if ok, msg := auth.ValidatePassword(req.NewPassword, pwdRule); !ok { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": msg}) return } if !auth.CheckPassword(req.OldPassword, user.PasswordHash) { c.JSON(http.StatusForbidden, gin.H{"code": 403, "msg": "原密码错误"}) return } hash, err := auth.HashPassword(req.NewPassword) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "密码加密失败"}) return } if err := h.db.UpdatePassword(user.Email, hash); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "密码已修改"}) } func userResponse(u *db.User) gin.H { return gin.H{ "id": u.ID, "email": u.Email, "name": u.Name, "role": u.Role, "email_verified": u.EmailVerified, "disabled": u.Disabled, "points": u.Points, } }