package config import ( "encoding/json" "os" "strconv" "strings" "sync" ) // MailServiceConfig 邮箱服务配置 type MailServiceConfig struct { Name string `yaml:"name" json:"name"` APIBase string `yaml:"api_base" json:"api_base"` APIToken string `yaml:"api_token" json:"api_token"` Domain string `yaml:"domain" json:"domain"` EmailPath string `yaml:"email_path,omitempty" json:"email_path,omitempty"` AddUserAPI string `yaml:"add_user_api,omitempty" json:"add_user_api,omitempty"` } // Config 应用配置 (实时从数据库读取) type Config struct { // 服务器配置 (启动时固定) Port int `json:"port"` CorsOrigin string `json:"cors_origin"` // S2A 配置 (可实时更新) S2AApiBase string `json:"s2a_api_base"` S2AAdminKey string `json:"s2a_admin_key"` // 入库配置 (可实时更新) Concurrency int `json:"concurrency"` Priority int `json:"priority"` GroupIDs []int `json:"group_ids"` ProxyID *int `json:"proxy_id"` // 代理配置 (可实时更新) ProxyEnabled bool `json:"proxy_enabled"` DefaultProxy string `json:"default_proxy"` // 自动化配置 AutoPauseOnExpired bool `json:"auto_pause_on_expired"` AccountsPath string `json:"accounts_path"` // 站点配置 SiteName string `json:"site_name"` // 邮箱服务 MailServices []MailServiceConfig `json:"mail_services"` } // GetProxy 获取代理地址(如果启用) func (c *Config) GetProxy() string { if c.ProxyEnabled && c.DefaultProxy != "" { return c.DefaultProxy } return "" } // Account 账号结构 (保持 JSON 格式用于账号文件) type Account struct { Email string `json:"email,omitempty"` Account string `json:"account,omitempty"` Password string `json:"password"` Name string `json:"name,omitempty"` AccessToken string `json:"access_token,omitempty"` Token string `json:"token,omitempty"` RefreshToken string `json:"refresh_token,omitempty"` CreatedAt string `json:"created_at,omitempty"` Pooled bool `json:"pooled,omitempty"` PooledAt string `json:"pooled_at,omitempty"` PoolID int `json:"pool_id,omitempty"` Used bool `json:"used,omitempty"` UsedAt string `json:"used_at,omitempty"` } // GetEmail 获取邮箱 func (a *Account) GetEmail() string { if a.Email != "" { return a.Email } return a.Account } // GetAccessToken 获取 Token func (a *Account) GetAccessToken() string { if a.AccessToken != "" { return a.AccessToken } return a.Token } // PoolingConfig 入库任务配置 type PoolingConfig struct { Concurrency int `json:"concurrency"` SerialAuthorize bool `json:"serial_authorize"` BrowserType string `json:"browser_type"` Headless bool `json:"headless"` Proxy string `json:"proxy"` } // 全局配置实例 var ( Global *Config configMu sync.RWMutex ) // ConfigDB 配置数据库接口 type ConfigDB interface { GetConfig(key string) (string, error) SetConfig(key, value string) error GetAllConfig() (map[string]string, error) } var configDB ConfigDB // SetConfigDB 设置配置数据库 func SetConfigDB(db ConfigDB) { configDB = db } // InitFromDB 从数据库初始化配置 func InitFromDB() *Config { configMu.Lock() defer configMu.Unlock() cfg := &Config{ Port: 8848, CorsOrigin: "*", Concurrency: 2, Priority: 0, } if configDB == nil { Global = cfg return cfg } // 从数据库加载配置 if v, _ := configDB.GetConfig("s2a_api_base"); v != "" { cfg.S2AApiBase = v } if v, _ := configDB.GetConfig("s2a_admin_key"); v != "" { cfg.S2AAdminKey = v } if v, _ := configDB.GetConfig("concurrency"); v != "" { if n, err := strconv.Atoi(v); err == nil { cfg.Concurrency = n } } if v, _ := configDB.GetConfig("priority"); v != "" { if n, err := strconv.Atoi(v); err == nil { cfg.Priority = n } } if v, _ := configDB.GetConfig("group_ids"); v != "" { var ids []int for _, s := range strings.Split(v, ",") { if n, err := strconv.Atoi(strings.TrimSpace(s)); err == nil { ids = append(ids, n) } } cfg.GroupIDs = ids } if v, _ := configDB.GetConfig("proxy_enabled"); v == "true" { cfg.ProxyEnabled = true } if v, _ := configDB.GetConfig("default_proxy"); v != "" { cfg.DefaultProxy = v } if v, _ := configDB.GetConfig("mail_services"); v != "" { var services []MailServiceConfig if err := json.Unmarshal([]byte(v), &services); err == nil { cfg.MailServices = services } } Global = cfg return cfg } // SaveToDB 保存配置到数据库 func SaveToDB() error { if configDB == nil || Global == nil { return nil } configMu.RLock() cfg := Global configMu.RUnlock() configDB.SetConfig("s2a_api_base", cfg.S2AApiBase) configDB.SetConfig("s2a_admin_key", cfg.S2AAdminKey) configDB.SetConfig("concurrency", strconv.Itoa(cfg.Concurrency)) configDB.SetConfig("priority", strconv.Itoa(cfg.Priority)) if len(cfg.GroupIDs) > 0 { var ids []string for _, id := range cfg.GroupIDs { ids = append(ids, strconv.Itoa(id)) } configDB.SetConfig("group_ids", strings.Join(ids, ",")) } configDB.SetConfig("proxy_enabled", strconv.FormatBool(cfg.ProxyEnabled)) configDB.SetConfig("default_proxy", cfg.DefaultProxy) if len(cfg.MailServices) > 0 { data, _ := json.Marshal(cfg.MailServices) configDB.SetConfig("mail_services", string(data)) } return nil } // Update 更新配置 (实时生效) func Update(cfg *Config) error { configMu.Lock() Global = cfg configMu.Unlock() return SaveToDB() } // Get 获取当前配置 func Get() *Config { configMu.RLock() defer configMu.RUnlock() return Global } // FindPath 查找配置文件路径 (兼容) func FindPath() string { if envPath := os.Getenv("CONFIG_PATH"); envPath != "" { return envPath } return "data/config.yaml" } // Load 加载配置 (兼容旧代码,现在直接从数据库加载) func Load(path string) (*Config, error) { return InitFromDB(), nil } // LoadAccounts 加载账号列表 (保持 JSON 格式) func LoadAccounts(path string) ([]Account, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var accounts []Account if err := json.Unmarshal(data, &accounts); err != nil { return nil, err } return accounts, nil } // SaveAccounts 保存账号列表 (保持 JSON 格式) func SaveAccounts(path string, accounts []Account) error { data, err := json.MarshalIndent(accounts, "", " ") if err != nil { return err } return os.WriteFile(path, data, 0644) }