feat: Implement S2A OAuth authorization using chromedp and rod browser automation, and integrate it into team processing.
This commit is contained in:
@@ -731,14 +731,18 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
|||||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", memberLogPrefix, attempt+1), memberChild.Email, "team")
|
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", memberLogPrefix, attempt+1), memberChild.Email, "team")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建日志回调(只输出关键日志)
|
// 创建日志回调(输出关键日志和调试信息)
|
||||||
authLogger := auth.NewAuthLogger(memberChild.Email, logPrefix, memberIdx+1, func(entry auth.AuthLogEntry) {
|
authLogger := auth.NewAuthLogger(memberChild.Email, logPrefix, memberIdx+1, func(entry auth.AuthLogEntry) {
|
||||||
// 只输出错误和关键步骤
|
|
||||||
if entry.IsError {
|
if entry.IsError {
|
||||||
logger.Error(fmt.Sprintf("%s %s", memberLogPrefix, 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 {
|
} 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), memberChild.Email, "team")
|
logger.Info(fmt.Sprintf("%s %s", memberLogPrefix, entry.Message), memberChild.Email, "team")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取授权 URL
|
// 获取授权 URL
|
||||||
@@ -826,13 +830,17 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
|||||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", ownerLogPrefix, attempt+1), 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) {
|
authLogger := auth.NewAuthLogger(owner.Email, logPrefix, 0, func(entry auth.AuthLogEntry) {
|
||||||
if entry.IsError {
|
if entry.IsError {
|
||||||
logger.Error(fmt.Sprintf("%s %s", ownerLogPrefix, 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 {
|
} else {
|
||||||
|
switch entry.Step {
|
||||||
|
case auth.StepNavigate, auth.StepInputEmail, auth.StepInputPassword,
|
||||||
|
auth.StepComplete, auth.StepConsent, auth.StepSelectWorkspace:
|
||||||
logger.Info(fmt.Sprintf("%s %s", ownerLogPrefix, entry.Message), owner.Email, "team")
|
logger.Info(fmt.Sprintf("%s %s", ownerLogPrefix, entry.Message), owner.Email, "team")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
s2aResp, err := auth.GenerateS2AAuthURL(config.Global.S2AApiBase, config.Global.S2AAdminKey, config.Global.ProxyID)
|
s2aResp, err := auth.GenerateS2AAuthURL(config.Global.S2AApiBase, config.Global.S2AAdminKey, config.Global.ProxyID)
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
|
|
||||||
var currentURL string
|
var currentURL string
|
||||||
_ = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
_ = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
||||||
|
logStep(StepNavigate, "页面加载完成 | URL: %s", currentURL)
|
||||||
|
|
||||||
if strings.Contains(currentURL, "code=") {
|
if strings.Contains(currentURL, "code=") {
|
||||||
logStep(StepComplete, "授权成功(快速通道)")
|
logStep(StepComplete, "授权成功(快速通道)")
|
||||||
@@ -194,6 +195,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
)
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
emailFilled = true
|
emailFilled = true
|
||||||
|
logStep(StepInputEmail, "邮箱已填写 | 选择器: %s", sel)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +203,8 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
findCancel()
|
findCancel()
|
||||||
|
|
||||||
if !emailFilled {
|
if !emailFilled {
|
||||||
logError(StepInputEmail, "未找到邮箱输入框")
|
_ = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
||||||
|
logError(StepInputEmail, "未找到邮箱输入框 | URL: %s", currentURL)
|
||||||
return "", fmt.Errorf("未找到邮箱输入框")
|
return "", fmt.Errorf("未找到邮箱输入框")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +212,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
|
|
||||||
buttonSelectors := []string{
|
buttonSelectors := []string{
|
||||||
`button[type="submit"]`,
|
`button[type="submit"]`,
|
||||||
|
`div._ctas_1alro_13 button`,
|
||||||
`button[data-testid="login-button"]`,
|
`button[data-testid="login-button"]`,
|
||||||
`button.continue-btn`,
|
`button.continue-btn`,
|
||||||
`input[type="submit"]`,
|
`input[type="submit"]`,
|
||||||
@@ -235,13 +239,15 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
return ExtractCodeFromCallbackURL(currentURL), nil
|
return ExtractCodeFromCallbackURL(currentURL), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logStep(StepInputPassword, "查找密码框 | URL: %s", currentURL)
|
||||||
|
|
||||||
// 密码输入框选择器
|
// 密码输入框选择器
|
||||||
passwordSelectors := []string{
|
passwordSelectors := []string{
|
||||||
|
`input[name="current-password"]`,
|
||||||
|
`input[autocomplete="current-password"]`,
|
||||||
`input[type="password"]`,
|
`input[type="password"]`,
|
||||||
`input[name="password"]`,
|
`input[name="password"]`,
|
||||||
`input[name="current-password"]`,
|
|
||||||
`input[id="password"]`,
|
`input[id="password"]`,
|
||||||
`input[autocomplete="current-password"]`,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用短超时查找密码框(10秒)
|
// 使用短超时查找密码框(10秒)
|
||||||
@@ -257,6 +263,7 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
)
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
passwordFilled = true
|
passwordFilled = true
|
||||||
|
logStep(StepInputPassword, "密码已填写 | 选择器: %s", sel)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +271,8 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
findCancel2()
|
findCancel2()
|
||||||
|
|
||||||
if !passwordFilled {
|
if !passwordFilled {
|
||||||
logError(StepInputPassword, "未找到密码输入框")
|
_ = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
||||||
|
logError(StepInputPassword, "未找到密码输入框 | URL: %s", currentURL)
|
||||||
return "", fmt.Errorf("未找到密码输入框")
|
return "", fmt.Errorf("未找到密码输入框")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,8 +303,14 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(url, "consent") {
|
if strings.Contains(url, "consent") {
|
||||||
logStep(StepConsent, "处理授权同意...")
|
logStep(StepConsent, "处理授权同意... | URL: %s", url)
|
||||||
for _, sel := range buttonSelectors {
|
// 同意页面的确认按钮(第二个按钮)
|
||||||
|
consentSelectors := []string{
|
||||||
|
`div._ctas_1alro_13 div:nth-child(2) button`,
|
||||||
|
`button[type="submit"]`,
|
||||||
|
`div._ctas_1alro_13 button`,
|
||||||
|
}
|
||||||
|
for _, sel := range consentSelectors {
|
||||||
err = chromedp.Run(ctx, chromedp.Click(sel, chromedp.ByQuery))
|
err = chromedp.Run(ctx, chromedp.Click(sel, chromedp.ByQuery))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -218,6 +218,11 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
|||||||
|
|
||||||
page.MustWaitDOMStable()
|
page.MustWaitDOMStable()
|
||||||
|
|
||||||
|
// 获取当前URL
|
||||||
|
info, _ := page.Info()
|
||||||
|
currentURL := info.URL
|
||||||
|
logStep(StepNavigate, "页面加载完成 | URL: %s", currentURL)
|
||||||
|
|
||||||
if code := r.checkForCode(page); code != "" {
|
if code := r.checkForCode(page); code != "" {
|
||||||
logStep(StepComplete, "授权成功(快速通道)")
|
logStep(StepComplete, "授权成功(快速通道)")
|
||||||
return code, nil
|
return code, nil
|
||||||
@@ -226,14 +231,17 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
|||||||
// 使用10秒超时查找邮箱输入框
|
// 使用10秒超时查找邮箱输入框
|
||||||
emailInput, err := page.Timeout(10 * time.Second).Element("input[name='email'], input[type='email'], input[name='username'], input[id='email'], input[autocomplete='email']")
|
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 {
|
if err != nil {
|
||||||
logError(StepInputEmail, "未找到邮箱输入框")
|
info, _ := page.Info()
|
||||||
|
logError(StepInputEmail, "未找到邮箱输入框 | URL: %s", info.URL)
|
||||||
return "", fmt.Errorf("未找到邮箱输入框")
|
return "", fmt.Errorf("未找到邮箱输入框")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logStep(StepInputEmail, "邮箱已填写")
|
||||||
emailInput.MustSelectAllText().MustInput(email)
|
emailInput.MustSelectAllText().MustInput(email)
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit'], button[name='action']"); btn != nil {
|
// 点击提交按钮
|
||||||
|
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit'], div._ctas_1alro_13 button, button[name='action']"); btn != nil {
|
||||||
btn.MustClick()
|
btn.MustClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,18 +252,24 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
|||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用10秒超时查找密码输入框
|
// 获取当前URL用于调试
|
||||||
passwordInput, err := page.Timeout(10 * time.Second).Element("input[type='password'], input[name='password'], input[id='password']")
|
info, _ = page.Info()
|
||||||
|
logStep(StepInputPassword, "查找密码框 | URL: %s", info.URL)
|
||||||
|
|
||||||
|
// 使用10秒超时查找密码输入框(优先使用 current-password)
|
||||||
|
passwordInput, err := page.Timeout(10 * time.Second).Element("input[name='current-password'], input[autocomplete='current-password'], input[type='password'], input[name='password'], input[id='password']")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logError(StepInputPassword, "未找到密码输入框")
|
info, _ := page.Info()
|
||||||
|
logError(StepInputPassword, "未找到密码输入框 | URL: %s", info.URL)
|
||||||
return "", fmt.Errorf("未找到密码输入框")
|
return "", fmt.Errorf("未找到密码输入框")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logStep(StepInputPassword, "密码已填写")
|
||||||
passwordInput.MustSelectAllText().MustInput(password)
|
passwordInput.MustSelectAllText().MustInput(password)
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
logStep(StepSubmitPassword, "正在登录...")
|
logStep(StepSubmitPassword, "正在登录...")
|
||||||
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit'], button[name='action']"); btn != nil {
|
if btn, _ := page.Timeout(2 * time.Second).Element("button[type='submit'], div._ctas_1alro_13 button, button[name='action']"); btn != nil {
|
||||||
btn.MustClick()
|
btn.MustClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,8 +286,9 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
|||||||
currentURL := info.URL
|
currentURL := info.URL
|
||||||
|
|
||||||
if strings.Contains(currentURL, "consent") {
|
if strings.Contains(currentURL, "consent") {
|
||||||
logStep(StepConsent, "处理授权同意...")
|
logStep(StepConsent, "处理授权同意... | URL: %s", currentURL)
|
||||||
if btn, _ := page.Timeout(500 * time.Millisecond).Element("button[type='submit']"); btn != nil {
|
// 同意页面的确认按钮(第二个按钮)
|
||||||
|
if btn, _ := page.Timeout(500 * time.Millisecond).Element("div._ctas_1alro_13 div:nth-child(2) button, button[type='submit'], div._ctas_1alro_13 button"); btn != nil {
|
||||||
btn.Click(proto.InputMouseButtonLeft, 1)
|
btn.Click(proto.InputMouseButtonLeft, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user