feat: Implement browser-based OAuth authentication using chromedp and rod, and add a team processing API.
This commit is contained in:
@@ -575,10 +575,9 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
}
|
||||
|
||||
// 发送邀请
|
||||
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 [邀请失败] %v", memberLogPrefix, 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") {
|
||||
@@ -587,18 +586,14 @@ 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 := registerWithTimeoutLogged(currentEmail, currentPassword, name, birthdate, req.Proxy, regLogger)
|
||||
_, err := registerWithTimeout(currentEmail, currentPassword, name, birthdate, req.Proxy)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s [注册失败] %v", memberLogPrefix, err), currentEmail, "team")
|
||||
continue
|
||||
@@ -736,25 +731,23 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
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")
|
||||
logger.Error(fmt.Sprintf("%s %s", memberLogPrefix, entry.Message), memberChild.Email, "team")
|
||||
} else if entry.Step == auth.StepComplete || entry.Step == auth.StepConsent || entry.Step == auth.StepSelectWorkspace {
|
||||
logger.Info(fmt.Sprintf("%s %s", memberLogPrefix, 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
|
||||
@@ -765,13 +758,11 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
}
|
||||
if err != nil {
|
||||
lastError = fmt.Sprintf("浏览器授权失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s (耗时: %.1fs)", memberLogPrefix, lastError, authLogger.TotalDuration().Seconds()), memberChild.Email, "team")
|
||||
logger.Error(fmt.Sprintf("%s %s", memberLogPrefix, lastError), 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,
|
||||
@@ -826,8 +817,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
if req.IncludeOwner && teamProcessState.Running {
|
||||
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")
|
||||
logger.Info(fmt.Sprintf("%s 开始母号入库...", ownerLogPrefix), owner.Email, "team")
|
||||
|
||||
var ownerSuccess bool
|
||||
var lastError string
|
||||
@@ -836,24 +826,21 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
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.Error(fmt.Sprintf("%s %s", ownerLogPrefix, entry.Message), owner.Email, "team")
|
||||
} else if entry.Step == auth.StepComplete || entry.Step == auth.StepConsent || entry.Step == auth.StepSelectWorkspace {
|
||||
logger.Info(fmt.Sprintf("%s %s", ownerLogPrefix, 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 {
|
||||
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" {
|
||||
@@ -863,12 +850,11 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
}
|
||||
if err != nil {
|
||||
lastError = fmt.Sprintf("浏览器授权失败: %v", err)
|
||||
logger.Error(fmt.Sprintf("%s %s (耗时: %.1fs)", ownerLogPrefix, lastError, authLogger.TotalDuration().Seconds()), owner.Email, "team")
|
||||
logger.Error(fmt.Sprintf("%s %s", ownerLogPrefix, lastError), 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")
|
||||
// 提交到 S2A
|
||||
_, err = auth.SubmitS2AOAuth(
|
||||
config.Global.S2AApiBase,
|
||||
config.Global.S2AAdminKey,
|
||||
@@ -896,7 +882,6 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
if !ownerSuccess {
|
||||
result.Errors = append(result.Errors, fmt.Sprintf("母号入库失败: %s", lastError))
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s ════════ 母号入库完成 ════════", logPrefix), owner.Email, "team")
|
||||
}
|
||||
|
||||
result.DurationMs = time.Since(startTime).Milliseconds()
|
||||
@@ -909,97 +894,50 @@ 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
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
}
|
||||
}
|
||||
|
||||
logStep(StepBrowserStart, "正在启动 Chromedp 浏览器...")
|
||||
// 获取随机浏览器配置
|
||||
profile := GetRandomBrowserProfile()
|
||||
|
||||
@@ -80,8 +79,8 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
ctx, cancel := chromedp.NewContext(allocCtx)
|
||||
defer cancel()
|
||||
|
||||
// 增加超时时间到 180 秒
|
||||
ctx, cancel = context.WithTimeout(ctx, 180*time.Second)
|
||||
// 设置合理的超时时间 30 秒
|
||||
ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var callbackURL string
|
||||
@@ -150,17 +149,16 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
tasks = append([]chromedp.Action{fetch.Enable().WithHandleAuthRequests(true)}, tasks...)
|
||||
}
|
||||
|
||||
logStep(StepNavigate, "正在访问授权页面...")
|
||||
err := chromedp.Run(ctx, tasks...)
|
||||
if err != nil {
|
||||
logError(StepNavigate, "访问失败: %v", err)
|
||||
logError(StepNavigate, "访问授权页失败: %v", err)
|
||||
return "", fmt.Errorf("访问失败: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if callbackURL != "" {
|
||||
logStep(StepExtractCode, "已捕获授权码回调")
|
||||
logStep(StepComplete, "授权成功(快速通道)")
|
||||
return ExtractCodeFromCallbackURL(callbackURL), nil
|
||||
}
|
||||
|
||||
@@ -168,21 +166,27 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
_ = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
||||
|
||||
if strings.Contains(currentURL, "code=") {
|
||||
logStep(StepComplete, "授权成功(快速通道)")
|
||||
return ExtractCodeFromCallbackURL(currentURL), nil
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 邮箱输入框选择器
|
||||
emailSelectors := []string{
|
||||
`input[name="email"]`,
|
||||
`input[type="email"]`,
|
||||
`input[name="username"]`,
|
||||
`input[id="email"]`,
|
||||
`input[autocomplete="email"]`,
|
||||
}
|
||||
|
||||
logStep(StepInputEmail, "正在查找邮箱输入框...")
|
||||
// 创建带短超时的上下文用于查找元素(10秒)
|
||||
findCtx, findCancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
|
||||
var emailFilled bool
|
||||
for _, sel := range emailSelectors {
|
||||
err = chromedp.Run(ctx, chromedp.WaitVisible(sel, chromedp.ByQuery))
|
||||
err = chromedp.Run(findCtx, chromedp.WaitVisible(sel, chromedp.ByQuery))
|
||||
if err == nil {
|
||||
err = chromedp.Run(ctx,
|
||||
chromedp.Clear(sel, chromedp.ByQuery),
|
||||
@@ -190,11 +194,11 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
)
|
||||
if err == nil {
|
||||
emailFilled = true
|
||||
logStep(StepInputEmail, "已输入邮箱")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
findCancel()
|
||||
|
||||
if !emailFilled {
|
||||
logError(StepInputEmail, "未找到邮箱输入框")
|
||||
@@ -208,9 +212,9 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
`button[data-testid="login-button"]`,
|
||||
`button.continue-btn`,
|
||||
`input[type="submit"]`,
|
||||
`button[name="action"]`,
|
||||
}
|
||||
|
||||
logStep(StepSubmitEmail, "正在提交邮箱...")
|
||||
for _, sel := range buttonSelectors {
|
||||
err = chromedp.Run(ctx, chromedp.Click(sel, chromedp.ByQuery))
|
||||
if err == nil {
|
||||
@@ -221,26 +225,31 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
if callbackURL != "" {
|
||||
logStep(StepExtractCode, "已捕获授权码回调")
|
||||
logStep(StepComplete, "授权成功")
|
||||
return ExtractCodeFromCallbackURL(callbackURL), nil
|
||||
}
|
||||
|
||||
_ = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
||||
if strings.Contains(currentURL, "code=") {
|
||||
logStep(StepExtractCode, "已获取授权码")
|
||||
logStep(StepComplete, "授权成功")
|
||||
return ExtractCodeFromCallbackURL(currentURL), nil
|
||||
}
|
||||
|
||||
logStep(StepInputPassword, "正在查找密码输入框...")
|
||||
// 密码输入框选择器
|
||||
passwordSelectors := []string{
|
||||
`input[name="current-password"]`,
|
||||
`input[name="password"]`,
|
||||
`input[type="password"]`,
|
||||
`input[name="password"]`,
|
||||
`input[name="current-password"]`,
|
||||
`input[id="password"]`,
|
||||
`input[autocomplete="current-password"]`,
|
||||
}
|
||||
|
||||
// 使用短超时查找密码框(10秒)
|
||||
findCtx2, findCancel2 := context.WithTimeout(ctx, 10*time.Second)
|
||||
|
||||
var passwordFilled bool
|
||||
for _, sel := range passwordSelectors {
|
||||
err = chromedp.Run(ctx, chromedp.WaitVisible(sel, chromedp.ByQuery))
|
||||
err = chromedp.Run(findCtx2, chromedp.WaitVisible(sel, chromedp.ByQuery))
|
||||
if err == nil {
|
||||
err = chromedp.Run(ctx,
|
||||
chromedp.Clear(sel, chromedp.ByQuery),
|
||||
@@ -248,11 +257,11 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
)
|
||||
if err == nil {
|
||||
passwordFilled = true
|
||||
logStep(StepInputPassword, "已输入密码")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
findCancel2()
|
||||
|
||||
if !passwordFilled {
|
||||
logError(StepInputPassword, "未找到密码输入框")
|
||||
@@ -261,7 +270,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
logStep(StepSubmitPassword, "正在提交密码...")
|
||||
logStep(StepSubmitPassword, "正在登录...")
|
||||
for _, sel := range buttonSelectors {
|
||||
err = chromedp.Run(ctx, chromedp.Click(sel, chromedp.ByQuery))
|
||||
if err == nil {
|
||||
@@ -269,23 +278,24 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
}
|
||||
}
|
||||
|
||||
logStep(StepWaitCallback, "等待授权回调...")
|
||||
// 等待授权回调(最多15秒)
|
||||
for i := 0; i < 30; i++ {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
if callbackURL != "" {
|
||||
logStep(StepComplete, "授权成功")
|
||||
return ExtractCodeFromCallbackURL(callbackURL), nil
|
||||
}
|
||||
|
||||
var url string
|
||||
if err := chromedp.Run(ctx, chromedp.Location(&url)); err == nil {
|
||||
if strings.Contains(url, "code=") {
|
||||
logStep(StepExtractCode, "已获取授权码")
|
||||
logStep(StepComplete, "授权成功")
|
||||
return ExtractCodeFromCallbackURL(url), nil
|
||||
}
|
||||
|
||||
if strings.Contains(url, "consent") {
|
||||
logStep(StepConsent, "正在处理授权同意页面...")
|
||||
logStep(StepConsent, "处理授权同意...")
|
||||
for _, sel := range buttonSelectors {
|
||||
err = chromedp.Run(ctx, chromedp.Click(sel, chromedp.ByQuery))
|
||||
if err == nil {
|
||||
@@ -296,7 +306,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
}
|
||||
|
||||
if strings.Contains(url, "authorize") && teamID != "" {
|
||||
logStep(StepSelectWorkspace, "正在选择工作区: %s", teamID)
|
||||
logStep(StepSelectWorkspace, "选择工作区...")
|
||||
err = chromedp.Run(ctx,
|
||||
chromedp.Click(fmt.Sprintf(`[data-workspace-id="%s"], [data-account-id="%s"]`, teamID, teamID), chromedp.ByQuery),
|
||||
)
|
||||
@@ -305,7 +315,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
||||
}
|
||||
|
||||
if callbackURL != "" {
|
||||
logStep(StepComplete, "授权完成")
|
||||
logStep(StepComplete, "授权成功")
|
||||
return ExtractCodeFromCallbackURL(callbackURL), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -208,67 +208,63 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
||||
ThisObj: nil,
|
||||
})
|
||||
|
||||
// 增加超时时间到 90 秒
|
||||
page = page.Timeout(90 * time.Second)
|
||||
// 设置合理的超时时间 60 秒
|
||||
page = page.Timeout(60 * time.Second)
|
||||
|
||||
logStep(StepNavigate, "正在访问授权页面...")
|
||||
if err := page.Navigate(authURL); err != nil {
|
||||
logError(StepNavigate, "访问失败: %v", err)
|
||||
logError(StepNavigate, "访问授权页失败: %v", err)
|
||||
return "", fmt.Errorf("访问授权URL失败: %v", err)
|
||||
}
|
||||
|
||||
page.MustWaitDOMStable()
|
||||
|
||||
if code := r.checkForCode(page); code != "" {
|
||||
logStep(StepExtractCode, "已捕获授权码回调")
|
||||
logStep(StepComplete, "授权成功(快速通道)")
|
||||
return code, nil
|
||||
}
|
||||
|
||||
logStep(StepInputEmail, "正在查找邮箱输入框...")
|
||||
emailInput, err := page.Timeout(5 * time.Second).Element("input[name='email'], input[type='email'], input[name='username']")
|
||||
// 使用10秒超时查找邮箱输入框
|
||||
emailInput, err := page.Timeout(10 * time.Second).Element("input[name='email'], input[type='email'], input[name='username'], input[id='email'], input[autocomplete='email']")
|
||||
if err != nil {
|
||||
logError(StepInputEmail, "未找到邮箱输入框")
|
||||
return "", fmt.Errorf("未找到邮箱输入框")
|
||||
}
|
||||
|
||||
emailInput.MustSelectAllText().MustInput(email)
|
||||
logStep(StepInputEmail, "已输入邮箱")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
logStep(StepSubmitEmail, "正在提交邮箱...")
|
||||
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit']"); btn != nil {
|
||||
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit'], button[name='action']"); btn != nil {
|
||||
btn.MustClick()
|
||||
}
|
||||
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
if code := r.checkForCode(page); code != "" {
|
||||
logStep(StepExtractCode, "已获取授权码")
|
||||
logStep(StepComplete, "授权成功")
|
||||
return code, nil
|
||||
}
|
||||
|
||||
logStep(StepInputPassword, "正在查找密码输入框...")
|
||||
passwordInput, err := page.Timeout(8 * time.Second).Element("input[type='password']")
|
||||
// 使用10秒超时查找密码输入框
|
||||
passwordInput, err := page.Timeout(10 * time.Second).Element("input[type='password'], input[name='password'], input[id='password']")
|
||||
if err != nil {
|
||||
logError(StepInputPassword, "未找到密码输入框")
|
||||
return "", fmt.Errorf("未找到密码输入框")
|
||||
}
|
||||
|
||||
passwordInput.MustSelectAllText().MustInput(password)
|
||||
logStep(StepInputPassword, "已输入密码")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
logStep(StepSubmitPassword, "正在提交密码...")
|
||||
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit']"); btn != nil {
|
||||
logStep(StepSubmitPassword, "正在登录...")
|
||||
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit'], button[name='action']"); btn != nil {
|
||||
btn.MustClick()
|
||||
}
|
||||
|
||||
logStep(StepWaitCallback, "等待授权回调...")
|
||||
for i := 0; i < 66; i++ {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
// 等待授权回调(最多20秒)
|
||||
for i := 0; i < 40; i++ {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
if code := r.checkForCode(page); code != "" {
|
||||
logStep(StepComplete, "授权完成")
|
||||
logStep(StepComplete, "授权成功")
|
||||
return code, nil
|
||||
}
|
||||
|
||||
@@ -276,14 +272,14 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
||||
currentURL := info.URL
|
||||
|
||||
if strings.Contains(currentURL, "consent") {
|
||||
logStep(StepConsent, "正在处理授权同意页面...")
|
||||
logStep(StepConsent, "处理授权同意...")
|
||||
if btn, _ := page.Timeout(500 * time.Millisecond).Element("button[type='submit']"); btn != nil {
|
||||
btn.Click(proto.InputMouseButtonLeft, 1)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(currentURL, "authorize") && teamID != "" {
|
||||
logStep(StepSelectWorkspace, "正在选择工作区: %s", teamID)
|
||||
logStep(StepSelectWorkspace, "选择工作区...")
|
||||
wsSelector := fmt.Sprintf("[data-workspace-id='%s'], [data-account-id='%s']", teamID, teamID)
|
||||
if wsBtn, _ := page.Timeout(500 * time.Millisecond).Element(wsSelector); wsBtn != nil {
|
||||
wsBtn.Click(proto.InputMouseButtonLeft, 1)
|
||||
|
||||
Reference in New Issue
Block a user