From d99ae35c2ee660d2de1f43025ce613df9c79d974 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Sat, 19 Oct 2024 00:18:30 +0200 Subject: [PATCH] feat: add a proxy option into settings --- package.json | 1 + pnpm-lock.yaml | 11 ++++--- server/index.ts | 23 +++++++++++++++ server/lib/settings/index.ts | 2 ++ server/utils/restartFlag.ts | 3 +- .../Settings/SettingsMain/index.tsx | 29 +++++++++++++++++++ 6 files changed, 64 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9ce9330f..e3881398 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "express-session": "1.17.3", "formik": "^2.4.6", "gravatar-url": "3.1.0", + "https-proxy-agent": "^7.0.5", "lodash": "4.17.21", "mime": "3", "next": "^14.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7391a775..2d93710d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: gravatar-url: specifier: 3.1.0 version: 3.1.0 + https-proxy-agent: + specifier: ^7.0.5 + version: 7.0.5 lodash: specifier: 4.17.21 version: 4.17.21 @@ -5389,8 +5392,8 @@ packages: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - https-proxy-agent@7.0.4: - resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} human-signals@1.1.1: @@ -12310,7 +12313,7 @@ snapshots: fs-extra: 11.2.0 globby: 11.1.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 issue-parser: 6.0.0 lodash: 4.17.21 mime: 3.0.0 @@ -15739,7 +15742,7 @@ snapshots: transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.4: + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 debug: 4.3.5(supports-color@8.1.1) diff --git a/server/index.ts b/server/index.ts index 96590361..0b3af77d 100644 --- a/server/index.ts +++ b/server/index.ts @@ -22,6 +22,7 @@ import routes from '@server/routes'; import avatarproxy from '@server/routes/avatarproxy'; import imageproxy from '@server/routes/imageproxy'; import { getAppVersion } from '@server/utils/appVersion'; +import bindHttpMethod from '@server/utils/bindHttpMethod'; import restartFlag from '@server/utils/restartFlag'; import { getClientIp } from '@supercharge/request-ip'; import { TypeormStore } from 'connect-typeorm/out'; @@ -32,6 +33,9 @@ import express from 'express'; import * as OpenApiValidator from 'express-openapi-validator'; import type { Store } from 'express-session'; import session from 'express-session'; +import http from 'http'; +import https from 'https'; +import { HttpsProxyAgent } from 'https-proxy-agent'; import next from 'next'; import dns from 'node:dns'; import net from 'node:net'; @@ -67,6 +71,25 @@ app const settings = await getSettings().load(); restartFlag.initializeSettings(settings.main); + // Register HTTP proxy + if (settings.main.httpProxy) { + const agent = new HttpsProxyAgent(settings.main.httpProxy); + + (globalThis as any)[Symbol.for('undici.globalDispatcher.1')] = agent; + + http.globalAgent = agent; + https.globalAgent = agent; + + const httpGet = http.get; + const httpRequest = http.request; + const httpsGet = https.get; + const httpsRequest = https.request; + http.get = bindHttpMethod(httpGet, agent); + http.request = bindHttpMethod(httpRequest, agent); + https.get = bindHttpMethod(httpsGet, agent); + https.request = bindHttpMethod(httpsRequest, agent); + } + // Migrate library types if ( settings.plex.libraries.length > 1 && diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 0fc47af9..360aeb29 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -119,6 +119,7 @@ export interface MainSettings { mediaServerType: number; partialRequestsEnabled: boolean; locale: string; + httpProxy: string; } interface PublicSettings { @@ -325,6 +326,7 @@ class Settings { mediaServerType: MediaServerType.NOT_CONFIGURED, partialRequestsEnabled: true, locale: 'en', + httpProxy: '', }, plex: { name: '', diff --git a/server/utils/restartFlag.ts b/server/utils/restartFlag.ts index 387ec5ce..bb5f011d 100644 --- a/server/utils/restartFlag.ts +++ b/server/utils/restartFlag.ts @@ -13,7 +13,8 @@ class RestartFlag { return ( this.settings.csrfProtection !== settings.csrfProtection || - this.settings.trustProxy !== settings.trustProxy + this.settings.trustProxy !== settings.trustProxy || + this.settings.httpProxy !== settings.httpProxy ); } } diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx index f7aac0d9..b4fdea78 100644 --- a/src/components/Settings/SettingsMain/index.tsx +++ b/src/components/Settings/SettingsMain/index.tsx @@ -55,6 +55,8 @@ const messages = defineMessages('components.Settings.SettingsMain', { validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', partialRequestsEnabled: 'Allow Partial Series Requests', locale: 'Display Language', + httpProxy: 'HTTP Proxy', + httpProxyTip: 'Tooltip to write', }); const SettingsMain = () => { @@ -82,6 +84,9 @@ const SettingsMain = () => { intl.formatMessage(messages.validationApplicationUrlTrailingSlash), (value) => !value || !value.endsWith('/') ), + httpProxy: Yup.string().url( + intl.formatMessage(messages.validationApplicationUrl) + ), }); const regenerate = async () => { @@ -137,6 +142,7 @@ const SettingsMain = () => { partialRequestsEnabled: data?.partialRequestsEnabled, trustProxy: data?.trustProxy, cacheImages: data?.cacheImages, + httpProxy: data?.httpProxy, }} enableReinitialize validationSchema={MainSettingsSchema} @@ -158,6 +164,7 @@ const SettingsMain = () => { partialRequestsEnabled: values.partialRequestsEnabled, trustProxy: values.trustProxy, cacheImages: values.cacheImages, + httpProxy: values.httpProxy, }), }); if (!res.ok) throw new Error(); @@ -437,6 +444,28 @@ const SettingsMain = () => { /> +
+ +
+
+ +
+ {errors.httpProxy && + touched.httpProxy && + typeof errors.httpProxy === 'string' && ( +
{errors.httpProxy}
+ )} +
+