Files

339 lines
10 KiB
Go

package handlers
import (
"log"
"net/http"
"net/mail"
"strings"
"github.com/gin-gonic/gin"
"kenaito-notebook-server/services"
)
func isValidEmail(email string) bool {
_, err := mail.ParseAddress(email)
return err == nil
}
type AuthHandler struct {
db *services.Database
auth *services.AuthService
email *services.EmailService
verify *services.VerificationService
}
func NewAuthHandler(db *services.Database, auth *services.AuthService, email *services.EmailService, verify *services.VerificationService) *AuthHandler {
return &AuthHandler{db: db, auth: auth, email: email, verify: verify}
}
type RegisterRequest struct {
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
Code string `json:"code" binding:"required"`
}
type LoginRequest struct {
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
}
type SendCodeRequest struct {
Email string `json:"email" binding:"required"`
Purpose string `json:"purpose" binding:"required"`
}
type VerifyCodeRequest struct {
Email string `json:"email" binding:"required"`
Code string `json:"code" binding:"required"`
Purpose string `json:"purpose" binding:"required"`
}
type ResetPasswordRequest struct {
Email string `json:"email" binding:"required"`
Code string `json:"code" binding:"required"`
NewPassword string `json:"newPassword" binding:"required"`
}
// SendCode 发送验证码
func (h *AuthHandler) SendCode(c *gin.Context) {
var req SendCodeRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("[Auth] SendCode bind error: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写完整信息"})
return
}
if !isValidEmail(req.Email) {
log.Printf("[Auth] SendCode invalid email: %s", req.Email)
c.JSON(http.StatusBadRequest, gin.H{"error": "邮箱格式不正确"})
return
}
if req.Purpose != "register" && req.Purpose != "reset" {
log.Printf("[Auth] SendCode invalid purpose: %s", req.Purpose)
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求类型"})
return
}
// 注册时检查邮箱是否已注册
if req.Purpose == "register" {
existing, _ := h.db.GetUserByEmail(req.Email)
if existing != nil {
log.Printf("[Auth] SendCode email already registered: %s", req.Email)
c.JSON(http.StatusConflict, gin.H{"error": "邮箱已被注册"})
return
}
}
// 重置密码时检查邮箱是否存在
if req.Purpose == "reset" {
existing, _ := h.db.GetUserByEmail(req.Email)
if existing == nil {
log.Printf("[Auth] SendCode email not found for reset: %s", req.Email)
c.JSON(http.StatusNotFound, gin.H{"error": "邮箱未注册"})
return
}
}
// 限频检查
if !h.verify.CheckRateLimit(req.Email, req.Purpose) {
log.Printf("[Auth] SendCode rate limit exceeded: %s, purpose: %s", req.Email, req.Purpose)
c.JSON(http.StatusTooManyRequests, gin.H{"error": "发送过于频繁,请 60 秒后重试"})
return
}
code := h.verify.GenerateCode(req.Email, req.Purpose)
if err := h.email.SendVerificationCode(req.Email, code, req.Purpose); err != nil {
log.Printf("[Auth] SendCode send email failed: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "发送验证码失败"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "验证码已发送"})
}
// VerifyCode 验证验证码
func (h *AuthHandler) VerifyCode(c *gin.Context) {
var req VerifyCodeRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("[Auth] VerifyCode bind error: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写完整信息"})
return
}
if err := h.verify.VerifyCode(req.Email, req.Code, req.Purpose); err != nil {
log.Printf("[Auth] VerifyCode failed: email=%s, purpose=%s, err=%v", req.Email, req.Purpose, err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "验证码正确"})
}
// Register 用户注册
func (h *AuthHandler) Register(c *gin.Context) {
var req RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("[Auth] Register bind error: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写完整信息"})
return
}
if !isValidEmail(req.Email) {
log.Printf("[Auth] Register invalid email: %s", req.Email)
c.JSON(http.StatusBadRequest, gin.H{"error": "邮箱格式不正确"})
return
}
// 检查邮箱是否已注册
existing, _ := h.db.GetUserByEmail(req.Email)
if existing != nil {
log.Printf("[Auth] Register email already registered: %s", req.Email)
c.JSON(http.StatusConflict, gin.H{"error": "邮箱已被注册"})
return
}
// 验证并消耗验证码
if err := h.verify.UseCode(req.Email, req.Code, "register"); err != nil {
log.Printf("[Auth] Register use code failed: email=%s, err=%v", req.Email, err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 哈希密码
hashedPassword, err := h.auth.HashPassword(req.Password)
if err != nil {
log.Printf("[Auth] Register hash password failed: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败"})
return
}
// 创建用户
user, err := h.db.CreateUser(req.Email, hashedPassword)
if err != nil {
log.Printf("[Auth] Register create user failed: email=%s, err=%v", req.Email, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败"})
return
}
// 生成 token
token, err := h.auth.GenerateToken(user.ID, user.Email)
if err != nil {
log.Printf("[Auth] Register generate token failed: userID=%d, err=%v", user.ID, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败"})
return
}
c.JSON(http.StatusCreated, gin.H{
"token": token,
"user": user,
})
}
// Login 用户登录
func (h *AuthHandler) Login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("[Auth] Login bind error: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写邮箱和密码"})
return
}
if !isValidEmail(req.Email) {
log.Printf("[Auth] Login invalid email: %s", req.Email)
c.JSON(http.StatusBadRequest, gin.H{"error": "邮箱格式不正确"})
return
}
// 查找用户
user, err := h.db.GetUserByEmail(req.Email)
if err != nil {
log.Printf("[Auth] Login user not found: %s, err=%v", req.Email, err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "邮箱或密码错误"})
return
}
// 验证密码
if !h.auth.CheckPassword(req.Password, user.HashedPassword) {
log.Printf("[Auth] Login password mismatch: email=%s", req.Email)
c.JSON(http.StatusUnauthorized, gin.H{"error": "邮箱或密码错误"})
return
}
// 生成 token
token, err := h.auth.GenerateToken(user.ID, user.Email)
if err != nil {
log.Printf("[Auth] Login generate token failed: userID=%d, err=%v", user.ID, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "登录失败"})
return
}
c.JSON(http.StatusOK, gin.H{
"token": token,
"user": user,
})
}
// ResetPassword 重置密码
func (h *AuthHandler) ResetPassword(c *gin.Context) {
var req ResetPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("[Auth] ResetPassword bind error: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写完整信息"})
return
}
if !isValidEmail(req.Email) {
log.Printf("[Auth] ResetPassword invalid email: %s", req.Email)
c.JSON(http.StatusBadRequest, gin.H{"error": "邮箱格式不正确"})
return
}
// 验证并消耗验证码
if err := h.verify.UseCode(req.Email, req.Code, "reset"); err != nil {
log.Printf("[Auth] ResetPassword use code failed: email=%s, err=%v", req.Email, err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 查找用户
user, err := h.db.GetUserByEmail(req.Email)
if err != nil {
log.Printf("[Auth] ResetPassword user not found: %s, err=%v", req.Email, err)
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
return
}
// 哈希新密码
hashedPassword, err := h.auth.HashPassword(req.NewPassword)
if err != nil {
log.Printf("[Auth] ResetPassword hash password failed: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "重置失败"})
return
}
// 更新密码
if err := h.db.UpdatePassword(user.ID, hashedPassword); err != nil {
log.Printf("[Auth] ResetPassword update password failed: userID=%d, err=%v", user.ID, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "重置失败"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "密码重置成功"})
}
// GetMe 获取当前用户信息
func (h *AuthHandler) GetMe(c *gin.Context) {
userID, exists := c.Get("userID")
if !exists {
log.Printf("[Auth] GetMe userID not found in context")
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
}
user, err := h.db.GetUserByID(userID.(int64))
if err != nil {
log.Printf("[Auth] GetMe user not found: userID=%v, err=%v", userID, err)
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
return
}
c.JSON(http.StatusOK, user)
}
// AuthMiddleware JWT 认证中间件
func AuthMiddleware(auth *services.AuthService) gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
log.Printf("[Auth] Middleware missing Authorization header: ip=%s", c.ClientIP())
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
c.Abort()
return
}
// 提取 Bearer token
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
log.Printf("[Auth] Middleware invalid auth format: ip=%s", c.ClientIP())
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证格式"})
c.Abort()
return
}
payload, err := auth.ValidateToken(parts[1])
if err != nil {
log.Printf("[Auth] Middleware token validation failed: ip=%s, err=%v", c.ClientIP(), err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "登录已过期,请重新登录"})
c.Abort()
return
}
// 将用户信息存入上下文
c.Set("userID", payload.UserID)
c.Set("email", payload.Email)
c.Next()
}
}