Skip to content

Commit bf70c47

Browse files
gauthier-thbrotaxt
authored andcommitted
feat(settings): add settings for custom DNS servers and IPv4 resolution first (fallenbagel#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
1 parent 1509343 commit bf70c47

File tree

7 files changed

+91
-6
lines changed

7 files changed

+91
-6
lines changed

cypress/config/settings.cypress.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
"mediaServerType": 1,
2424
"partialRequestsEnabled": true,
2525
"enableSpecialEpisodes": false,
26+
"forceIpv4First": false,
27+
"dnsServers": "",
2628
"locale": "en"
2729
},
2830
"plex": {

overseerr-api.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ components:
191191
enableSpecialEpisodes:
192192
type: boolean
193193
example: false
194+
forceIpv4First:
195+
type: boolean
196+
example: false
197+
dnsServers:
198+
type: string
199+
example: '1.1.1.1'
194200
PlexLibrary:
195201
type: object
196202
properties:

server/index.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@ import path from 'path';
4141
import swaggerUi from 'swagger-ui-express';
4242
import YAML from 'yamljs';
4343

44-
if (process.env.forceIpv4First === 'true') {
45-
dns.setDefaultResultOrder('ipv4first');
46-
net.setDefaultAutoSelectFamily(false);
47-
}
48-
4944
const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml');
5045

5146
logger.info(`Starting Overseerr version ${getAppVersion()}`);
@@ -79,6 +74,18 @@ app
7974
const settings = await getSettings().load();
8075
restartFlag.initializeSettings(settings.main);
8176

77+
// Check if we force IPv4 first
78+
if (process.env.forceIpv4First === 'true' || settings.main.forceIpv4First) {
79+
dns.setDefaultResultOrder('ipv4first');
80+
net.setDefaultAutoSelectFamily(false);
81+
}
82+
83+
if (settings.main.dnsServers.trim() !== '') {
84+
dns.setServers(
85+
settings.main.dnsServers.split(',').map((server) => server.trim())
86+
);
87+
}
88+
8289
// Register HTTP proxy
8390
if (settings.main.proxy.enabled) {
8491
await createCustomProxyAgent(settings.main.proxy);

server/lib/settings/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ export interface MainSettings {
132132
mediaServerType: number;
133133
partialRequestsEnabled: boolean;
134134
enableSpecialEpisodes: boolean;
135+
forceIpv4First: boolean;
136+
dnsServers: string;
135137
locale: string;
136138
proxy: ProxySettings;
137139
}
@@ -346,6 +348,8 @@ class Settings {
346348
mediaServerType: MediaServerType.NOT_CONFIGURED,
347349
partialRequestsEnabled: true,
348350
enableSpecialEpisodes: false,
351+
forceIpv4First: false,
352+
dnsServers: '',
349353
locale: 'en',
350354
proxy: {
351355
enabled: false,

server/utils/restartFlag.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ class RestartFlag {
1414
return (
1515
this.settings.csrfProtection !== settings.csrfProtection ||
1616
this.settings.trustProxy !== settings.trustProxy ||
17-
this.settings.proxy.enabled !== settings.proxy.enabled
17+
this.settings.proxy.enabled !== settings.proxy.enabled ||
18+
this.settings.forceIpv4First !== settings.forceIpv4First ||
19+
this.settings.dnsServers !== settings.dnsServers
1820
);
1921
}
2022
}

src/components/Settings/SettingsMain/index.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ const messages = defineMessages('components.Settings.SettingsMain', {
5757
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
5858
partialRequestsEnabled: 'Allow Partial Series Requests',
5959
enableSpecialEpisodes: 'Allow Special Episodes Requests',
60+
forceIpv4First: 'IPv4 Resolution First',
61+
forceIpv4FirstTip:
62+
'Force Jellyseerr to resolve IPv4 addresses first instead of IPv6',
63+
dnsServers: 'Custom DNS Servers',
64+
dnsServersTip:
65+
'Comma-separated list of custom DNS servers, e.g. "1.1.1.1,[2606:4700:4700::1111]"',
6066
locale: 'Display Language',
6167
proxyEnabled: 'HTTP(S) Proxy',
6268
proxyHostname: 'Proxy Hostname',
@@ -160,6 +166,8 @@ const SettingsMain = () => {
160166
streamingRegion: data?.streamingRegion || 'US',
161167
partialRequestsEnabled: data?.partialRequestsEnabled,
162168
enableSpecialEpisodes: data?.enableSpecialEpisodes,
169+
forceIpv4First: data?.forceIpv4First,
170+
dnsServers: data?.dnsServers,
163171
trustProxy: data?.trustProxy,
164172
cacheImages: data?.cacheImages,
165173
proxyEnabled: data?.proxy?.enabled,
@@ -191,6 +199,8 @@ const SettingsMain = () => {
191199
originalLanguage: values.originalLanguage,
192200
partialRequestsEnabled: values.partialRequestsEnabled,
193201
enableSpecialEpisodes: values.enableSpecialEpisodes,
202+
forceIpv4First: values.forceIpv4First,
203+
dnsServers: values.dnsServers,
194204
trustProxy: values.trustProxy,
195205
cacheImages: values.cacheImages,
196206
proxy: {
@@ -524,6 +534,55 @@ const SettingsMain = () => {
524534
/>
525535
</div>
526536
</div>
537+
<div className="form-row">
538+
<label htmlFor="forceIpv4First" className="checkbox-label">
539+
<span className="mr-2">
540+
{intl.formatMessage(messages.forceIpv4First)}
541+
</span>
542+
<SettingsBadge badgeType="advanced" className="mr-2" />
543+
<SettingsBadge badgeType="restartRequired" />
544+
<span className="label-tip">
545+
{intl.formatMessage(messages.forceIpv4FirstTip)}
546+
</span>
547+
</label>
548+
<div className="form-input-area">
549+
<Field
550+
type="checkbox"
551+
id="forceIpv4First"
552+
name="forceIpv4First"
553+
onChange={() => {
554+
setFieldValue('forceIpv4First', !values.forceIpv4First);
555+
}}
556+
/>
557+
</div>
558+
</div>
559+
<div className="form-row">
560+
<label htmlFor="dnsServers" className="checkbox-label">
561+
<span className="mr-2">
562+
{intl.formatMessage(messages.dnsServers)}
563+
</span>
564+
<SettingsBadge badgeType="advanced" className="mr-2" />
565+
<SettingsBadge badgeType="restartRequired" />
566+
<span className="label-tip">
567+
{intl.formatMessage(messages.dnsServersTip)}
568+
</span>
569+
</label>
570+
<div className="form-input-area">
571+
<div className="form-input-field">
572+
<Field
573+
id="dnsServers"
574+
name="dnsServers"
575+
type="text"
576+
inputMode="url"
577+
/>
578+
</div>
579+
{errors.dnsServers &&
580+
touched.dnsServers &&
581+
typeof errors.dnsServers === 'string' && (
582+
<div className="error">{errors.dnsServers}</div>
583+
)}
584+
</div>
585+
</div>
527586
<div className="form-row">
528587
<label htmlFor="proxyEnabled" className="checkbox-label">
529588
<span className="mr-2">

src/i18n/locale/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
"components.Login.initialsigningin": "Connecting…",
247247
"components.Login.invalidurlerror": "Unable to connect to {mediaServerName} server.",
248248
"components.Login.loginerror": "Something went wrong while trying to sign in.",
249+
"components.Login.noadminerror": "No admin user found on the server.",
249250
"components.Login.password": "Password",
250251
"components.Login.port": "Port",
251252
"components.Login.save": "Add",
@@ -919,7 +920,11 @@
919920
"components.Settings.SettingsMain.csrfProtectionTip": "Set external API access to read-only (requires HTTPS)",
920921
"components.Settings.SettingsMain.discoverRegion": "Discover Region",
921922
"components.Settings.SettingsMain.discoverRegionTip": "Filter content by regional availability",
923+
"components.Settings.SettingsMain.dnsServers": "Custom DNS Servers",
924+
"components.Settings.SettingsMain.dnsServersTip": "Comma-separated list of custom DNS servers, e.g. \"1.1.1.1,[2606:4700:4700::1111]\"",
922925
"components.Settings.SettingsMain.enableSpecialEpisodes": "Allow Special Episodes Requests",
926+
"components.Settings.SettingsMain.forceIpv4First": "IPv4 Resolution First",
927+
"components.Settings.SettingsMain.forceIpv4FirstTip": "Force Jellyseerr to resolve IPv4 addresses first instead of IPv6",
923928
"components.Settings.SettingsMain.general": "General",
924929
"components.Settings.SettingsMain.generalsettings": "General Settings",
925930
"components.Settings.SettingsMain.generalsettingsDescription": "Configure global and default settings for Jellyseerr.",

0 commit comments

Comments
 (0)