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) }