fix(proxy): separate upstream target resolution from proxy dispatch and add site-level proxy URLs
This commit is contained in:
@@ -57,6 +57,22 @@ describe('siteProxy', () => {
|
||||
.toBeNull();
|
||||
});
|
||||
|
||||
it('prefers site-specific proxy url over the shared system proxy', async () => {
|
||||
await db.insert(schema.settings).values({
|
||||
key: 'system_proxy_url',
|
||||
value: JSON.stringify('http://127.0.0.1:7890'),
|
||||
}).run();
|
||||
|
||||
await db.run(sql`
|
||||
INSERT INTO sites (name, url, platform, proxy_url, use_system_proxy)
|
||||
VALUES ('proxy-site', 'https://proxy-site.example.com', 'new-api', 'socks5://127.0.0.1:1080', 1)
|
||||
`);
|
||||
|
||||
const { resolveSiteProxyUrlByRequestUrl } = await import('./siteProxy.js');
|
||||
expect(await resolveSiteProxyUrlByRequestUrl('https://proxy-site.example.com/v1/models'))
|
||||
.toBe('socks5://127.0.0.1:1080');
|
||||
});
|
||||
|
||||
it('injects dispatcher when a site opts into the configured system proxy', async () => {
|
||||
await db.insert(schema.settings).values({
|
||||
key: 'system_proxy_url',
|
||||
@@ -75,6 +91,20 @@ describe('siteProxy', () => {
|
||||
expect('dispatcher' in requestInit).toBe(true);
|
||||
});
|
||||
|
||||
it('injects dispatcher when a site defines its own proxy url', async () => {
|
||||
await db.run(sql`
|
||||
INSERT INTO sites (name, url, platform, proxy_url, use_system_proxy)
|
||||
VALUES ('proxy-site', 'https://proxy-site.example.com', 'new-api', 'http://127.0.0.1:7890', 0)
|
||||
`);
|
||||
|
||||
const { withSiteProxyRequestInit } = await import('./siteProxy.js');
|
||||
const requestInit = await withSiteProxyRequestInit('https://proxy-site.example.com/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect('dispatcher' in requestInit).toBe(true);
|
||||
});
|
||||
|
||||
it('injects a working dispatcher for socks5 system proxies', async () => {
|
||||
const upstreamServer = createServer((_request, response) => {
|
||||
response.writeHead(200, { 'content-type': 'application/json' });
|
||||
@@ -170,6 +200,7 @@ describe('siteProxy', () => {
|
||||
it('merges site custom headers from site records even without cache lookup', async () => {
|
||||
const { withSiteRecordProxyRequestInit } = await import('./siteProxy.js');
|
||||
const requestInit = withSiteRecordProxyRequestInit({
|
||||
proxyUrl: 'http://127.0.0.1:7890',
|
||||
useSystemProxy: false,
|
||||
customHeaders: JSON.stringify({
|
||||
'x-site-scope': 'site-level',
|
||||
@@ -184,5 +215,6 @@ describe('siteProxy', () => {
|
||||
|
||||
expect(headers.get('x-site-scope')).toBe('site-level');
|
||||
expect(headers.get('x-request-id')).toBe('req-1');
|
||||
expect('dispatcher' in requestInit).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ const DEFAULT_PROXY_KEEPALIVE_INITIAL_DELAY_MS = 60_000;
|
||||
|
||||
type SiteProxyRow = {
|
||||
siteUrl: string;
|
||||
proxyUrl: string | null;
|
||||
useSystemProxy: boolean;
|
||||
customHeaders: string | null;
|
||||
};
|
||||
@@ -42,6 +43,7 @@ type ParsedSiteProxyInput = {
|
||||
};
|
||||
|
||||
type SiteProxyConfigLike = {
|
||||
proxyUrl?: string | null;
|
||||
useSystemProxy?: boolean | null;
|
||||
customHeaders?: string | null;
|
||||
};
|
||||
@@ -103,6 +105,7 @@ async function getCachedSiteProxyRows(nowMs = Date.now()): Promise<SiteProxyRow[
|
||||
db
|
||||
.select({
|
||||
siteUrl: schema.sites.url,
|
||||
proxyUrl: schema.sites.proxyUrl,
|
||||
useSystemProxy: schema.sites.useSystemProxy,
|
||||
customHeaders: schema.sites.customHeaders,
|
||||
})
|
||||
@@ -129,6 +132,7 @@ async function getCachedSiteProxyRows(nowMs = Date.now()): Promise<SiteProxyRow[
|
||||
loadedAt: nowMs,
|
||||
rows: rows.map((row) => ({
|
||||
siteUrl: normalizeSiteUrl(row.siteUrl),
|
||||
proxyUrl: normalizeSiteProxyUrl(row.proxyUrl),
|
||||
useSystemProxy: !!row.useSystemProxy,
|
||||
customHeaders: typeof row.customHeaders === 'string' ? row.customHeaders : null,
|
||||
})),
|
||||
@@ -368,7 +372,8 @@ async function resolveSiteRequestConfigByRequestUrl(requestUrl: string): Promise
|
||||
|
||||
const rows = await getCachedSiteProxyRows();
|
||||
const matchedRow = findBestMatchingSiteRow(rows, normalizedRequestUrl);
|
||||
const proxyUrl = matchedRow?.useSystemProxy ? siteProxyCache.systemProxyUrl : null;
|
||||
const proxyUrl = matchedRow?.proxyUrl
|
||||
|| (matchedRow?.useSystemProxy ? siteProxyCache.systemProxyUrl : null);
|
||||
return {
|
||||
proxyUrl: proxyUrl || null,
|
||||
customHeaders: matchedRow?.customHeaders ?? null,
|
||||
@@ -425,6 +430,8 @@ export function withExplicitProxyRequestInit(
|
||||
}
|
||||
|
||||
export function resolveProxyUrlForSite(site: SiteProxyConfigLike | null | undefined): string | null {
|
||||
const explicitProxyUrl = normalizeSiteProxyUrl(site?.proxyUrl);
|
||||
if (explicitProxyUrl) return explicitProxyUrl;
|
||||
if (!site?.useSystemProxy) return null;
|
||||
return normalizeSiteProxyUrl(config.systemProxyUrl);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user