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:
2026-02-01 04:58:12 +08:00
parent 842a4ab4b2
commit e867bc5cbd
10 changed files with 314 additions and 22 deletions

View File

@@ -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)