diff --git a/src/server/services/accountExtraConfig.ts b/src/server/services/accountExtraConfig.ts index 280ee69..1950761 100644 --- a/src/server/services/accountExtraConfig.ts +++ b/src/server/services/accountExtraConfig.ts @@ -67,6 +67,11 @@ export function normalizeCredentialMode(raw: unknown): AccountCredentialMode | u return normalized as AccountCredentialMode; } +export function getProxyUrlFromExtraConfig(extraConfig?: string | null): string | null { + const parsed = parseExtraConfig(extraConfig); + return normalizeNonEmptyString(parsed.proxyUrl) ?? null; +} + export function getPlatformUserIdFromExtraConfig(extraConfig?: string | null): number | undefined { const parsed = parseExtraConfig(extraConfig); return normalizeUserId(parsed.platformUserId); diff --git a/src/server/services/siteProxy.ts b/src/server/services/siteProxy.ts index 77b46c7..878ad03 100644 --- a/src/server/services/siteProxy.ts +++ b/src/server/services/siteProxy.ts @@ -1,9 +1,11 @@ +import { AsyncLocalStorage } from 'node:async_hooks'; import { db, schema } from '../db/index.js'; import { eq } from 'drizzle-orm'; import { config } from '../config.js'; import type { Dispatcher, RequestInit as UndiciRequestInit } from 'undici'; import { ProxyAgent } from 'undici'; import { mergeHeadersWithSiteCustomHeaders } from './siteCustomHeaders.js'; +import { getProxyUrlFromExtraConfig } from './accountExtraConfig.js'; const SITE_PROXY_CACHE_TTL_MS = 3_000; const SUPPORTED_PROXY_PROTOCOLS = new Set([ @@ -45,6 +47,17 @@ let siteProxyCache: { const dispatcherCache = new Map(); +const accountProxyOverride = new AsyncLocalStorage(); + +export function withAccountProxyOverride( + proxyUrl: string | null | undefined, + fn: () => Promise, +): Promise { + const normalized = normalizeSiteProxyUrl(proxyUrl); + if (!normalized) return fn(); + return accountProxyOverride.run(normalized, fn); +} + function normalizeSiteUrl(value: string): string { const trimmed = (value || '').trim(); if (!trimmed) return ''; @@ -231,11 +244,14 @@ export async function withSiteProxyRequestInit( nextOptions.headers = mergedHeaders; } - if (!resolved.proxyUrl) { + const alsOverride = accountProxyOverride.getStore(); + const proxyUrl = alsOverride ?? resolved.proxyUrl; + + if (!proxyUrl) { return nextOptions; } - const dispatcher = getDispatcherByProxyUrl(resolved.proxyUrl); + const dispatcher = getDispatcherByProxyUrl(proxyUrl); if (!dispatcher) { return nextOptions; } @@ -270,6 +286,7 @@ export function resolveProxyUrlForSite(site: SiteProxyConfigLike | null | undefi export function withSiteRecordProxyRequestInit( site: SiteProxyConfigLike | null | undefined, options?: UndiciRequestInit, + accountProxyUrl?: string | null, ): UndiciRequestInit { const nextOptions: UndiciRequestInit = { ...(options || {}), @@ -278,5 +295,17 @@ export function withSiteRecordProxyRequestInit( if (mergedHeaders) { nextOptions.headers = mergedHeaders; } - return withExplicitProxyRequestInit(resolveProxyUrlForSite(site), nextOptions); + const proxyUrl = normalizeSiteProxyUrl(accountProxyUrl) || resolveProxyUrlForSite(site); + return withExplicitProxyRequestInit(proxyUrl, nextOptions); +} + +export function resolveChannelProxyUrl( + site: SiteProxyConfigLike | null | undefined, + accountExtraConfig?: string | null, +): string | null { + if (accountExtraConfig) { + const normalized = normalizeSiteProxyUrl(getProxyUrlFromExtraConfig(accountExtraConfig)); + if (normalized) return normalized; + } + return resolveProxyUrlForSite(site); }