mirror of
https://github.com/sct/overseerr.git
synced 2025-12-26 16:27:17 +01:00
feat: add bypass list, bypass local addresses and username/password to proxy setting (#1059)
* fix: use fs/promises for settings This PR switches from synchronous operations with the 'fs' module to asynchronous operations with the 'fs/promises' module. It also corrects a small error with hostname migration. * fix: add missing merge function of default and current config * feat: add bypass list, bypass local addresses and username/password to proxy setting This PR adds more options to the proxy setting, like username/password authentication, bypass list of domains and bypass local addresses. The UX is taken from *arrs. * fix: add error handling for proxy creating * fix: remove logs
This commit is contained in:
111
server/utils/customProxyAgent.ts
Normal file
111
server/utils/customProxyAgent.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { ProxySettings } from '@server/lib/settings';
|
||||
import logger from '@server/logger';
|
||||
import type { Dispatcher } from 'undici';
|
||||
import { Agent, ProxyAgent, setGlobalDispatcher } from 'undici';
|
||||
|
||||
export default async function createCustomProxyAgent(
|
||||
proxySettings: ProxySettings
|
||||
) {
|
||||
const defaultAgent = new Agent();
|
||||
|
||||
const skipUrl = (url: string) => {
|
||||
const hostname = new URL(url).hostname;
|
||||
|
||||
if (proxySettings.bypassLocalAddresses && isLocalAddress(hostname)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const address of proxySettings.bypassFilter.split(',')) {
|
||||
const trimmedAddress = address.trim();
|
||||
if (!trimmedAddress) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmedAddress.startsWith('*')) {
|
||||
const domain = trimmedAddress.slice(1);
|
||||
if (hostname.endsWith(domain)) {
|
||||
return true;
|
||||
}
|
||||
} else if (hostname === trimmedAddress) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const noProxyInterceptor = (
|
||||
dispatch: Dispatcher['dispatch']
|
||||
): Dispatcher['dispatch'] => {
|
||||
return (opts, handler) => {
|
||||
const url = opts.origin?.toString();
|
||||
return url && skipUrl(url)
|
||||
? defaultAgent.dispatch(opts, handler)
|
||||
: dispatch(opts, handler);
|
||||
};
|
||||
};
|
||||
|
||||
const token =
|
||||
proxySettings.user && proxySettings.password
|
||||
? `Basic ${Buffer.from(
|
||||
`${proxySettings.user}:${proxySettings.password}`
|
||||
).toString('base64')}`
|
||||
: undefined;
|
||||
|
||||
try {
|
||||
const proxyAgent = new ProxyAgent({
|
||||
uri:
|
||||
(proxySettings.useSsl ? 'https://' : 'http://') +
|
||||
proxySettings.hostname +
|
||||
':' +
|
||||
proxySettings.port,
|
||||
token,
|
||||
interceptors: {
|
||||
Client: [noProxyInterceptor],
|
||||
},
|
||||
});
|
||||
|
||||
setGlobalDispatcher(proxyAgent);
|
||||
} catch (e) {
|
||||
logger.error('Failed to connect to the proxy: ' + e.message, {
|
||||
label: 'Proxy',
|
||||
});
|
||||
setGlobalDispatcher(defaultAgent);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('https://www.google.com', { method: 'HEAD' });
|
||||
if (res.ok) {
|
||||
logger.debug('HTTP(S) proxy connected successfully', { label: 'Proxy' });
|
||||
} else {
|
||||
logger.error('Proxy responded, but with a non-OK status: ' + res.status, {
|
||||
label: 'Proxy',
|
||||
});
|
||||
setGlobalDispatcher(defaultAgent);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
'Failed to connect to the proxy: ' + e.message + ': ' + e.cause,
|
||||
{ label: 'Proxy' }
|
||||
);
|
||||
setGlobalDispatcher(defaultAgent);
|
||||
}
|
||||
}
|
||||
|
||||
function isLocalAddress(hostname: string) {
|
||||
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const privateIpRanges = [
|
||||
/^10\./, // 10.x.x.x
|
||||
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.x.x - 172.31.x.x
|
||||
/^192\.168\./, // 192.168.x.x
|
||||
];
|
||||
if (privateIpRanges.some((regex) => regex.test(hostname))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user