mirror of
https://github.com/seerr-team/seerr.git
synced 2026-08-02 11:09:16 -04:00
Proxying for axios relied on the per-instance httpAgent/httpsAgent, which axios.create() snapshots from axios.defaults at construction time. API clients constructed at import (such as BaseScanner.tmdb, avalabilitySync.tmdb) are built before createCustomProxyAgent runs, so they snapshotted no proxy agent and bypassed the proxy permanently, while clients created per-request afterwards were proxied. This fix resolves the agent per request from a live module-level holder via a stable interceptor that is registered on the default instance and every ExternalAPI client, so construction order no longer matters. forceIpv4First has been moved to the same path logic as well. fix #3193
73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
import ExternalAPI from '@server/api/externalapi';
|
|
import type { ProxySettings } from '@server/lib/settings';
|
|
import createCustomProxyAgent from '@server/utils/customProxyAgent';
|
|
import axios, {
|
|
type AxiosResponse,
|
|
type InternalAxiosRequestConfig,
|
|
} from 'axios';
|
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
import assert from 'node:assert/strict';
|
|
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
|
|
|
class TestExternalAPI extends ExternalAPI {
|
|
public constructor() {
|
|
super('https://api.themoviedb.org/3', {}, {});
|
|
}
|
|
|
|
public async resolvedHttpsAgent(path = '/movie/123'): Promise<unknown> {
|
|
let captured: InternalAxiosRequestConfig | undefined;
|
|
this.axios.defaults.adapter = (config) => {
|
|
captured = config;
|
|
return Promise.resolve({
|
|
data: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config,
|
|
request: {},
|
|
} as AxiosResponse);
|
|
};
|
|
await this.axios.get(path);
|
|
return captured?.httpsAgent;
|
|
}
|
|
}
|
|
|
|
// constructed before any test configures the proxy, like BaseScanner.tmdb
|
|
const preProxyClient = new TestExternalAPI();
|
|
|
|
const proxySettings: ProxySettings = {
|
|
enabled: true,
|
|
hostname: 'proxy.test',
|
|
port: 3128,
|
|
useSsl: false,
|
|
user: '',
|
|
password: '',
|
|
bypassFilter: '*.bypass.test',
|
|
bypassLocalAddresses: true,
|
|
};
|
|
|
|
describe('proxy routing (construction-order independence)', () => {
|
|
beforeEach(() => {
|
|
mock.method(axios, 'head', async () => ({ status: 200 }));
|
|
});
|
|
|
|
afterEach(() => {
|
|
mock.restoreAll();
|
|
});
|
|
|
|
it('routes a client constructed BEFORE the proxy was configured', async () => {
|
|
await createCustomProxyAgent(proxySettings);
|
|
const agent = await preProxyClient.resolvedHttpsAgent();
|
|
assert.ok(
|
|
agent instanceof HttpsProxyAgent,
|
|
'client created before proxy setup must still route through the proxy'
|
|
);
|
|
});
|
|
|
|
it('routes a client constructed AFTER the proxy was configured', async () => {
|
|
await createCustomProxyAgent(proxySettings);
|
|
const agent = await new TestExternalAPI().resolvedHttpsAgent();
|
|
assert.ok(agent instanceof HttpsProxyAgent);
|
|
});
|
|
});
|