Files
QWQLwToo 079ee4eaeb
build-winui / winui (push) Has been cancelled
Add server components
2026-06-26 13:28:09 +08:00

264 lines
6.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handlers
import (
"fmt"
"net/http"
"software-download-center/database"
"software-download-center/models"
"software-download-center/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// RegisterRequest 注册请求
type RegisterRequest struct {
Username string `json:"username" binding:"required,min=3,max=50"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=8"`
}
// LoginRequest 登录请求
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// Register 用户注册
func Register(c *gin.Context) {
var req RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "请求参数错误: " + err.Error(),
})
return
}
// 验证密码强度
if err := utils.ValidatePasswordStrength(req.Password); err != nil {
AddLog("WARN", fmt.Sprintf("注册失败(密码强度不足): 用户名=%s, IP=%s", req.Username, c.ClientIP()))
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
// 检查用户名是否已存在
var existingUser models.User
if err := database.DB.Where("username = ?", req.Username).First(&existingUser).Error; err == nil {
AddLog("WARN", fmt.Sprintf("注册失败(用户名已存在): 用户名=%s, IP=%s", req.Username, c.ClientIP()))
c.JSON(http.StatusConflict, gin.H{
"error": "用户名已存在",
})
return
}
// 检查邮箱是否已存在
if err := database.DB.Where("email = ?", req.Email).First(&existingUser).Error; err == nil {
AddLog("WARN", fmt.Sprintf("注册失败(邮箱已被注册): 邮箱=%s, IP=%s", req.Email, c.ClientIP()))
c.JSON(http.StatusConflict, gin.H{
"error": "邮箱已被注册",
})
return
}
// 检查是否是第一个用户(自动成为管理员)
var userCount int64
database.DB.Model(&models.User{}).Count(&userCount)
isAdmin := userCount == 0
// 加密密码
hashedPassword, err := utils.HashPassword(req.Password)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "密码加密失败",
})
return
}
// 创建用户
user := models.User{
Username: req.Username,
Email: req.Email,
Password: hashedPassword,
IsAdmin: isAdmin,
IsActive: true,
}
if err := database.DB.Create(&user).Error; err != nil {
AddLog("ERROR", fmt.Sprintf("创建用户数据库错误: 用户名=%s - %s, IP=%s", req.Username, err.Error(), c.ClientIP()))
c.JSON(http.StatusInternalServerError, gin.H{
"error": "创建用户失败",
})
return
}
AddLog("INFO", fmt.Sprintf("用户注册成功: 用户名=%s, 邮箱=%s, 管理员=%v, IP=%s", user.Username, user.Email, user.IsAdmin, c.ClientIP()))
// 生成 token
token, err := utils.GenerateToken(user.ID, user.Username, user.IsAdmin)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "生成 token 失败",
})
return
}
// 设置 cookie
c.SetCookie("token", token, 24*3600, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{
"message": "注册成功",
"token": token,
"user": gin.H{
"id": user.ID,
"username": user.Username,
"email": user.Email,
"is_admin": user.IsAdmin,
},
})
}
// Login 用户登录
func Login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "请求参数错误",
})
return
}
// 检查数据库是否已初始化
if !database.IsDBInitialized() {
// 使用默认管理员账号
if req.Username == "admin" && req.Password == "admin123456" {
// 生成临时 token(用于安装页面)
token, err := utils.GenerateToken(0, "admin", true)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "生成 token 失败",
})
return
}
AddLog("INFO", fmt.Sprintf("默认管理员登录成功: 用户名=%s, IP=%s", req.Username, c.ClientIP()))
c.SetCookie("token", token, 24*3600, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{
"message": "登录成功,请配置数据库",
"token": token,
"user": gin.H{
"id": 0,
"username": "admin",
"is_admin": true,
"is_setup": false, // 标记需要安装
},
})
return
}
c.JSON(http.StatusUnauthorized, gin.H{
"error": "数据库未初始化,请使用默认管理员账号登录(admin/admin123456",
})
return
}
// 查找用户
var user models.User
if err := database.DB.Where("username = ?", req.Username).First(&user).Error; err != nil {
if err == gorm.ErrRecordNotFound {
AddLog("WARN", fmt.Sprintf("登录失败(用户不存在): 用户名=%s, IP=%s", req.Username, c.ClientIP()))
c.JSON(http.StatusUnauthorized, gin.H{
"error": "用户名或密码错误",
})
return
}
AddLog("ERROR", fmt.Sprintf("查询用户失败: 用户名=%s - %s, IP=%s", req.Username, err.Error(), c.ClientIP()))
c.JSON(http.StatusInternalServerError, gin.H{
"error": "查询用户失败",
})
return
}
// 检查用户是否激活
if !user.IsActive {
AddLog("WARN", fmt.Sprintf("登录失败(账户已禁用): 用户名=%s, IP=%s", req.Username, c.ClientIP()))
c.JSON(http.StatusForbidden, gin.H{
"error": "账户已被禁用",
})
return
}
// 验证密码
if !utils.CheckPassword(req.Password, user.Password) {
AddLog("WARN", fmt.Sprintf("登录失败(密码错误): 用户名=%s, IP=%s", req.Username, c.ClientIP()))
c.JSON(http.StatusUnauthorized, gin.H{
"error": "用户名或密码错误",
})
return
}
AddLog("INFO", fmt.Sprintf("用户登录成功: 用户名=%s, 管理员=%v, IP=%s", user.Username, user.IsAdmin, c.ClientIP()))
// 生成 token
token, err := utils.GenerateToken(user.ID, user.Username, user.IsAdmin)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "生成 token 失败",
})
return
}
// 设置 cookie
c.SetCookie("token", token, 24*3600, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{
"message": "登录成功",
"token": token,
"user": gin.H{
"id": user.ID,
"username": user.Username,
"email": user.Email,
"is_admin": user.IsAdmin,
},
})
}
// Logout 用户登出
func Logout(c *gin.Context) {
c.SetCookie("token", "", -1, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{
"message": "登出成功",
})
}
// GetCurrentUser 获取当前用户信息
func GetCurrentUser(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "未授权",
})
return
}
var user models.User
if err := database.DB.First(&user, userID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"error": "用户不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"user": gin.H{
"id": user.ID,
"username": user.Username,
"email": user.Email,
"is_admin": user.IsAdmin,
"is_active": user.IsActive,
},
})
}