feat: Implement S2A, Rod, and Chromedp based authentication for external services, and introduce new frontend pages and backend APIs for monitoring, configuration, upload, and team processes.
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"codex-pool/internal/proxyutil"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/launcher"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
@@ -17,6 +19,8 @@ type RodAuth struct {
|
||||
browser *rod.Browser
|
||||
headless bool
|
||||
proxy string
|
||||
proxyUser string
|
||||
proxyPass string
|
||||
}
|
||||
|
||||
// getChromiumPath 获取 Chromium 路径
|
||||
@@ -49,6 +53,24 @@ func getChromiumPath() string {
|
||||
|
||||
// NewRodAuth 创建 Rod 授权器
|
||||
func NewRodAuth(headless bool, proxy string) (*RodAuth, error) {
|
||||
var proxyServer string
|
||||
var proxyUser string
|
||||
var proxyPass string
|
||||
if proxy != "" {
|
||||
info, err := proxyutil.Parse(proxy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("代理格式错误: %v", err)
|
||||
}
|
||||
if info.URL != nil {
|
||||
proxy = info.URL.String() // normalized
|
||||
}
|
||||
if info.Server != nil {
|
||||
proxyServer = info.Server.String()
|
||||
}
|
||||
proxyUser = info.Username
|
||||
proxyPass = info.Password
|
||||
}
|
||||
|
||||
l := launcher.New().
|
||||
Headless(headless).
|
||||
Set("disable-blink-features", "AutomationControlled").
|
||||
@@ -67,8 +89,8 @@ func NewRodAuth(headless bool, proxy string) (*RodAuth, error) {
|
||||
l = l.Bin(chromiumPath)
|
||||
}
|
||||
|
||||
if proxy != "" {
|
||||
l = l.Proxy(proxy)
|
||||
if proxyServer != "" {
|
||||
l = l.Proxy(proxyServer)
|
||||
}
|
||||
|
||||
controlURL, err := l.Launch()
|
||||
@@ -85,6 +107,8 @@ func NewRodAuth(headless bool, proxy string) (*RodAuth, error) {
|
||||
browser: browser,
|
||||
headless: headless,
|
||||
proxy: proxy,
|
||||
proxyUser: proxyUser,
|
||||
proxyPass: proxyPass,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -97,6 +121,39 @@ func (r *RodAuth) Close() {
|
||||
|
||||
// CompleteOAuth 完成 OAuth 授权
|
||||
func (r *RodAuth) CompleteOAuth(authURL, email, password, teamID string) (string, error) {
|
||||
// Handle proxy auth (407) in headless mode.
|
||||
// When Fetch domain is enabled without patterns, requests will be paused and must be continued.
|
||||
if r.proxy != "" {
|
||||
authBrowser, cancel := r.browser.WithCancel()
|
||||
defer cancel()
|
||||
|
||||
restoreFetch := authBrowser.EnableDomain("", &proto.FetchEnable{HandleAuthRequests: true})
|
||||
defer restoreFetch()
|
||||
|
||||
wait := authBrowser.EachEvent(
|
||||
func(e *proto.FetchRequestPaused) {
|
||||
_ = proto.FetchContinueRequest{RequestID: e.RequestID}.Call(authBrowser)
|
||||
},
|
||||
func(e *proto.FetchAuthRequired) {
|
||||
resp := &proto.FetchAuthChallengeResponse{
|
||||
Response: proto.FetchAuthChallengeResponseResponseDefault,
|
||||
}
|
||||
if e.AuthChallenge != nil && e.AuthChallenge.Source == proto.FetchAuthChallengeSourceProxy {
|
||||
if r.proxyUser != "" {
|
||||
resp.Response = proto.FetchAuthChallengeResponseResponseProvideCredentials
|
||||
resp.Username = r.proxyUser
|
||||
resp.Password = r.proxyPass
|
||||
} else {
|
||||
// Fail fast if the proxy requires auth but user didn't provide credentials.
|
||||
resp.Response = proto.FetchAuthChallengeResponseResponseCancelAuth
|
||||
}
|
||||
}
|
||||
_ = proto.FetchContinueWithAuth{RequestID: e.RequestID, AuthChallengeResponse: resp}.Call(authBrowser)
|
||||
},
|
||||
)
|
||||
go wait()
|
||||
}
|
||||
|
||||
page, err := stealth.Page(r.browser)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建页面失败: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user