feat: Implement SQLite database for team owner management, configuration, batch processing, and proxies, introducing new auto-add API and Accounts UI.
This commit is contained in:
@@ -17,6 +17,8 @@ var (
|
|||||||
autoAddMu sync.Mutex
|
autoAddMu sync.Mutex
|
||||||
autoAddStopChan chan struct{}
|
autoAddStopChan chan struct{}
|
||||||
lastAutoAddTime time.Time
|
lastAutoAddTime time.Time
|
||||||
|
lastAutoRegTime time.Time // 上次自动注册时间
|
||||||
|
autoRegisterRunning bool // 自动注册是否正在运行
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartAutoAddService 启动自动补号服务(后台检查器)
|
// StartAutoAddService 启动自动补号服务(后台检查器)
|
||||||
@@ -148,6 +150,8 @@ func checkAndAutoAdd() {
|
|||||||
|
|
||||||
if len(owners) == 0 {
|
if len(owners) == 0 {
|
||||||
logger.Warning("没有可用的 Owner 账号,无法自动补号", "", "auto-add")
|
logger.Warning("没有可用的 Owner 账号,无法自动补号", "", "auto-add")
|
||||||
|
// 检查是否开启自动注册
|
||||||
|
tryAutoRegisterOwners(teamsNeeded, 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +159,9 @@ func checkAndAutoAdd() {
|
|||||||
actualTeams := teamsNeeded
|
actualTeams := teamsNeeded
|
||||||
if actualTeams > len(owners) {
|
if actualTeams > len(owners) {
|
||||||
logger.Warning(fmt.Sprintf("可用 Owner 不足: 需要 %d 个, 仅有 %d 个可用", teamsNeeded, len(owners)), "", "auto-add")
|
logger.Warning(fmt.Sprintf("可用 Owner 不足: 需要 %d 个, 仅有 %d 个可用", teamsNeeded, len(owners)), "", "auto-add")
|
||||||
|
// 检查是否开启自动注册,补充不足的母号
|
||||||
|
shortfall := teamsNeeded - len(owners)
|
||||||
|
tryAutoRegisterOwners(shortfall, len(owners))
|
||||||
actualTeams = len(owners)
|
actualTeams = len(owners)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +216,7 @@ func checkAndAutoAdd() {
|
|||||||
Owners: reqOwners,
|
Owners: reqOwners,
|
||||||
MembersPerTeam: membersPerTeam,
|
MembersPerTeam: membersPerTeam,
|
||||||
ConcurrentTeams: concurrentTeams,
|
ConcurrentTeams: concurrentTeams,
|
||||||
S2AConcurrency: s2aConcurrency,
|
ConcurrentS2A: s2aConcurrency,
|
||||||
IncludeOwner: false,
|
IncludeOwner: false,
|
||||||
Headless: true,
|
Headless: true,
|
||||||
BrowserType: browserType,
|
BrowserType: browserType,
|
||||||
@@ -295,3 +302,104 @@ func getS2AAccountCount() (int, error) {
|
|||||||
|
|
||||||
return result.Data.NormalAccounts, nil
|
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")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|||||||
@@ -943,6 +943,12 @@ func (d *DB) DeleteCodexProxy(id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearCodexProxies 清空所有代理
|
||||||
|
func (d *DB) ClearCodexProxies() error {
|
||||||
|
_, err := d.db.Exec("DELETE FROM codex_auth_proxies")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// GetCodexProxyByID 获取指定 ID 的代理地址
|
// GetCodexProxyByID 获取指定 ID 的代理地址
|
||||||
func (d *DB) GetCodexProxyByID(id int64) (string, error) {
|
func (d *DB) GetCodexProxyByID(id int64) (string, error) {
|
||||||
var proxyURL string
|
var proxyURL string
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export default function Accounts() {
|
|||||||
<Select
|
<Select
|
||||||
value={statusFilter}
|
value={statusFilter}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setStatusFilter(e.target.value)
|
setStatusFilter(String(e.target.value))
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}
|
||||||
options={[
|
options={[
|
||||||
|
|||||||
Reference in New Issue
Block a user