From f2757cf3132f6fd1b8911459c05314e8f77391be Mon Sep 17 00:00:00 2001 From: kyx236 Date: Tue, 3 Feb 2026 06:54:40 +0800 Subject: [PATCH] feat: Implement SQLite database for team owner management, configuration, batch processing, and proxies, introducing new auto-add API and Accounts UI. --- backend/internal/api/auto_add.go | 118 ++++++++++++++++++++++++++-- backend/internal/database/sqlite.go | 6 ++ frontend/src/pages/Accounts.tsx | 2 +- 3 files changed, 120 insertions(+), 6 deletions(-) diff --git a/backend/internal/api/auto_add.go b/backend/internal/api/auto_add.go index 3f76cbf..1d79d8e 100644 --- a/backend/internal/api/auto_add.go +++ b/backend/internal/api/auto_add.go @@ -13,10 +13,12 @@ import ( ) var ( - autoAddRunning bool - autoAddMu sync.Mutex - autoAddStopChan chan struct{} - lastAutoAddTime time.Time + autoAddRunning bool + autoAddMu sync.Mutex + autoAddStopChan chan struct{} + lastAutoAddTime time.Time + lastAutoRegTime time.Time // 上次自动注册时间 + autoRegisterRunning bool // 自动注册是否正在运行 ) // StartAutoAddService 启动自动补号服务(后台检查器) @@ -148,6 +150,8 @@ func checkAndAutoAdd() { if len(owners) == 0 { logger.Warning("没有可用的 Owner 账号,无法自动补号", "", "auto-add") + // 检查是否开启自动注册 + tryAutoRegisterOwners(teamsNeeded, 0) return } @@ -155,6 +159,9 @@ func checkAndAutoAdd() { actualTeams := teamsNeeded if actualTeams > len(owners) { logger.Warning(fmt.Sprintf("可用 Owner 不足: 需要 %d 个, 仅有 %d 个可用", teamsNeeded, len(owners)), "", "auto-add") + // 检查是否开启自动注册,补充不足的母号 + shortfall := teamsNeeded - len(owners) + tryAutoRegisterOwners(shortfall, len(owners)) actualTeams = len(owners) } @@ -209,7 +216,7 @@ func checkAndAutoAdd() { Owners: reqOwners, MembersPerTeam: membersPerTeam, ConcurrentTeams: concurrentTeams, - S2AConcurrency: s2aConcurrency, + ConcurrentS2A: s2aConcurrency, IncludeOwner: false, Headless: true, BrowserType: browserType, @@ -295,3 +302,104 @@ func getS2AAccountCount() (int, error) { return result.Data.NormalAccounts, nil } + +// tryAutoRegisterOwners 尝试自动注册母号 +// count: 需要注册的数量 +// currentOwners: 当前可用的母号数量(用于日志) +func tryAutoRegisterOwners(count int, currentOwners int) { + if database.Instance == nil { + return + } + + // 检查是否开启自动注册 + autoRegEnabled := false + if val, _ := database.Instance.GetConfig("monitor_auto_register"); val == "true" { + autoRegEnabled = true + } + if !autoRegEnabled { + logger.Info("自动注册未开启,跳过母号注册", "", "auto-add") + return + } + + // 检查是否有注册任务在运行 + autoAddMu.Lock() + if autoRegisterRunning { + autoAddMu.Unlock() + logger.Info("已有自动注册任务在运行,跳过", "", "auto-add") + return + } + + // 检查 Team 注册是否在运行 + teamRegState.mu.Lock() + teamRegRunning := teamRegState.Running + teamRegState.mu.Unlock() + if teamRegRunning { + autoAddMu.Unlock() + logger.Info("Team 注册任务正在运行,跳过自动注册", "", "auto-add") + return + } + + // 检查距离上次自动注册的间隔(至少 5 分钟) + if time.Since(lastAutoRegTime) < 5*time.Minute { + autoAddMu.Unlock() + remaining := 5*time.Minute - time.Since(lastAutoRegTime) + logger.Info(fmt.Sprintf("自动注册: 冷却中 (还需 %.0f 秒)", remaining.Seconds()), "", "auto-add") + return + } + + autoRegisterRunning = true + lastAutoRegTime = time.Now() + autoAddMu.Unlock() + + // 读取自动注册配置 + concurrency := 2 + if val, _ := database.Instance.GetConfig("monitor_auto_reg_concurrency"); val != "" { + if v, err := strconv.Atoi(val); err == nil && v >= 1 && v <= 10 { + concurrency = v + } + } + + useProxy := false + if val, _ := database.Instance.GetConfig("monitor_auto_reg_use_proxy"); val == "true" { + useProxy = true + } + + // 获取代理地址 + proxyURL := "" + if useProxy && config.Global != nil { + proxyURL = config.Global.DefaultProxy + } + + // 限制单次注册数量(最多 20 个) + if count > 20 { + count = 20 + } + if count < 1 { + count = 1 + } + + logger.Info(fmt.Sprintf("自动注册: 当前母号 %d 个不足,将注册 %d 个新账号 (并发: %d, 代理: %v)", + currentOwners, count, concurrency, useProxy), "", "auto-add") + + // 构建注册配置 + regConfig := TeamRegConfig{ + Count: count, + Concurrency: concurrency, + Proxy: proxyURL, + AutoImport: true, // 自动导入到数据库 + } + + // 异步执行注册 + go func() { + defer func() { + autoAddMu.Lock() + autoRegisterRunning = false + autoAddMu.Unlock() + }() + + // 调用 Team 注册流程 + runTeamRegProcess(regConfig) + + logger.Info("自动注册任务已完成", "", "auto-add") + }() +} diff --git a/backend/internal/database/sqlite.go b/backend/internal/database/sqlite.go index 0e25762..e32de35 100644 --- a/backend/internal/database/sqlite.go +++ b/backend/internal/database/sqlite.go @@ -943,6 +943,12 @@ func (d *DB) DeleteCodexProxy(id int64) error { return err } +// ClearCodexProxies 清空所有代理 +func (d *DB) ClearCodexProxies() error { + _, err := d.db.Exec("DELETE FROM codex_auth_proxies") + return err +} + // GetCodexProxyByID 获取指定 ID 的代理地址 func (d *DB) GetCodexProxyByID(id int64) (string, error) { var proxyURL string diff --git a/frontend/src/pages/Accounts.tsx b/frontend/src/pages/Accounts.tsx index 223c3d1..5ae29f7 100644 --- a/frontend/src/pages/Accounts.tsx +++ b/frontend/src/pages/Accounts.tsx @@ -125,7 +125,7 @@ export default function Accounts() {