feat: Implement browser-based OAuth authentication using Chromedp and Rod, add an upload page, and introduce team processing API.
This commit is contained in:
@@ -32,13 +32,14 @@ type TeamProcessRequest struct {
|
||||
// Owner 账号列表
|
||||
Owners []TeamOwner `json:"owners"`
|
||||
// 配置
|
||||
MembersPerTeam int `json:"members_per_team"` // 每个 Team 的成员数
|
||||
ConcurrentTeams int `json:"concurrent_teams"` // 并发 Team 数量
|
||||
BrowserType string `json:"browser_type"` // "chromedp" 或 "rod"
|
||||
Headless bool `json:"headless"` // 是否无头模式
|
||||
Proxy string `json:"proxy"` // 代理设置
|
||||
IncludeOwner bool `json:"include_owner"` // 母号也入库到 S2A
|
||||
ProcessCount int `json:"process_count"` // 处理数量,0表示全部
|
||||
MembersPerTeam int `json:"members_per_team"` // 每个 Team 的成员数
|
||||
ConcurrentTeams int `json:"concurrent_teams"` // 并发 Team 数量
|
||||
ConcurrentS2A int `json:"concurrent_s2a"` // 入库并发数(默认2)
|
||||
BrowserType string `json:"browser_type"` // "chromedp" 或 "rod"
|
||||
Headless bool `json:"headless"` // 是否无头模式
|
||||
Proxy string `json:"proxy"` // 代理设置
|
||||
IncludeOwner bool `json:"include_owner"` // 母号也入库到 S2A
|
||||
ProcessCount int `json:"process_count"` // 处理数量,0表示全部
|
||||
}
|
||||
|
||||
// TeamProcessResult 团队处理结果
|
||||
@@ -125,6 +126,12 @@ func HandleTeamProcess(w http.ResponseWriter, r *http.Request) {
|
||||
if req.BrowserType == "" {
|
||||
req.BrowserType = "chromedp" // 默认使用 Chromedp
|
||||
}
|
||||
if req.ConcurrentS2A <= 0 {
|
||||
req.ConcurrentS2A = 2 // 默认入库并发数为 2
|
||||
}
|
||||
if req.ConcurrentS2A > 4 {
|
||||
req.ConcurrentS2A = 4 // 最大入库并发数为 4(避免浏览器资源耗尽)
|
||||
}
|
||||
if req.Proxy == "" && config.Global != nil {
|
||||
req.Proxy = config.Global.GetProxy() // 使用新的代理获取方法
|
||||
}
|
||||
@@ -549,6 +556,8 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
registerMember := func(memberIdx int, email, password string) bool {
|
||||
name := register.GenerateName()
|
||||
birthdate := register.GenerateBirthdate()
|
||||
memberLogPrefix := fmt.Sprintf("%s [成员 %d]", logPrefix, memberIdx+1)
|
||||
regStartTime := time.Now()
|
||||
|
||||
for attempt := 0; attempt < 2; attempt++ { // 最多尝试2次(首次+1次重试)
|
||||
// 检查是否应该停止
|
||||
@@ -562,13 +571,14 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
// 重试时使用新邮箱
|
||||
currentEmail = mail.GenerateEmail()
|
||||
currentPassword = register.GeneratePassword()
|
||||
logger.Warning(fmt.Sprintf("%s [成员 %d] 重试, 新邮箱: %s", logPrefix, memberIdx+1, currentEmail), currentEmail, "team")
|
||||
logger.Warning(fmt.Sprintf("%s 重试 (第%d次), 新邮箱: %s", memberLogPrefix, attempt+1, currentEmail), currentEmail, "team")
|
||||
}
|
||||
|
||||
// 发送邀请
|
||||
logger.Info(fmt.Sprintf("%s [发送邀请] %s", memberLogPrefix, currentEmail), currentEmail, "team")
|
||||
if err := inviter.SendInvites([]string{currentEmail}); err != nil {
|
||||
errStr := err.Error()
|
||||
logger.Error(fmt.Sprintf("%s [成员 %d] 邀请失败: %v", logPrefix, memberIdx+1, err), currentEmail, "team")
|
||||
logger.Error(fmt.Sprintf("%s [邀请失败] %v", memberLogPrefix, err), currentEmail, "team")
|
||||
|
||||
// 检测 Team 已达邀请上限(401 或 maximum number of seats)
|
||||
if strings.Contains(errStr, "401") || strings.Contains(errStr, "maximum number of seats") {
|
||||
@@ -577,31 +587,37 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
}
|
||||
continue
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s [邀请成功]", memberLogPrefix), currentEmail, "team")
|
||||
|
||||
// 再次检查是否应该停止(邀请期间其他 goroutine 可能已标记)
|
||||
if isTeamExhausted() {
|
||||
return false
|
||||
}
|
||||
|
||||
// 创建注册日志记录器
|
||||
regLogger := NewRegisterLogger(memberLogPrefix, currentEmail)
|
||||
|
||||
// 注册
|
||||
_, err := registerWithTimeout(currentEmail, currentPassword, name, birthdate, req.Proxy)
|
||||
_, err := registerWithTimeoutLogged(currentEmail, currentPassword, name, birthdate, req.Proxy, regLogger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [成员 %d] 注册失败: %v", logPrefix, memberIdx+1, err), currentEmail, "team")
|
||||
logger.Error(fmt.Sprintf("%s [注册失败] %v", memberLogPrefix, err), currentEmail, "team")
|
||||
continue
|
||||
}
|
||||
|
||||
// 成功
|
||||
regDuration := time.Since(regStartTime)
|
||||
memberMu.Lock()
|
||||
children[memberIdx] = MemberAccount{Email: currentEmail, Password: currentPassword, Success: true}
|
||||
memberMu.Unlock()
|
||||
logger.Success(fmt.Sprintf("%s [成员 %d] ✓ 注册成功", logPrefix, memberIdx+1), currentEmail, "team")
|
||||
logger.Success(fmt.Sprintf("%s ✓ 注册成功 (耗时: %.1fs)", memberLogPrefix, regDuration.Seconds()), currentEmail, "team")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 第一轮:并发注册4个成员
|
||||
logger.Info(fmt.Sprintf("%s 开始并发注册 %d 个成员", logPrefix, req.MembersPerTeam), owner.Email, "team")
|
||||
logger.Info(fmt.Sprintf("%s ════════ 开始注册阶段 ════════ 目标: %d 个成员", logPrefix, req.MembersPerTeam), owner.Email, "team")
|
||||
regPhaseStartTime := time.Now()
|
||||
for i := 0; i < req.MembersPerTeam; i++ {
|
||||
memberWg.Add(1)
|
||||
go func(idx int) {
|
||||
@@ -666,7 +682,8 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
if len(failedSlots) > 0 {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("%d 个成员注册失败", len(failedSlots)))
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s 注册完成: %d/%d 成功", logPrefix, result.Registered, req.MembersPerTeam), owner.Email, "team")
|
||||
regPhaseDuration := time.Since(regPhaseStartTime)
|
||||
logger.Info(fmt.Sprintf("%s ════════ 注册阶段完成 ════════ 成功: %d/%d, 耗时: %.1fs", logPrefix, result.Registered, req.MembersPerTeam, regPhaseDuration.Seconds()), owner.Email, "team")
|
||||
|
||||
// 如果没有任何成员注册成功,跳过入库步骤
|
||||
if len(registeredChildren) == 0 {
|
||||
@@ -676,91 +693,182 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
return result
|
||||
}
|
||||
|
||||
// Step 4: S2A 授权入库(成员)- 带重试
|
||||
// Step 4: S2A 授权入库(成员)- 并发入库
|
||||
logger.Info(fmt.Sprintf("%s ════════ 开始入库阶段 ════════ 共 %d 个成员, 并发数: %d", logPrefix, len(registeredChildren), req.ConcurrentS2A), owner.Email, "team")
|
||||
s2aStartTime := time.Now()
|
||||
|
||||
// 入库结果
|
||||
type S2AResult struct {
|
||||
Index int
|
||||
Email string
|
||||
Success bool
|
||||
Error string
|
||||
}
|
||||
|
||||
s2aResults := make(chan S2AResult, len(registeredChildren))
|
||||
s2aSem := make(chan struct{}, req.ConcurrentS2A) // 并发控制信号量
|
||||
|
||||
var s2aWg sync.WaitGroup
|
||||
|
||||
for i, child := range registeredChildren {
|
||||
if !teamProcessState.Running {
|
||||
break
|
||||
}
|
||||
|
||||
var s2aSuccess bool
|
||||
for attempt := 0; attempt < 2; attempt++ { // 最多重试1次
|
||||
if attempt > 0 {
|
||||
logger.Warning(fmt.Sprintf("%s [成员 %d] 入库重试...", logPrefix, i+1), child.Email, "team")
|
||||
s2aWg.Add(1)
|
||||
go func(memberIdx int, memberChild MemberAccount) {
|
||||
defer s2aWg.Done()
|
||||
|
||||
// 获取信号量
|
||||
s2aSem <- struct{}{}
|
||||
defer func() { <-s2aSem }()
|
||||
|
||||
memberStartTime := time.Now()
|
||||
memberLogPrefix := fmt.Sprintf("%s [成员 %d]", logPrefix, memberIdx+1)
|
||||
|
||||
logger.Info(fmt.Sprintf("%s 开始入库 | 邮箱: %s", memberLogPrefix, memberChild.Email), memberChild.Email, "team")
|
||||
|
||||
var s2aSuccess bool
|
||||
var lastError string
|
||||
|
||||
for attempt := 0; attempt < 2; attempt++ { // 最多重试1次
|
||||
if attempt > 0 {
|
||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", memberLogPrefix, attempt+1), memberChild.Email, "team")
|
||||
}
|
||||
|
||||
// 创建日志回调
|
||||
authLogger := auth.NewAuthLogger(memberChild.Email, logPrefix, memberIdx+1, func(entry auth.AuthLogEntry) {
|
||||
stepName := auth.StepName(entry.Step)
|
||||
if entry.IsError {
|
||||
logger.Error(fmt.Sprintf("%s [%s] %s (%.1fs)", memberLogPrefix, stepName, entry.Message, entry.Duration.Seconds()), memberChild.Email, "team")
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("%s [%s] %s", memberLogPrefix, stepName, entry.Message), memberChild.Email, "team")
|
||||
}
|
||||
})
|
||||
|
||||
// 获取授权 URL
|
||||
logger.Info(fmt.Sprintf("%s 获取 S2A 授权 URL...", memberLogPrefix), memberChild.Email, "team")
|
||||
s2aResp, err := auth.GenerateS2AAuthURL(config.Global.S2AApiBase, config.Global.S2AAdminKey, config.Global.ProxyID)
|
||||
if err != nil {
|
||||
lastError = fmt.Sprintf("获取授权URL失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s", memberLogPrefix, lastError), memberChild.Email, "team")
|
||||
continue
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s 授权 URL 获取成功, SessionID: %s", memberLogPrefix, s2aResp.Data.SessionID[:8]+"..."), memberChild.Email, "team")
|
||||
|
||||
// 根据配置选择浏览器自动化
|
||||
var code string
|
||||
if req.BrowserType == "rod" {
|
||||
code, err = auth.CompleteWithRodLogged(s2aResp.Data.AuthURL, memberChild.Email, memberChild.Password, teamID, req.Headless, req.Proxy, authLogger)
|
||||
} else {
|
||||
code, err = auth.CompleteWithChromedpLogged(s2aResp.Data.AuthURL, memberChild.Email, memberChild.Password, teamID, req.Headless, req.Proxy, authLogger)
|
||||
}
|
||||
if err != nil {
|
||||
lastError = fmt.Sprintf("浏览器授权失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s (耗时: %.1fs)", memberLogPrefix, lastError, authLogger.TotalDuration().Seconds()), memberChild.Email, "team")
|
||||
continue
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s 浏览器授权成功, 授权码: %s... (耗时: %.1fs)", memberLogPrefix, code[:8], authLogger.TotalDuration().Seconds()), memberChild.Email, "team")
|
||||
|
||||
// 提交到 S2A
|
||||
logger.Info(fmt.Sprintf("%s 正在提交到 S2A...", memberLogPrefix), memberChild.Email, "team")
|
||||
_, err = auth.SubmitS2AOAuth(
|
||||
config.Global.S2AApiBase,
|
||||
config.Global.S2AAdminKey,
|
||||
s2aResp.Data.SessionID,
|
||||
code,
|
||||
memberChild.Email,
|
||||
config.Global.Concurrency,
|
||||
config.Global.Priority,
|
||||
config.Global.GroupIDs,
|
||||
config.Global.ProxyID,
|
||||
)
|
||||
if err != nil {
|
||||
lastError = fmt.Sprintf("S2A提交失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s", memberLogPrefix, lastError), memberChild.Email, "team")
|
||||
continue
|
||||
}
|
||||
|
||||
s2aSuccess = true
|
||||
memberDuration := time.Since(memberStartTime)
|
||||
logger.Success(fmt.Sprintf("%s ✓ 入库成功 (总耗时: %.1fs)", memberLogPrefix, memberDuration.Seconds()), memberChild.Email, "team")
|
||||
break
|
||||
}
|
||||
|
||||
s2aResp, err := auth.GenerateS2AAuthURL(config.Global.S2AApiBase, config.Global.S2AAdminKey, config.Global.ProxyID)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [成员 %d] 获取授权URL失败: %v", logPrefix, i+1, err), child.Email, "team")
|
||||
continue
|
||||
s2aResults <- S2AResult{
|
||||
Index: memberIdx,
|
||||
Email: memberChild.Email,
|
||||
Success: s2aSuccess,
|
||||
Error: lastError,
|
||||
}
|
||||
}(i, child)
|
||||
}
|
||||
|
||||
// 根据配置选择浏览器自动化
|
||||
var code string
|
||||
if req.BrowserType == "rod" {
|
||||
code, err = auth.CompleteWithRod(s2aResp.Data.AuthURL, child.Email, child.Password, teamID, req.Headless, req.Proxy)
|
||||
} else {
|
||||
code, err = auth.CompleteWithChromedp(s2aResp.Data.AuthURL, child.Email, child.Password, teamID, req.Headless, req.Proxy)
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [成员 %d] 浏览器授权失败: %v", logPrefix, i+1, err), child.Email, "team")
|
||||
continue
|
||||
}
|
||||
// 等待所有入库完成
|
||||
go func() {
|
||||
s2aWg.Wait()
|
||||
close(s2aResults)
|
||||
}()
|
||||
|
||||
// 提交到 S2A
|
||||
_, err = auth.SubmitS2AOAuth(
|
||||
config.Global.S2AApiBase,
|
||||
config.Global.S2AAdminKey,
|
||||
s2aResp.Data.SessionID,
|
||||
code,
|
||||
child.Email,
|
||||
config.Global.Concurrency,
|
||||
config.Global.Priority,
|
||||
config.Global.GroupIDs,
|
||||
config.Global.ProxyID,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [成员 %d] S2A提交失败: %v", logPrefix, i+1, err), child.Email, "team")
|
||||
continue
|
||||
}
|
||||
|
||||
s2aSuccess = true
|
||||
// 收集入库结果
|
||||
for s2aRes := range s2aResults {
|
||||
if s2aRes.Success {
|
||||
result.AddedToS2A++
|
||||
logger.Success(fmt.Sprintf("%s [成员 %d] ✓ 入库成功", logPrefix, i+1), child.Email, "team")
|
||||
break
|
||||
}
|
||||
|
||||
if !s2aSuccess {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("成员 %d 入库失败", i+1))
|
||||
} else {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("成员 %d 入库失败: %s", s2aRes.Index+1, s2aRes.Error))
|
||||
}
|
||||
}
|
||||
|
||||
s2aDuration := time.Since(s2aStartTime)
|
||||
logger.Info(fmt.Sprintf("%s ════════ 入库阶段完成 ════════ 成功: %d/%d, 耗时: %.1fs", logPrefix, result.AddedToS2A, len(registeredChildren), s2aDuration.Seconds()), owner.Email, "team")
|
||||
|
||||
// Step 5: 母号也入库(如果开启)- 带重试
|
||||
if req.IncludeOwner && teamProcessState.Running {
|
||||
logger.Info(fmt.Sprintf("%s 开始将母号入库到 S2A", logPrefix), owner.Email, "team")
|
||||
ownerLogPrefix := fmt.Sprintf("%s [母号 ]", logPrefix)
|
||||
ownerStartTime := time.Now()
|
||||
logger.Info(fmt.Sprintf("%s ════════ 开始母号入库 ════════", logPrefix), owner.Email, "team")
|
||||
logger.Info(fmt.Sprintf("%s 开始入库 | 邮箱: %s", ownerLogPrefix, owner.Email), owner.Email, "team")
|
||||
|
||||
var ownerSuccess bool
|
||||
var lastError string
|
||||
for attempt := 0; attempt < 2; attempt++ { // 最多重试1次
|
||||
if attempt > 0 {
|
||||
logger.Warning(fmt.Sprintf("%s [母号] 入库重试...", logPrefix), owner.Email, "team")
|
||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", ownerLogPrefix, attempt+1), owner.Email, "team")
|
||||
}
|
||||
|
||||
// 创建日志回调
|
||||
authLogger := auth.NewAuthLogger(owner.Email, logPrefix, 0, func(entry auth.AuthLogEntry) {
|
||||
stepName := auth.StepName(entry.Step)
|
||||
if entry.IsError {
|
||||
logger.Error(fmt.Sprintf("%s [%s] %s (%.1fs)", ownerLogPrefix, stepName, entry.Message, entry.Duration.Seconds()), owner.Email, "team")
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("%s [%s] %s", ownerLogPrefix, stepName, entry.Message), owner.Email, "team")
|
||||
}
|
||||
})
|
||||
|
||||
logger.Info(fmt.Sprintf("%s 获取 S2A 授权 URL...", ownerLogPrefix), owner.Email, "team")
|
||||
s2aResp, err := auth.GenerateS2AAuthURL(config.Global.S2AApiBase, config.Global.S2AAdminKey, config.Global.ProxyID)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [母号] 获取授权URL失败: %v", logPrefix, err), owner.Email, "team")
|
||||
lastError = fmt.Sprintf("获取授权URL失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s", ownerLogPrefix, lastError), owner.Email, "team")
|
||||
continue
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s 授权 URL 获取成功, SessionID: %s", ownerLogPrefix, s2aResp.Data.SessionID[:8]+"..."), owner.Email, "team")
|
||||
|
||||
var code string
|
||||
if req.BrowserType == "rod" {
|
||||
code, err = auth.CompleteWithRod(s2aResp.Data.AuthURL, owner.Email, owner.Password, teamID, req.Headless, req.Proxy)
|
||||
code, err = auth.CompleteWithRodLogged(s2aResp.Data.AuthURL, owner.Email, owner.Password, teamID, req.Headless, req.Proxy, authLogger)
|
||||
} else {
|
||||
code, err = auth.CompleteWithChromedp(s2aResp.Data.AuthURL, owner.Email, owner.Password, teamID, req.Headless, req.Proxy)
|
||||
code, err = auth.CompleteWithChromedpLogged(s2aResp.Data.AuthURL, owner.Email, owner.Password, teamID, req.Headless, req.Proxy, authLogger)
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [母号] 浏览器授权失败: %v", logPrefix, err), owner.Email, "team")
|
||||
lastError = fmt.Sprintf("浏览器授权失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s (耗时: %.1fs)", ownerLogPrefix, lastError, authLogger.TotalDuration().Seconds()), owner.Email, "team")
|
||||
continue
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s 浏览器授权成功, 授权码: %s... (耗时: %.1fs)", ownerLogPrefix, code[:8], authLogger.TotalDuration().Seconds()), owner.Email, "team")
|
||||
|
||||
logger.Info(fmt.Sprintf("%s 正在提交到 S2A...", ownerLogPrefix), owner.Email, "team")
|
||||
_, err = auth.SubmitS2AOAuth(
|
||||
config.Global.S2AApiBase,
|
||||
config.Global.S2AAdminKey,
|
||||
@@ -773,19 +881,22 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
config.Global.ProxyID,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [母号] S2A提交失败: %v", logPrefix, err), owner.Email, "team")
|
||||
lastError = fmt.Sprintf("S2A提交失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s", ownerLogPrefix, lastError), owner.Email, "team")
|
||||
continue
|
||||
}
|
||||
|
||||
ownerSuccess = true
|
||||
result.AddedToS2A++
|
||||
logger.Success(fmt.Sprintf("%s [母号 ] ✓ 入库成功", logPrefix), owner.Email, "team")
|
||||
ownerDuration := time.Since(ownerStartTime)
|
||||
logger.Success(fmt.Sprintf("%s ✓ 入库成功 (总耗时: %.1fs)", ownerLogPrefix, ownerDuration.Seconds()), owner.Email, "team")
|
||||
break
|
||||
}
|
||||
|
||||
if !ownerSuccess {
|
||||
result.Errors = append(result.Errors, "母号入库失败")
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("母号入库失败: %s", lastError))
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s ════════ 母号入库完成 ════════", logPrefix), owner.Email, "team")
|
||||
}
|
||||
|
||||
result.DurationMs = time.Since(startTime).Milliseconds()
|
||||
@@ -798,45 +909,97 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
return result
|
||||
}
|
||||
|
||||
// RegisterLogger 注册日志记录器
|
||||
type RegisterLogger struct {
|
||||
logPrefix string
|
||||
email string
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// NewRegisterLogger 创建注册日志记录器
|
||||
func NewRegisterLogger(logPrefix, email string) *RegisterLogger {
|
||||
return &RegisterLogger{
|
||||
logPrefix: logPrefix,
|
||||
email: email,
|
||||
startTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// LogStep 记录步骤
|
||||
func (l *RegisterLogger) LogStep(step string) {
|
||||
logger.Info(fmt.Sprintf("%s [%s]", l.logPrefix, step), l.email, "team")
|
||||
}
|
||||
|
||||
// LogStepDone 记录步骤完成
|
||||
func (l *RegisterLogger) LogStepDone(step string, duration time.Duration) {
|
||||
logger.Info(fmt.Sprintf("%s [%s] 完成 (%.1fs)", l.logPrefix, step, duration.Seconds()), l.email, "team")
|
||||
}
|
||||
|
||||
// registerWithTimeout 带超时的注册
|
||||
func registerWithTimeout(email, password, name, birthdate, proxy string) (*register.ChatGPTReg, error) {
|
||||
return registerWithTimeoutLogged(email, password, name, birthdate, proxy, nil)
|
||||
}
|
||||
|
||||
// registerWithTimeoutLogged 带超时和日志的注册
|
||||
func registerWithTimeoutLogged(email, password, name, birthdate, proxy string, regLogger *RegisterLogger) (*register.ChatGPTReg, error) {
|
||||
logStep := func(step string) {
|
||||
if regLogger != nil {
|
||||
regLogger.LogStep(step)
|
||||
}
|
||||
}
|
||||
|
||||
reg, err := register.New(proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logStep("初始化会话")
|
||||
if err := reg.InitSession(); err != nil {
|
||||
return nil, fmt.Errorf("初始化失败: %v", err)
|
||||
}
|
||||
|
||||
logStep("获取授权URL")
|
||||
if err := reg.GetAuthorizeURL(email); err != nil {
|
||||
return nil, fmt.Errorf("获取授权URL失败: %v", err)
|
||||
}
|
||||
|
||||
logStep("启动授权")
|
||||
if err := reg.StartAuthorize(); err != nil {
|
||||
return nil, fmt.Errorf("启动授权失败: %v", err)
|
||||
}
|
||||
|
||||
logStep("提交注册信息")
|
||||
if err := reg.Register(email, password); err != nil {
|
||||
return nil, fmt.Errorf("注册失败: %v", err)
|
||||
}
|
||||
|
||||
logStep("发送验证邮件")
|
||||
if err := reg.SendVerificationEmail(); err != nil {
|
||||
return nil, fmt.Errorf("发送邮件失败: %v", err)
|
||||
}
|
||||
|
||||
// 短超时获取验证码
|
||||
logStep("等待验证码 (5s)")
|
||||
otpCode, err := mail.GetVerificationCode(email, 5*time.Second)
|
||||
if err != nil {
|
||||
logStep("等待验证码 (15s)")
|
||||
otpCode, err = mail.GetVerificationCode(email, 15*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("验证码获取超时")
|
||||
}
|
||||
}
|
||||
|
||||
logStep("验证OTP")
|
||||
if err := reg.ValidateOTP(otpCode); err != nil {
|
||||
return nil, fmt.Errorf("OTP验证失败: %v", err)
|
||||
}
|
||||
|
||||
logStep("创建账户")
|
||||
if err := reg.CreateAccount(name, birthdate); err != nil {
|
||||
return nil, fmt.Errorf("创建账户失败: %v", err)
|
||||
}
|
||||
|
||||
logStep("获取会话令牌")
|
||||
_ = reg.GetSessionToken()
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user