feat: Introduce core application structure, configuration, monitoring, and team management features.
This commit is contained in:
@@ -128,8 +128,16 @@ func checkAndAutoAdd() {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算需要多少个 Team(每个 Team 产生 4 个账号)
|
||||
teamsNeeded := (deficit + 3) / 4 // 向上取整
|
||||
// 读取每 Team 成员数配置
|
||||
membersPerTeam := 4
|
||||
if val, _ := database.Instance.GetConfig("monitor_members_per_team"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil && v >= 1 && v <= 10 {
|
||||
membersPerTeam = v
|
||||
}
|
||||
}
|
||||
|
||||
// 计算需要多少个 Team
|
||||
teamsNeeded := (deficit + membersPerTeam - 1) / membersPerTeam // 向上取整
|
||||
|
||||
// 获取可用的 Owner
|
||||
owners, err := database.Instance.GetPendingOwners()
|
||||
@@ -164,13 +172,14 @@ func checkAndAutoAdd() {
|
||||
}
|
||||
}
|
||||
|
||||
// 读取代理配置
|
||||
// 读取代理配置 - 支持代理池模式
|
||||
proxyURL := ""
|
||||
replenishUseProxy := false
|
||||
if val, _ := database.Instance.GetConfig("monitor_replenish_use_proxy"); val == "true" {
|
||||
replenishUseProxy = true
|
||||
}
|
||||
if replenishUseProxy {
|
||||
// 使用全局代理配置(支持 pool:random, pool:id:N 等格式)
|
||||
proxyURL = config.Global.DefaultProxy
|
||||
}
|
||||
|
||||
@@ -180,10 +189,27 @@ func checkAndAutoAdd() {
|
||||
browserType = val
|
||||
}
|
||||
|
||||
// 读取并发 Team 数配置
|
||||
concurrentTeams := 2
|
||||
if val, _ := database.Instance.GetConfig("monitor_concurrent_teams"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil && v >= 1 && v <= 10 {
|
||||
concurrentTeams = v
|
||||
}
|
||||
}
|
||||
|
||||
// 读取入库并发数配置
|
||||
s2aConcurrency := 2
|
||||
if val, _ := database.Instance.GetConfig("monitor_s2a_concurrency"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil && v >= 1 && v <= 4 {
|
||||
s2aConcurrency = v
|
||||
}
|
||||
}
|
||||
|
||||
req := TeamProcessRequest{
|
||||
Owners: reqOwners,
|
||||
MembersPerTeam: 4,
|
||||
ConcurrentTeams: 2,
|
||||
MembersPerTeam: membersPerTeam,
|
||||
ConcurrentTeams: concurrentTeams,
|
||||
S2AConcurrency: s2aConcurrency,
|
||||
IncludeOwner: false,
|
||||
Headless: true,
|
||||
BrowserType: browserType,
|
||||
@@ -192,7 +218,13 @@ func checkAndAutoAdd() {
|
||||
|
||||
// 输出代理使用状态日志
|
||||
if proxyURL != "" {
|
||||
logger.Info(fmt.Sprintf("自动补号: 使用代理 %s", proxyURL), "", "auto-add")
|
||||
displayProxy := proxyURL
|
||||
if proxyURL == "pool:random" {
|
||||
displayProxy = "代理池轮询模式"
|
||||
} else if len(proxyURL) > 8 && proxyURL[:8] == "pool:id:" {
|
||||
displayProxy = fmt.Sprintf("代理池固定项 (ID: %s)", proxyURL[8:])
|
||||
}
|
||||
logger.Info(fmt.Sprintf("自动补号: 使用代理 %s", displayProxy), "", "auto-add")
|
||||
} else {
|
||||
logger.Info("自动补号: 未使用代理", "", "auto-add")
|
||||
}
|
||||
@@ -209,7 +241,8 @@ func checkAndAutoAdd() {
|
||||
// 异步执行
|
||||
go runTeamProcess(req)
|
||||
|
||||
logger.Success(fmt.Sprintf("自动补号任务已启动: %d 个 Team (浏览器: %s)", actualTeams, browserType), "", "auto-add")
|
||||
logger.Success(fmt.Sprintf("自动补号任务已启动: %d 个 Team, 每 Team %d 成员, 并发 %d (浏览器: %s)",
|
||||
actualTeams, membersPerTeam, concurrentTeams, browserType), "", "auto-add")
|
||||
}
|
||||
|
||||
// getS2AAccountCount 获取 S2A 当前账号数量
|
||||
|
||||
@@ -11,14 +11,20 @@ import (
|
||||
|
||||
// MonitorSettings 监控设置
|
||||
type MonitorSettings struct {
|
||||
Target int `json:"target"`
|
||||
AutoAdd bool `json:"auto_add"`
|
||||
MinInterval int `json:"min_interval"`
|
||||
CheckInterval int `json:"check_interval"` // 自动补号检查间隔(秒)
|
||||
PollingEnabled bool `json:"polling_enabled"`
|
||||
PollingInterval int `json:"polling_interval"`
|
||||
ReplenishUseProxy bool `json:"replenish_use_proxy"` // 补号时使用代理
|
||||
BrowserType string `json:"browser_type"` // 授权浏览器引擎: chromedp 或 rod
|
||||
Target int `json:"target"`
|
||||
AutoAdd bool `json:"auto_add"`
|
||||
AutoRegister bool `json:"auto_register"` // 母号不足时自动注册
|
||||
AutoRegConcurrency int `json:"auto_reg_concurrency"` // 自动注册并发数
|
||||
AutoRegUseProxy bool `json:"auto_reg_use_proxy"` // 自动注册时使用代理
|
||||
MinInterval int `json:"min_interval"`
|
||||
CheckInterval int `json:"check_interval"` // 自动补号检查间隔(秒)
|
||||
PollingEnabled bool `json:"polling_enabled"`
|
||||
PollingInterval int `json:"polling_interval"`
|
||||
ReplenishUseProxy bool `json:"replenish_use_proxy"` // 补号时使用代理
|
||||
BrowserType string `json:"browser_type"` // 授权浏览器引擎: chromedp 或 rod
|
||||
MembersPerTeam int `json:"members_per_team"` // 每 Team 成员数
|
||||
ConcurrentTeams int `json:"concurrent_teams"` // 并发 Team 数
|
||||
S2AConcurrency int `json:"s2a_concurrency"` // 入库并发数
|
||||
}
|
||||
|
||||
// HandleGetMonitorSettings 获取监控设置
|
||||
@@ -34,14 +40,20 @@ func HandleGetMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
settings := MonitorSettings{
|
||||
Target: 50,
|
||||
AutoAdd: false,
|
||||
MinInterval: 300,
|
||||
CheckInterval: 60,
|
||||
PollingEnabled: false,
|
||||
PollingInterval: 60,
|
||||
ReplenishUseProxy: false,
|
||||
BrowserType: "chromedp", // 默认使用 chromedp
|
||||
Target: 50,
|
||||
AutoAdd: false,
|
||||
AutoRegister: false,
|
||||
AutoRegConcurrency: 2,
|
||||
AutoRegUseProxy: false,
|
||||
MinInterval: 300,
|
||||
CheckInterval: 60,
|
||||
PollingEnabled: false,
|
||||
PollingInterval: 60,
|
||||
ReplenishUseProxy: false,
|
||||
BrowserType: "chromedp", // 默认使用 chromedp
|
||||
MembersPerTeam: 4, // 默认每 Team 4 个成员
|
||||
ConcurrentTeams: 2, // 默认并发 2 个 Team
|
||||
S2AConcurrency: 2, // 默认入库并发 2
|
||||
}
|
||||
|
||||
if val, _ := database.Instance.GetConfig("monitor_target"); val != "" {
|
||||
@@ -52,6 +64,17 @@ func HandleGetMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if val, _ := database.Instance.GetConfig("monitor_auto_add"); val == "true" {
|
||||
settings.AutoAdd = true
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_auto_register"); val == "true" {
|
||||
settings.AutoRegister = true
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_auto_reg_concurrency"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil {
|
||||
settings.AutoRegConcurrency = v
|
||||
}
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_auto_reg_use_proxy"); val == "true" {
|
||||
settings.AutoRegUseProxy = true
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_min_interval"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil {
|
||||
settings.MinInterval = v
|
||||
@@ -76,6 +99,21 @@ func HandleGetMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if val, _ := database.Instance.GetConfig("monitor_browser_type"); val != "" {
|
||||
settings.BrowserType = val
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_members_per_team"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil {
|
||||
settings.MembersPerTeam = v
|
||||
}
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_concurrent_teams"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil {
|
||||
settings.ConcurrentTeams = v
|
||||
}
|
||||
}
|
||||
if val, _ := database.Instance.GetConfig("monitor_s2a_concurrency"); val != "" {
|
||||
if v, err := strconv.Atoi(val); err == nil {
|
||||
settings.S2AConcurrency = v
|
||||
}
|
||||
}
|
||||
|
||||
Success(w, settings)
|
||||
}
|
||||
@@ -113,6 +151,22 @@ func HandleSaveMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if err := database.Instance.SetConfig("monitor_auto_add", strconv.FormatBool(settings.AutoAdd)); err != nil {
|
||||
saveErrors = append(saveErrors, "auto_add: "+err.Error())
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_auto_register", strconv.FormatBool(settings.AutoRegister)); err != nil {
|
||||
saveErrors = append(saveErrors, "auto_register: "+err.Error())
|
||||
}
|
||||
// 自动注册并发数 (1-10)
|
||||
autoRegConcurrency := settings.AutoRegConcurrency
|
||||
if autoRegConcurrency < 1 {
|
||||
autoRegConcurrency = 1
|
||||
} else if autoRegConcurrency > 10 {
|
||||
autoRegConcurrency = 10
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_auto_reg_concurrency", strconv.Itoa(autoRegConcurrency)); err != nil {
|
||||
saveErrors = append(saveErrors, "auto_reg_concurrency: "+err.Error())
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_auto_reg_use_proxy", strconv.FormatBool(settings.AutoRegUseProxy)); err != nil {
|
||||
saveErrors = append(saveErrors, "auto_reg_use_proxy: "+err.Error())
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_min_interval", strconv.Itoa(settings.MinInterval)); err != nil {
|
||||
saveErrors = append(saveErrors, "min_interval: "+err.Error())
|
||||
}
|
||||
@@ -141,6 +195,36 @@ func HandleSaveMonitorSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if err := database.Instance.SetConfig("monitor_browser_type", browserType); err != nil {
|
||||
saveErrors = append(saveErrors, "browser_type: "+err.Error())
|
||||
}
|
||||
// 每 Team 成员数 (1-10)
|
||||
membersPerTeam := settings.MembersPerTeam
|
||||
if membersPerTeam < 1 {
|
||||
membersPerTeam = 1
|
||||
} else if membersPerTeam > 10 {
|
||||
membersPerTeam = 10
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_members_per_team", strconv.Itoa(membersPerTeam)); err != nil {
|
||||
saveErrors = append(saveErrors, "members_per_team: "+err.Error())
|
||||
}
|
||||
// 并发 Team 数 (1-10)
|
||||
concurrentTeams := settings.ConcurrentTeams
|
||||
if concurrentTeams < 1 {
|
||||
concurrentTeams = 1
|
||||
} else if concurrentTeams > 10 {
|
||||
concurrentTeams = 10
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_concurrent_teams", strconv.Itoa(concurrentTeams)); err != nil {
|
||||
saveErrors = append(saveErrors, "concurrent_teams: "+err.Error())
|
||||
}
|
||||
// 入库并发数 (1-4)
|
||||
s2aConcurrency := settings.S2AConcurrency
|
||||
if s2aConcurrency < 1 {
|
||||
s2aConcurrency = 1
|
||||
} else if s2aConcurrency > 4 {
|
||||
s2aConcurrency = 4
|
||||
}
|
||||
if err := database.Instance.SetConfig("monitor_s2a_concurrency", strconv.Itoa(s2aConcurrency)); err != nil {
|
||||
saveErrors = append(saveErrors, "s2a_concurrency: "+err.Error())
|
||||
}
|
||||
|
||||
if len(saveErrors) > 0 {
|
||||
errMsg := "保存监控设置部分失败: " + saveErrors[0]
|
||||
|
||||
@@ -148,11 +148,11 @@ func HandleBanCheckSettings(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
settings := map[string]interface{}{
|
||||
"enabled": false,
|
||||
"interval": 1800, // 检查服务间隔(秒)
|
||||
"check_hours": 24, // 多少小时后重新检查
|
||||
"last_check": lastBanCheckTime,
|
||||
"task_state": banCheckTaskState,
|
||||
"enabled": false,
|
||||
"interval": 1800, // 检查服务间隔(秒)
|
||||
"check_hours": 24, // 多少小时后重新检查
|
||||
"last_check": lastBanCheckTime,
|
||||
"task_state": banCheckTaskState,
|
||||
"service_running": banCheckRunning,
|
||||
}
|
||||
|
||||
@@ -225,9 +225,9 @@ func HandleManualBanCheck(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var req struct {
|
||||
IDs []int64 `json:"ids"` // 指定检查的母号 ID,为空则检查所有有效母号
|
||||
Concurrency int `json:"concurrency"` // 并发数
|
||||
ForceCheck bool `json:"force_check"` // 是否强制检查(忽略上次检查时间)
|
||||
IDs []int64 `json:"ids"` // 指定检查的母号 ID,为空则检查所有有效母号
|
||||
Concurrency int `json:"concurrency"` // 并发数
|
||||
ForceCheck bool `json:"force_check"` // 是否强制检查(忽略上次检查时间)
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
// 允许空 body
|
||||
@@ -315,10 +315,10 @@ func runBanCheckTask(owners []database.TeamOwner, concurrency int) {
|
||||
taskChan := make(chan database.TeamOwner, len(owners))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 获取代理配置
|
||||
proxy := ""
|
||||
// 获取代理配置模式
|
||||
proxyStr := ""
|
||||
if config.Global != nil {
|
||||
proxy = config.Global.GetProxy()
|
||||
proxyStr = config.Global.GetProxy()
|
||||
}
|
||||
|
||||
// 启动 worker
|
||||
@@ -327,7 +327,9 @@ func runBanCheckTask(owners []database.TeamOwner, concurrency int) {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for owner := range taskChan {
|
||||
result := checkSingleOwnerBan(owner, proxy)
|
||||
// 每次循环重新解析代理,支持轮询
|
||||
resolvedProxy := database.Instance.ResolveProxy(proxyStr)
|
||||
result := checkSingleOwnerBan(owner, resolvedProxy)
|
||||
|
||||
// 更新计数
|
||||
atomic.AddInt32(&banCheckTaskState.Checked, 1)
|
||||
|
||||
@@ -155,12 +155,15 @@ func HandleTeamProcess(w http.ResponseWriter, r *http.Request) {
|
||||
req.Proxy = config.Global.GetProxy() // 使用新的代理获取方法
|
||||
}
|
||||
if req.Proxy != "" {
|
||||
normalized, err := proxyutil.Normalize(req.Proxy)
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, fmt.Sprintf("代理格式错误: %v", err))
|
||||
return
|
||||
// 如果是模式字符串(pool:random, pool:id:N),跳过 Normalize
|
||||
if !strings.HasPrefix(req.Proxy, "pool:") && req.Proxy != "[RANDOM]" {
|
||||
normalized, err := proxyutil.Normalize(req.Proxy)
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, fmt.Sprintf("代理格式错误: %v", err))
|
||||
return
|
||||
}
|
||||
req.Proxy = normalized
|
||||
}
|
||||
req.Proxy = normalized
|
||||
}
|
||||
|
||||
// 初始化状态
|
||||
@@ -466,7 +469,9 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
|
||||
// Step 1: 获取 Team ID(优先使用已存储的 account_id)
|
||||
var teamID string
|
||||
inviter := invite.NewWithProxy(owner.Token, req.Proxy)
|
||||
// Resolve proxy for inviter
|
||||
resolvedProxy := database.Instance.ResolveProxy(req.Proxy)
|
||||
inviter := invite.NewWithProxy(owner.Token, resolvedProxy)
|
||||
|
||||
if owner.AccountID != "" {
|
||||
// 直接使用数据库中存储的 account_id
|
||||
@@ -581,7 +586,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
|
||||
// 入库单个成员的函数
|
||||
doS2A := func(memberIdx int, memberEmail, memberPassword string) bool {
|
||||
memberLogPrefix := fmt.Sprintf("%s [成员 %d]", logPrefix, memberIdx+1)
|
||||
memberLogPrefix := fmt.Sprintf("%s [Member %d]", logPrefix, memberIdx+1)
|
||||
memberStartTime := time.Now()
|
||||
|
||||
// 获取入库信号量
|
||||
@@ -598,15 +603,21 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", memberLogPrefix, attempt+1), memberEmail, "team")
|
||||
}
|
||||
|
||||
// 创建日志回调
|
||||
// 创建日志回调 - 只显示关键步骤
|
||||
authLogger := auth.NewAuthLogger(memberEmail, logPrefix, memberIdx+1, func(entry auth.AuthLogEntry) {
|
||||
if entry.IsError {
|
||||
logger.Error(fmt.Sprintf("%s %s", memberLogPrefix, entry.Message), memberEmail, "team")
|
||||
} else {
|
||||
// 只显示关键步骤:提交邮箱、验证密码、选择工作区、授权成功
|
||||
switch entry.Step {
|
||||
case auth.StepNavigate, auth.StepInputEmail, auth.StepInputPassword,
|
||||
auth.StepComplete, auth.StepConsent, auth.StepSelectWorkspace:
|
||||
logger.Info(fmt.Sprintf("%s %s", memberLogPrefix, entry.Message), memberEmail, "team")
|
||||
case auth.StepInputEmail:
|
||||
logger.Info(fmt.Sprintf("%s 提交邮箱: %s", memberLogPrefix, memberEmail), memberEmail, "team")
|
||||
case auth.StepInputPassword:
|
||||
logger.Info(fmt.Sprintf("%s 验证密码...", memberLogPrefix), memberEmail, "team")
|
||||
case auth.StepSelectWorkspace:
|
||||
logger.Info(fmt.Sprintf("%s 选择工作区: %s", memberLogPrefix, teamID), memberEmail, "team")
|
||||
case auth.StepComplete:
|
||||
logger.Info(fmt.Sprintf("%s 授权成功,获取到授权码", memberLogPrefix), memberEmail, "team")
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -622,10 +633,9 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
// 根据配置选择授权方式
|
||||
var code string
|
||||
if config.Global.AuthMethod == "api" {
|
||||
proxyToUse := req.Proxy
|
||||
if poolProxy, poolErr := database.Instance.GetRandomCodexProxy(); poolErr == nil && poolProxy != "" {
|
||||
proxyToUse = poolProxy
|
||||
logger.Info(fmt.Sprintf("%s 使用代理池: %s", memberLogPrefix, getProxyDisplay(poolProxy)), memberEmail, "team")
|
||||
proxyToUse := database.Instance.ResolveProxy(req.Proxy)
|
||||
if proxyToUse != req.Proxy && proxyToUse != "" {
|
||||
logger.Info(fmt.Sprintf("%s 使用解析代理: %s", memberLogPrefix, getProxyDisplay(proxyToUse)), memberEmail, "team")
|
||||
}
|
||||
code, err = auth.CompleteWithCodexAPI(memberEmail, memberPassword, teamID, s2aResp.Data.AuthURL, s2aResp.Data.SessionID, proxyToUse, authLogger)
|
||||
if proxyToUse != req.Proxy && proxyToUse != "" {
|
||||
@@ -681,7 +691,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
registerAndS2AMember := func(memberIdx int, email, password string) bool {
|
||||
name := register.GenerateName()
|
||||
birthdate := register.GenerateBirthdate()
|
||||
memberLogPrefix := fmt.Sprintf("%s [成员 %d]", logPrefix, memberIdx+1)
|
||||
memberLogPrefix := fmt.Sprintf("%s [Member %d]", logPrefix, memberIdx+1)
|
||||
regStartTime := time.Now()
|
||||
|
||||
for attempt := 0; attempt < 2; attempt++ { // 最多尝试2次(首次+1次重试)
|
||||
@@ -764,7 +774,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
}
|
||||
email := mail.GenerateEmail()
|
||||
password := register.GeneratePassword()
|
||||
logger.Info(fmt.Sprintf("%s [成员 %d] 邮箱: %s | 密码: %s", logPrefix, idx+1, email, password), email, "team")
|
||||
logger.Info(fmt.Sprintf("%s [Member %d] Email: %s | Password: %s", logPrefix, idx+1, email, password), email, "team")
|
||||
registerAndS2AMember(idx, email, password)
|
||||
}(i)
|
||||
}
|
||||
|
||||
@@ -297,8 +297,14 @@ func runTeamRegProcess(config TeamRegConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
// 解析代理
|
||||
resolvedProxy := database.Instance.ResolveProxy(config.Proxy)
|
||||
if config.Proxy != resolvedProxy && config.Proxy != "" && resolvedProxy != "" {
|
||||
addTeamRegLog(fmt.Sprintf("[系统] 代理模式: %s -> %s", config.Proxy, resolvedProxy))
|
||||
}
|
||||
|
||||
addTeamRegLog(fmt.Sprintf("[系统] 配置: 数量=%d, 并发=%d, 代理=%s",
|
||||
config.Count, config.Concurrency, config.Proxy))
|
||||
config.Count, config.Concurrency, resolvedProxy))
|
||||
|
||||
// 创建上下文用于取消
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -360,8 +366,8 @@ func runTeamRegProcess(config TeamRegConfig) {
|
||||
fmt.Fprintf(stdin, "%d\n", config.Concurrency)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
addTeamRegLog(fmt.Sprintf("[输入] 代理地址: %s", config.Proxy))
|
||||
fmt.Fprintf(stdin, "%s\n", config.Proxy)
|
||||
addTeamRegLog(fmt.Sprintf("[输入] 代理地址: %s", resolvedProxy))
|
||||
fmt.Fprintf(stdin, "%s\n", resolvedProxy)
|
||||
|
||||
// 等待进程完成
|
||||
err = cmd.Wait()
|
||||
|
||||
@@ -379,7 +379,8 @@ func fetchAccountID(token string) (string, error) {
|
||||
proxy = cfg.GetProxy()
|
||||
}
|
||||
|
||||
tlsClient, err := client.New(proxy)
|
||||
resolvedProxy := database.Instance.ResolveProxy(proxy)
|
||||
tlsClient, err := client.New(resolvedProxy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建 TLS 客户端失败: %v", err)
|
||||
}
|
||||
|
||||
@@ -477,11 +477,10 @@ func (c *CodexAPIAuth) ObtainAuthorizationCode() (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
c.logStep(StepInputPassword, "邮箱提交响应 pageType=%s, 包含password=%v", pageType, strings.Contains(string(body), "password"))
|
||||
c.logStep(StepInputPassword, "验证密码...")
|
||||
|
||||
if pageType == "password" || strings.Contains(string(body), "password") {
|
||||
// 5. 验证密码
|
||||
c.logStep(StepInputPassword, "验证密码...")
|
||||
if !c.callSentinelReq("authorize_continue__auto") {
|
||||
return "", fmt.Errorf("Sentinel 请求失败")
|
||||
}
|
||||
@@ -493,7 +492,6 @@ func (c *CodexAPIAuth) ObtainAuthorizationCode() (string, error) {
|
||||
verifyHeaders["OpenAI-Sentinel-Token"] = c.getSentinelHeader("password_verify")
|
||||
|
||||
passwordPayload := map[string]string{
|
||||
"username": c.email,
|
||||
"password": c.password,
|
||||
}
|
||||
|
||||
@@ -502,7 +500,6 @@ func (c *CodexAPIAuth) ObtainAuthorizationCode() (string, error) {
|
||||
c.logError(StepInputPassword, "密码验证失败: %d - %s", resp.StatusCode, string(body[:min(200, len(body))]))
|
||||
return "", fmt.Errorf("密码验证失败: %d", resp.StatusCode)
|
||||
}
|
||||
c.logStep(StepInputPassword, "密码验证成功")
|
||||
|
||||
// 解析密码验证响应
|
||||
json.Unmarshal(body, &data)
|
||||
@@ -520,8 +517,6 @@ func (c *CodexAPIAuth) ObtainAuthorizationCode() (string, error) {
|
||||
c.logError(StepInputPassword, "账号需要邮箱验证,无法继续 Codex 授权流程")
|
||||
return "", fmt.Errorf("账号需要邮箱验证,请使用浏览器模式或等待账号状态更新")
|
||||
}
|
||||
} else {
|
||||
c.logStep(StepInputPassword, "跳过密码验证步骤 (服务器未要求)")
|
||||
}
|
||||
|
||||
// 6. 选择工作区
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
@@ -941,10 +943,44 @@ 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
|
||||
err := d.db.QueryRow("SELECT proxy_url FROM codex_auth_proxies WHERE id = ?", id).Scan(&proxyURL)
|
||||
if err == sql.ErrNoRows {
|
||||
return "", nil
|
||||
}
|
||||
return proxyURL, err
|
||||
}
|
||||
|
||||
// ResolveProxy 解析代理字符串(支持 pool:random, pool:id:N, 或直接 URL)
|
||||
func (d *DB) ResolveProxy(proxyStr string) string {
|
||||
if proxyStr == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 兼容旧的 [RANDOM] 格式
|
||||
if proxyStr == "pool:random" || proxyStr == "[RANDOM]" {
|
||||
p, _ := d.GetRandomCodexProxy()
|
||||
return p
|
||||
}
|
||||
|
||||
// 处理 pool:id:N 格式
|
||||
if strings.HasPrefix(proxyStr, "pool:id:") {
|
||||
idStr := strings.TrimPrefix(proxyStr, "pool:id:")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err == nil {
|
||||
p, _ := d.GetCodexProxyByID(id)
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
// 否则视为字面 URL,如果没协议头加一个(保持现有行为)
|
||||
if !strings.HasPrefix(proxyStr, "http://") && !strings.HasPrefix(proxyStr, "https://") && !strings.HasPrefix(proxyStr, "socks5://") {
|
||||
return "http://" + proxyStr
|
||||
}
|
||||
|
||||
return proxyStr
|
||||
}
|
||||
|
||||
// GetCodexProxyStats 获取代理统计
|
||||
|
||||
Reference in New Issue
Block a user