e6956aa001
- React frontend with route-level code splitting - Backend rebranded from Baihu to TaskPool - DB brand migration script and local compatibility
264 lines
7.2 KiB
Go
264 lines
7.2 KiB
Go
package services
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/engigu/taskpool/internal/constant"
|
|
"github.com/engigu/taskpool/internal/database"
|
|
"github.com/engigu/taskpool/internal/logger"
|
|
"github.com/engigu/taskpool/internal/utils"
|
|
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
// InstallRequest 安装请求
|
|
type InstallRequest struct {
|
|
// 数据库配置
|
|
DBType string `json:"db_type" binding:"required"` // sqlite 或 mysql
|
|
DBHost string `json:"db_host"` // MySQL 主机
|
|
DBPort int `json:"db_port"` // MySQL 端口
|
|
DBUser string `json:"db_user"` // MySQL 用户名
|
|
DBPassword string `json:"db_password"` // MySQL 密码
|
|
DBName string `json:"db_name"` // MySQL 数据库名
|
|
DBPath string `json:"db_path"` // SQLite 数据库路径
|
|
DBSSLMode string `json:"db_ssl_mode"` // SSL 模式
|
|
|
|
// Redis 配置(可选)
|
|
RedisEnabled bool `json:"redis_enabled"` // 是否启用 Redis
|
|
RedisHost string `json:"redis_host"` // Redis 主机
|
|
RedisPort int `json:"redis_port"` // Redis 端口
|
|
RedisPassword string `json:"redis_password"` // Redis 密码
|
|
RedisDB int `json:"redis_db"` // Redis 数据库索引
|
|
|
|
// 管理员账号
|
|
AdminUsername string `json:"admin_username" binding:"required"` // 管理员用户名
|
|
AdminPassword string `json:"admin_password" binding:"required"` // 管理员密码
|
|
AdminEmail string `json:"admin_email"` // 管理员邮箱
|
|
|
|
// 站点设置
|
|
SiteTitle string `json:"site_title"` // 站点标题
|
|
SiteSubtitle string `json:"site_subtitle"` // 站点副标题
|
|
}
|
|
|
|
// InstallStatus 安装状态
|
|
type InstallStatus struct {
|
|
Installed bool `json:"installed"`
|
|
ConfigPath string `json:"config_path"`
|
|
DBType string `json:"db_type"`
|
|
}
|
|
|
|
// InstallService 安装服务
|
|
type InstallService struct {
|
|
settingsService *SettingsService
|
|
userService *UserService
|
|
}
|
|
|
|
// NewInstallService 创建安装服务
|
|
func NewInstallService() *InstallService {
|
|
return &InstallService{
|
|
settingsService: NewSettingsService(),
|
|
userService: NewUserService(),
|
|
}
|
|
}
|
|
|
|
// CheckInstallStatus 检查安装状态
|
|
func (s *InstallService) CheckInstallStatus() (*InstallStatus, error) {
|
|
status := &InstallStatus{
|
|
ConfigPath: constant.ConfigPath,
|
|
}
|
|
|
|
// 检查配置文件是否存在
|
|
configExists := false
|
|
if _, err := os.Stat(constant.ConfigPath); err == nil {
|
|
configExists = true
|
|
}
|
|
|
|
// 检查是否已初始化(数据库中有管理员用户)
|
|
initialized := false
|
|
if configExists {
|
|
// 尝试检查是否有管理员用户
|
|
admin := s.userService.GetUserByUsername("admin")
|
|
if admin != nil {
|
|
initialized = true
|
|
}
|
|
}
|
|
|
|
// 同时检查数据库中的初始化标志
|
|
dbInitialized := s.settingsService.Get(constant.SectionSystem, constant.KeyInitialized)
|
|
if dbInitialized == "true" {
|
|
initialized = true
|
|
}
|
|
|
|
status.Installed = initialized
|
|
status.DBType = Config.Database.Type
|
|
|
|
return status, nil
|
|
}
|
|
|
|
// Install 执行安装
|
|
func (s *InstallService) Install(req *InstallRequest) error {
|
|
// 1. 创建配置文件
|
|
if err := s.createConfigFile(req); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 2. 重新加载数据库配置
|
|
if err := s.reloadDatabase(req); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 3. 初始化数据库
|
|
if err := database.Migrate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 4. 初始化设置
|
|
if err := s.settingsService.InitSettings(); err != nil {
|
|
logger.Warnf("[Install] 初始化设置失败: %v", err)
|
|
}
|
|
|
|
// 5. 创建管理员账号
|
|
if err := s.createAdmin(req); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 6. 保存站点设置
|
|
if req.SiteTitle != "" || req.SiteSubtitle != "" {
|
|
siteSettings := make(map[string]string)
|
|
if req.SiteTitle != "" {
|
|
siteSettings[constant.KeyTitle] = req.SiteTitle
|
|
}
|
|
if req.SiteSubtitle != "" {
|
|
siteSettings[constant.KeySubtitle] = req.SiteSubtitle
|
|
}
|
|
if err := s.settingsService.SetSection(constant.SectionSite, siteSettings); err != nil {
|
|
logger.Warnf("[Install] 保存站点设置失败: %v", err)
|
|
}
|
|
}
|
|
|
|
// 7. 标记已初始化
|
|
if err := s.settingsService.Set(constant.SectionSystem, constant.KeyInitialized, "true"); err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Info("[Install] 安装完成")
|
|
return nil
|
|
}
|
|
|
|
// createConfigFile 创建配置文件
|
|
func (s *InstallService) createConfigFile(req *InstallRequest) error {
|
|
// 确保配置目录存在
|
|
configDir := filepath.Dir(constant.ConfigPath)
|
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 创建配置文件
|
|
cfg := ini.Empty()
|
|
|
|
// [server] 配置
|
|
serverSection, _ := cfg.NewSection("server")
|
|
serverSection.NewKey("port", "8052")
|
|
serverSection.NewKey("host", "0.0.0.0")
|
|
serverSection.NewKey("cookie_name", "BHToken")
|
|
|
|
// [database] 配置
|
|
dbSection, _ := cfg.NewSection("database")
|
|
dbSection.NewKey("type", req.DBType)
|
|
|
|
if req.DBType == "sqlite" {
|
|
dbPath := req.DBPath
|
|
if dbPath == "" {
|
|
dbPath = constant.DefaultDBPath
|
|
}
|
|
dbSection.NewKey("path", dbPath)
|
|
} else if req.DBType == "mysql" {
|
|
dbSection.NewKey("host", req.DBHost)
|
|
dbSection.NewKey("port", intToStr(req.DBPort))
|
|
dbSection.NewKey("user", req.DBUser)
|
|
dbSection.NewKey("password", req.DBPassword)
|
|
dbSection.NewKey("dbname", req.DBName)
|
|
if req.DBSSLMode != "" {
|
|
dbSection.NewKey("ssl_mode", req.DBSSLMode)
|
|
}
|
|
}
|
|
dbSection.NewKey("table_prefix", "taskpool_")
|
|
|
|
// [redis] 配置(如果启用)
|
|
if req.RedisEnabled {
|
|
redisSection, _ := cfg.NewSection("redis")
|
|
redisSection.NewKey("enabled", "true")
|
|
redisSection.NewKey("host", req.RedisHost)
|
|
redisSection.NewKey("port", intToStr(req.RedisPort))
|
|
if req.RedisPassword != "" {
|
|
redisSection.NewKey("password", req.RedisPassword)
|
|
}
|
|
redisSection.NewKey("db", intToStr(req.RedisDB))
|
|
}
|
|
|
|
// [security] 配置
|
|
securitySection, _ := cfg.NewSection("security")
|
|
secret := utils.RandomString(32)
|
|
securitySection.NewKey("secret", secret)
|
|
|
|
// 保存配置文件
|
|
if err := cfg.SaveTo(constant.ConfigPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Infof("[Install] 配置文件已保存到: %s", constant.ConfigPath)
|
|
return nil
|
|
}
|
|
|
|
// reloadDatabase 重新加载数据库
|
|
func (s *InstallService) reloadDatabase(req *InstallRequest) error {
|
|
dbCfg := &database.Config{
|
|
Type: req.DBType,
|
|
Host: req.DBHost,
|
|
Port: req.DBPort,
|
|
User: req.DBUser,
|
|
Password: req.DBPassword,
|
|
DBName: req.DBName,
|
|
Path: req.DBPath,
|
|
SSLMode: req.DBSSLMode,
|
|
}
|
|
|
|
if req.DBType == "sqlite" && req.DBPath == "" {
|
|
dbCfg.Path = constant.DefaultDBPath
|
|
}
|
|
|
|
if err := database.Init(dbCfg); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// createAdmin 创建管理员账号
|
|
func (s *InstallService) createAdmin(req *InstallRequest) error {
|
|
// 检查用户是否已存在
|
|
existingUser := s.userService.GetUserByUsername(req.AdminUsername)
|
|
if existingUser != nil {
|
|
logger.Info("[Install] 管理员账号已存在,跳过创建")
|
|
return nil
|
|
}
|
|
|
|
email := req.AdminEmail
|
|
if email == "" {
|
|
email = "admin@local"
|
|
}
|
|
|
|
s.userService.CreateUser(req.AdminUsername, req.AdminPassword, email, "admin")
|
|
logger.Infof("[Install] 管理员账号创建成功: %s", req.AdminUsername)
|
|
return nil
|
|
}
|
|
|
|
// intToStr 整数转字符串
|
|
func intToStr(n int) string {
|
|
if n == 0 {
|
|
return ""
|
|
}
|
|
return utils.IntToStr(n)
|
|
}
|