Files
codexautopool/backend/internal/config/config.go
kyx236 2eb4a57639 feat(mail): Add support for multiple domains per mail service
- Add `Domains` field to MailServiceConfig for managing additional domains under single API
- Implement `matchDomain` helper function for precise and subdomain matching logic
- Update `GetServiceByDomain` to check both primary domain and additional domains list
- Enhance EmailConfig UI to display domain count and allow comma-separated domain input
- Add domains field to mail service request/response structures in API handlers
- Update frontend types to include domains array in MailService interface
- Improve documentation with clarification on primary vs additional domains usage
- Allows single mail service API to manage multiple email domains for verification and operations
2026-02-08 02:57:19 +08:00

301 lines
7.5 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 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"`
Domains []string `yaml:"domains,omitempty" json:"domains,omitempty"` // 附加域名列表同一个API管理的多个域名
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"`
TeamRegProxy string `json:"team_reg_proxy"` // Team 注册使用的代理
// 授权方式配置
AuthMethod string `json:"auth_method"` // "api" 或 "browser"
// 自动化配置
AutoPauseOnExpired bool `json:"auto_pause_on_expired"`
AccountsPath string `json:"accounts_path"`
// 站点配置
SiteName string `json:"site_name"`
// 母号降级配置
DemoteAfterUse bool `json:"demote_after_use"` // 母号使用后自动降级
// 邮箱服务
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("team_reg_proxy"); v != "" {
cfg.TeamRegProxy = v
}
if v, _ := configDB.GetConfig("mail_services"); v != "" {
var services []MailServiceConfig
if err := json.Unmarshal([]byte(v), &services); err == nil {
cfg.MailServices = services
}
}
if v, _ := configDB.GetConfig("site_name"); v != "" {
cfg.SiteName = v
}
if v, _ := configDB.GetConfig("auth_method"); v != "" {
cfg.AuthMethod = v
} else {
cfg.AuthMethod = "browser" // 默认使用浏览器模式
}
if v, _ := configDB.GetConfig("demote_after_use"); v == "true" {
cfg.DemoteAfterUse = true
}
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)
configDB.SetConfig("team_reg_proxy", cfg.TeamRegProxy)
if len(cfg.MailServices) > 0 {
data, _ := json.Marshal(cfg.MailServices)
configDB.SetConfig("mail_services", string(data))
}
if cfg.SiteName != "" {
configDB.SetConfig("site_name", cfg.SiteName)
}
if cfg.AuthMethod != "" {
configDB.SetConfig("auth_method", cfg.AuthMethod)
}
configDB.SetConfig("demote_after_use", strconv.FormatBool(cfg.DemoteAfterUse))
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)
}