Files
NetworkAuth/controllers/install/install.go
skyle1995 3293fc452a 修修复 因为secure导致的登录失败问题
修复 安装拦截器部分情况下失效的问题
2026-03-31 02:14:08 +08:00

181 lines
6.0 KiB
Go
Raw 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 install
import (
"NetworkAuth/config"
"NetworkAuth/database"
"NetworkAuth/models"
"NetworkAuth/services"
"NetworkAuth/utils"
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// InstallSubmitHandler 处理安装表单提交
func InstallSubmitHandler(c *gin.Context) {
var req struct {
// 数据库配置
DbType string `json:"db_type" binding:"required,oneof=sqlite mysql"`
DbHost string `json:"db_host"`
DbPort int `json:"db_port"`
DbName string `json:"db_name"`
DbUser string `json:"db_user"`
DbPass string `json:"db_pass"`
// 站点和管理员配置
SiteTitle string `json:"site_title" binding:"required"`
AdminUsername string `json:"admin_username" binding:"required"`
AdminPassword string `json:"admin_password" binding:"required,min=6"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误: " + err.Error()})
return
}
// 1. 更新配置文件
err := config.UpdateConfig(func(cfg *config.AppConfig) {
cfg.Database.Type = req.DbType
if req.DbType == "mysql" {
cfg.Database.MySQL.Host = req.DbHost
cfg.Database.MySQL.Port = req.DbPort
cfg.Database.MySQL.Database = req.DbName
cfg.Database.MySQL.Username = req.DbUser
cfg.Database.MySQL.Password = req.DbPass
}
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "更新配置文件失败: " + err.Error()})
return
}
// 2. 使用新配置尝试连接数据库
var testDB *gorm.DB
if req.DbType == "mysql" {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
req.DbUser, req.DbPass, req.DbHost, req.DbPort, req.DbName)
testDB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "连接 MySQL 数据库失败,请检查配置是否正确: " + err.Error()})
return
}
sqlDB, err := testDB.DB()
if err != nil || sqlDB.Ping() != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "连接 MySQL 数据库失败,无法 Ping 通,请检查配置是否正确"})
return
}
}
// 3. 重新初始化全局数据库连接并执行迁移
db, err := database.ReInit()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "连接数据库失败: " + err.Error()})
return
}
if db == nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "获取数据库实例失败,请检查数据库配置是否正确"})
return
}
// 强制执行迁移确保表存在
if err := database.AutoMigrate(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "初始化数据表失败: " + err.Error()})
return
}
// 初始化系统默认设置
database.SeedDefaultSettings()
// 3. 生成新的管理员密码哈希和盐值
adminSalt, err := utils.GenerateRandomSalt()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "生成盐值失败"})
return
}
adminPasswordHash, err := utils.HashPasswordWithSalt(req.AdminPassword, adminSalt)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "加密密码失败"})
return
}
// 开启事务进行更新
tx := db.Begin()
// 更新或创建超级管理员账号
var adminUser models.User
if err := tx.Where("uuid = ?", "00000000-0000-0000-0000-000000000000").First(&adminUser).Error; err != nil {
// 如果不存在则创建
adminUser = models.User{
UUID: "00000000-0000-0000-0000-000000000000",
Username: strings.TrimSpace(req.AdminUsername),
Password: adminPasswordHash,
PasswordSalt: adminSalt,
Nickname: "管理员",
Avatar: "",
Role: 0,
Status: 1,
Remark: "系统默认超级管理员",
}
// 使用 Select("Role") 确保 Role 字段值为0时是零值被显式插入避免使用数据库默认值 1
if err := tx.Select("UUID", "Username", "Password", "PasswordSalt", "Nickname", "Avatar", "Role", "Status", "Remark").Create(&adminUser).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "创建管理员账号失败"})
return
}
} else {
// 存在则更新
adminUser.Username = strings.TrimSpace(req.AdminUsername)
adminUser.Password = adminPasswordHash
adminUser.PasswordSalt = adminSalt
adminUser.Nickname = "管理员"
adminUser.Role = 0
if err := tx.Save(&adminUser).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "更新管理员账号失败"})
return
}
// 确保角色被更新为0GORM的Save可能忽略零值所以额外Update一次
tx.Model(&adminUser).Update("Role", 0)
}
// 如果是新创建的,再额外确保一次 Role 为 0避免 default 标签导致的零值问题
tx.Model(&adminUser).Update("Role", 0)
// 4. 更新设置表
settingsToUpdate := map[string]string{
"site_title": req.SiteTitle,
"is_installed": "1", // 标记为已安装
}
for name, value := range settingsToUpdate {
// 先尝试更新,如果没有该记录,则忽略(因为 AutoMigrate 已经创建了默认记录)
if err := tx.Model(&models.Settings{}).Where("name = ?", name).Update("value", value).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "保存设置失败: " + name})
return
}
}
tx.Commit()
// 5. 更新内存缓存
services.ResetSettingsService()
// 6. 动态初始化核心组件
// 在系统安装完成后,执行本来在 server.go 中需要已安装才能执行的初始化逻辑
encryptionKey := services.GetSettingsService().GetEncryptionKey()
if err := utils.InitEncryption(encryptionKey); err != nil {
logrus.WithError(err).Error("安装完成后加密管理器初始化失败")
}
// 启动日志清理定时任务
services.StartLogCleanupTask()
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "安装成功"})
}