From 7fcc0eb66d907e74b72197d6abee511150ab5e1e Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 16 Jan 2025 10:46:27 +0100 Subject: [PATCH] feat(settings): add settings for custom DNS servers and IPv4 resolution first (#1266) * feat(settings): add settings for custom DNS servers and IPv4 resolution first This PR adds settings to change the DNS servers Jellyseerr uses and to force Jellyseerr to resolve DNS queries using IPv4 first. These settings aim to make it easier for less experienced users to fix network errors related to DNS resolution. * style: fix missing newline --- cypress/config/settings.cypress.json | 2 + overseerr-api.yml | 6 ++ server/index.ts | 17 ++++-- server/lib/settings/index.ts | 4 ++ server/utils/restartFlag.ts | 4 +- .../Settings/SettingsMain/index.tsx | 59 +++++++++++++++++++ src/i18n/locale/en.json | 5 ++ 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json index e3d31cc11..e49d8888b 100644 --- a/cypress/config/settings.cypress.json +++ b/cypress/config/settings.cypress.json @@ -23,6 +23,8 @@ "mediaServerType": 1, "partialRequestsEnabled": true, "enableSpecialEpisodes": false, + "forceIpv4First": false, + "dnsServers": "", "locale": "en" }, "plex": { diff --git a/overseerr-api.yml b/overseerr-api.yml index b707ae8b3..f3be28ad2 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -191,6 +191,12 @@ components: enableSpecialEpisodes: type: boolean example: false + forceIpv4First: + type: boolean + example: false + dnsServers: + type: string + example: '1.1.1.1' PlexLibrary: type: object properties: diff --git a/server/index.ts b/server/index.ts index 64233c0e2..dde0d5083 100644 --- a/server/index.ts +++ b/server/index.ts @@ -41,11 +41,6 @@ import path from 'path'; import swaggerUi from 'swagger-ui-express'; import YAML from 'yamljs'; -if (process.env.forceIpv4First === 'true') { - dns.setDefaultResultOrder('ipv4first'); - net.setDefaultAutoSelectFamily(false); -} - const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml'); logger.info(`Starting Overseerr version ${getAppVersion()}`); @@ -79,6 +74,18 @@ app const settings = await getSettings().load(); restartFlag.initializeSettings(settings.main); + // Check if we force IPv4 first + if (process.env.forceIpv4First === 'true' || settings.main.forceIpv4First) { + dns.setDefaultResultOrder('ipv4first'); + net.setDefaultAutoSelectFamily(false); + } + + if (settings.main.dnsServers.trim() !== '') { + dns.setServers( + settings.main.dnsServers.split(',').map((server) => server.trim()) + ); + } + // Register HTTP proxy if (settings.main.proxy.enabled) { await createCustomProxyAgent(settings.main.proxy); diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index cd8ebb974..343c01e2f 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -132,6 +132,8 @@ export interface MainSettings { mediaServerType: number; partialRequestsEnabled: boolean; enableSpecialEpisodes: boolean; + forceIpv4First: boolean; + dnsServers: string; locale: string; proxy: ProxySettings; } @@ -346,6 +348,8 @@ class Settings { mediaServerType: MediaServerType.NOT_CONFIGURED, partialRequestsEnabled: true, enableSpecialEpisodes: false, + forceIpv4First: false, + dnsServers: '', locale: 'en', proxy: { enabled: false, diff --git a/server/utils/restartFlag.ts b/server/utils/restartFlag.ts index 18d03ea64..6b364d1f0 100644 --- a/server/utils/restartFlag.ts +++ b/server/utils/restartFlag.ts @@ -14,7 +14,9 @@ class RestartFlag { return ( this.settings.csrfProtection !== settings.csrfProtection || this.settings.trustProxy !== settings.trustProxy || - this.settings.proxy.enabled !== settings.proxy.enabled + this.settings.proxy.enabled !== settings.proxy.enabled || + this.settings.forceIpv4First !== settings.forceIpv4First || + this.settings.dnsServers !== settings.dnsServers ); } } diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx index 199b220a3..fb1df6b5e 100644 --- a/src/components/Settings/SettingsMain/index.tsx +++ b/src/components/Settings/SettingsMain/index.tsx @@ -57,6 +57,12 @@ const messages = defineMessages('components.Settings.SettingsMain', { validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', partialRequestsEnabled: 'Allow Partial Series Requests', enableSpecialEpisodes: 'Allow Special Episodes Requests', + forceIpv4First: 'IPv4 Resolution First', + forceIpv4FirstTip: + 'Force Jellyseerr to resolve IPv4 addresses first instead of IPv6', + dnsServers: 'Custom DNS Servers', + dnsServersTip: + 'Comma-separated list of custom DNS servers, e.g. "1.1.1.1,[2606:4700:4700::1111]"', locale: 'Display Language', proxyEnabled: 'HTTP(S) Proxy', proxyHostname: 'Proxy Hostname', @@ -160,6 +166,8 @@ const SettingsMain = () => { streamingRegion: data?.streamingRegion || 'US', partialRequestsEnabled: data?.partialRequestsEnabled, enableSpecialEpisodes: data?.enableSpecialEpisodes, + forceIpv4First: data?.forceIpv4First, + dnsServers: data?.dnsServers, trustProxy: data?.trustProxy, cacheImages: data?.cacheImages, proxyEnabled: data?.proxy?.enabled, @@ -191,6 +199,8 @@ const SettingsMain = () => { originalLanguage: values.originalLanguage, partialRequestsEnabled: values.partialRequestsEnabled, enableSpecialEpisodes: values.enableSpecialEpisodes, + forceIpv4First: values.forceIpv4First, + dnsServers: values.dnsServers, trustProxy: values.trustProxy, cacheImages: values.cacheImages, proxy: { @@ -524,6 +534,55 @@ const SettingsMain = () => { /> +
+ +
+ { + setFieldValue('forceIpv4First', !values.forceIpv4First); + }} + /> +
+
+
+ +
+
+ +
+ {errors.dnsServers && + touched.dnsServers && + typeof errors.dnsServers === 'string' && ( +
{errors.dnsServers}
+ )} +
+