forked from boos/BoosAPI
feat(proxy): add account-level proxy resolution core
Add getProxyUrlFromExtraConfig to extract proxy URL from account extraConfig JSON. Extend siteProxy with AsyncLocalStorage-based withAccountProxyOverride, resolveChannelProxyUrl, and optional accountProxyUrl parameter on withSiteRecordProxyRequestInit. Priority chain: account proxyUrl > site useSystemProxy > direct.
This commit is contained in:
@@ -67,6 +67,11 @@ export function normalizeCredentialMode(raw: unknown): AccountCredentialMode | u
|
|||||||
return normalized as AccountCredentialMode;
|
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 {
|
export function getPlatformUserIdFromExtraConfig(extraConfig?: string | null): number | undefined {
|
||||||
const parsed = parseExtraConfig(extraConfig);
|
const parsed = parseExtraConfig(extraConfig);
|
||||||
return normalizeUserId(parsed.platformUserId);
|
return normalizeUserId(parsed.platformUserId);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
||||||
import { db, schema } from '../db/index.js';
|
import { db, schema } from '../db/index.js';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { config } from '../config.js';
|
import { config } from '../config.js';
|
||||||
import type { Dispatcher, RequestInit as UndiciRequestInit } from 'undici';
|
import type { Dispatcher, RequestInit as UndiciRequestInit } from 'undici';
|
||||||
import { ProxyAgent } from 'undici';
|
import { ProxyAgent } from 'undici';
|
||||||
import { mergeHeadersWithSiteCustomHeaders } from './siteCustomHeaders.js';
|
import { mergeHeadersWithSiteCustomHeaders } from './siteCustomHeaders.js';
|
||||||
|
import { getProxyUrlFromExtraConfig } from './accountExtraConfig.js';
|
||||||
|
|
||||||
const SITE_PROXY_CACHE_TTL_MS = 3_000;
|
const SITE_PROXY_CACHE_TTL_MS = 3_000;
|
||||||
const SUPPORTED_PROXY_PROTOCOLS = new Set([
|
const SUPPORTED_PROXY_PROTOCOLS = new Set([
|
||||||
@@ -45,6 +47,17 @@ let siteProxyCache: {
|
|||||||
|
|
||||||
const dispatcherCache = new Map<string, Dispatcher>();
|
const dispatcherCache = new Map<string, Dispatcher>();
|
||||||
|
|
||||||
|
const accountProxyOverride = new AsyncLocalStorage<string | null>();
|
||||||
|
|
||||||
|
export function withAccountProxyOverride<T>(
|
||||||
|
proxyUrl: string | null | undefined,
|
||||||
|
fn: () => Promise<T>,
|
||||||
|
): Promise<T> {
|
||||||
|
const normalized = normalizeSiteProxyUrl(proxyUrl);
|
||||||
|
if (!normalized) return fn();
|
||||||
|
return accountProxyOverride.run(normalized, fn);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeSiteUrl(value: string): string {
|
function normalizeSiteUrl(value: string): string {
|
||||||
const trimmed = (value || '').trim();
|
const trimmed = (value || '').trim();
|
||||||
if (!trimmed) return '';
|
if (!trimmed) return '';
|
||||||
@@ -231,11 +244,14 @@ export async function withSiteProxyRequestInit(
|
|||||||
nextOptions.headers = mergedHeaders;
|
nextOptions.headers = mergedHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resolved.proxyUrl) {
|
const alsOverride = accountProxyOverride.getStore();
|
||||||
|
const proxyUrl = alsOverride ?? resolved.proxyUrl;
|
||||||
|
|
||||||
|
if (!proxyUrl) {
|
||||||
return nextOptions;
|
return nextOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatcher = getDispatcherByProxyUrl(resolved.proxyUrl);
|
const dispatcher = getDispatcherByProxyUrl(proxyUrl);
|
||||||
if (!dispatcher) {
|
if (!dispatcher) {
|
||||||
return nextOptions;
|
return nextOptions;
|
||||||
}
|
}
|
||||||
@@ -270,6 +286,7 @@ export function resolveProxyUrlForSite(site: SiteProxyConfigLike | null | undefi
|
|||||||
export function withSiteRecordProxyRequestInit(
|
export function withSiteRecordProxyRequestInit(
|
||||||
site: SiteProxyConfigLike | null | undefined,
|
site: SiteProxyConfigLike | null | undefined,
|
||||||
options?: UndiciRequestInit,
|
options?: UndiciRequestInit,
|
||||||
|
accountProxyUrl?: string | null,
|
||||||
): UndiciRequestInit {
|
): UndiciRequestInit {
|
||||||
const nextOptions: UndiciRequestInit = {
|
const nextOptions: UndiciRequestInit = {
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
@@ -278,5 +295,17 @@ export function withSiteRecordProxyRequestInit(
|
|||||||
if (mergedHeaders) {
|
if (mergedHeaders) {
|
||||||
nextOptions.headers = 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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user