From 8c6e578a84f7936ced07f7030e08cb23633beca1 Mon Sep 17 00:00:00 2001 From: win Date: Thu, 26 Mar 2026 14:00:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20IP=E7=AE=A1=E7=90=86=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E4=B8=8E=20node-tls-proxy=20=E6=8C=87=E7=BA=B9=E4=BC=AA?= =?UTF-8?q?=E8=A3=85=E5=85=B1=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Do()/DoWithTLS() 移除 proxyURL=="" 条件,绑了代理也走 node-tls-proxy - doViaNodeTLSProxy 通过 X-Upstream-Proxy header 传递账号代理给 node-tls-proxy - node-tls-proxy 支持 per-request 动态上游代理,优先 X-Upstream-Proxy,回退全局 UPSTREAM_PROXY - 效果:IP管理 = 落地机网络,账号绑代理后指纹伪装仍然生效 --- antigravity/node-tls-proxy/proxy.js | 11 ++++++++--- backend/internal/repository/http_upstream.go | 11 +++++++---- .../internal/repository/http_upstream_antigravity.go | 6 ++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/antigravity/node-tls-proxy/proxy.js b/antigravity/node-tls-proxy/proxy.js index 3fad7d4c..1233ea88 100644 --- a/antigravity/node-tls-proxy/proxy.js +++ b/antigravity/node-tls-proxy/proxy.js @@ -573,10 +573,15 @@ function sendViaH1(targetHost, method, path, reqHeaders, body, res, savedHeaders proxyReq.end(body); }; - if (UPSTREAM_PROXY) { - connectViaProxy(UPSTREAM_PROXY, targetHost, 443) + // 动态上游代理:优先使用 per-request 的 X-Upstream-Proxy,回退到全局 UPSTREAM_PROXY + const upstreamProxy = reqHeaders['x-upstream-proxy'] || UPSTREAM_PROXY; + // 清除内部 header,不传给上游 + delete headers['x-upstream-proxy']; + + if (upstreamProxy) { + connectViaProxy(upstreamProxy, targetHost, 443) .then((socket) => { opts.socket = socket; opts.agent = false; finish(opts); }) - .catch((err) => { log('error', 'tunnel_failed', { error: err.message }); if (!res.headersSent) { res.writeHead(502); res.end('tunnel error'); } resolve('error'); }); + .catch((err) => { log('error', 'tunnel_failed', { error: err.message, proxy: upstreamProxy }); if (!res.headersSent) { res.writeHead(502); res.end('tunnel error'); } resolve('error'); }); } else { finish(opts); } diff --git a/backend/internal/repository/http_upstream.go b/backend/internal/repository/http_upstream.go index a0ad1d97..7c79f128 100644 --- a/backend/internal/repository/http_upstream.go +++ b/backend/internal/repository/http_upstream.go @@ -124,8 +124,10 @@ func NewHTTPUpstream(cfg *config.Config) service.HTTPUpstream { // - 调用方必须关闭 resp.Body,否则会导致 inFlight 计数泄漏 // - inFlight > 0 的客户端不会被淘汰,确保活跃请求不被中断 func (s *httpUpstreamService) Do(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) { - // Node.js TLS 代理:Anthropic + Google APIs(无 per-account 代理时) - if s.isNodeTLSProxyEnabled() && proxyURL == "" && req != nil && req.URL != nil && req.URL.Scheme == "https" { + // Node.js TLS 代理:Anthropic + Google APIs + // 无论是否绑定 per-account 代理,都走 node-tls-proxy(指纹伪装) + // proxyURL 通过 X-Upstream-Proxy header 传递给 node-tls-proxy 动态选择出口 + if s.isNodeTLSProxyEnabled() && req != nil && req.URL != nil && req.URL.Scheme == "https" { host := req.URL.Hostname() if host == "api.anthropic.com" || strings.HasSuffix(host, ".googleapis.com") { @@ -184,8 +186,9 @@ func (s *httpUpstreamService) DoWithTLS(req *http.Request, proxyURL string, acco return s.Do(req, proxyURL, accountID, accountConcurrency) } - // 优先使用 Node.js TLS 代理模式(Anthropic + Google APIs,无 per-account 代理) - if s.isNodeTLSProxyEnabled() && proxyURL == "" && req != nil && req.URL != nil { + // 优先使用 Node.js TLS 代理模式(Anthropic + Google APIs) + // 无论是否绑定 per-account 代理,都走 node-tls-proxy(指纹伪装) + if s.isNodeTLSProxyEnabled() && req != nil && req.URL != nil { host := req.URL.Hostname() if host == "api.anthropic.com" || strings.HasSuffix(host, ".googleapis.com") { return s.doViaNodeTLSProxy(req, proxyURL, accountID, accountConcurrency) diff --git a/backend/internal/repository/http_upstream_antigravity.go b/backend/internal/repository/http_upstream_antigravity.go index b12dead9..c2f77fb6 100644 --- a/backend/internal/repository/http_upstream_antigravity.go +++ b/backend/internal/repository/http_upstream_antigravity.go @@ -79,6 +79,12 @@ func (s *httpUpstreamService) doViaNodeTLSProxy(req *http.Request, proxyURL stri originalHost := req.URL.Host proxyReq.Header.Set("X-Forwarded-Host", originalHost) + // 如果账号绑定了代理(落地机 GOST),通过 header 传递给 node-tls-proxy + // node-tls-proxy 会用此代理作为上游出口,实现动态路由 + if proxyURL != "" { + proxyReq.Header.Set("X-Upstream-Proxy", proxyURL) + } + // 重写请求 URL:https://api.anthropic.com/v1/... → http://127.0.0.1:3456/v1/... proxyReq.URL.Scheme = "http" proxyReq.URL.Host = fmt.Sprintf("%s:%d", listenHost, listenPort)