feat: add force ipv4 first setting (#1719)

This PR adds a 'Force IPv4' setting in the network settings, like the one that existed before we
migrated from Fetch API to Axios.
This commit is contained in:
Gauthier
2025-06-17 21:35:52 +02:00
committed by GitHub
parent 049bc59d2d
commit 0357d17205
6 changed files with 50 additions and 3 deletions

View File

@@ -105,6 +105,12 @@ In some places (like China), the ISP blocks not only the DNS resolution but also
You can configure Jellyseerr to use a proxy with the [HTTP(S) Proxy](/using-jellyseerr/settings/general#https-proxy) setting.
### Option 3: Force IPV4 resolution first
Sometimes there are configuration issues with IPV6 that prevent the hostname resolution from working correctly.
You can try to force the resolution to use IPV4 first by going to `Settings > Networking > Advanced Networking` and enabling `Force IPv4 Resolution First` setting and restarting Jellyseerr.
### Option 4: Check that your server can reach TMDB API
Make sure that your server can reach the TMDB API by running the following command:

4
pnpm-lock.yaml generated
View File

@@ -15811,7 +15811,7 @@ snapshots:
debug: 4.3.5
enhanced-resolve: 5.17.0
eslint: 8.35.0
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.35.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.35.0)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.35.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.35.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.35.0))(eslint@8.35.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.54.0(eslint@8.35.0)(typescript@4.9.5))(eslint@8.35.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.5
@@ -15833,7 +15833,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.35.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.35.0):
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.35.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.35.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.35.0))(eslint@8.35.0):
dependencies:
debug: 3.2.7(supports-color@8.1.1)
optionalDependencies:

View File

@@ -28,6 +28,7 @@ import { getAppVersion } from '@server/utils/appVersion';
import createCustomProxyAgent from '@server/utils/customProxyAgent';
import restartFlag from '@server/utils/restartFlag';
import { getClientIp } from '@supercharge/request-ip';
import axios from 'axios';
import { TypeormStore } from 'connect-typeorm/out';
import cookieParser from 'cookie-parser';
import type { NextFunction, Request, Response } from 'express';
@@ -35,6 +36,8 @@ 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 next from 'next';
import path from 'path';
import swaggerUi from 'swagger-ui-express';
@@ -73,6 +76,11 @@ app
const settings = await getSettings().load();
restartFlag.initializeSettings(settings);
if (settings.network.forceIpv4First) {
axios.defaults.httpAgent = new http.Agent({ family: 4 });
axios.defaults.httpsAgent = new https.Agent({ family: 4 });
}
// Register HTTP proxy
if (settings.network.proxy.enabled) {
await createCustomProxyAgent(settings.network.proxy);

View File

@@ -140,6 +140,7 @@ export interface MainSettings {
export interface NetworkSettings {
csrfProtection: boolean;
forceIpv4First: boolean;
trustProxy: boolean;
proxy: ProxySettings;
}
@@ -544,6 +545,7 @@ class Settings {
},
network: {
csrfProtection: false,
forceIpv4First: false,
trustProxy: false,
proxy: {
enabled: false,

View File

@@ -42,6 +42,9 @@ const messages = defineMessages('components.Settings.SettingsNetwork', {
networkDisclaimer:
'Network parameters from your container/system should be used instead of these settings. See the {docs} for more information.',
docs: 'documentation',
forceIpv4First: 'Force IPv4 Resolution First',
forceIpv4FirstTip:
'Force Jellyseerr to resolve IPv4 addresses first instead of IPv6',
});
const SettingsNetwork = () => {
@@ -86,6 +89,7 @@ const SettingsNetwork = () => {
<Formik
initialValues={{
csrfProtection: data?.csrfProtection,
forceIpv4First: data?.forceIpv4First,
trustProxy: data?.trustProxy,
proxyEnabled: data?.proxy?.enabled,
proxyHostname: data?.proxy?.hostname,
@@ -102,6 +106,7 @@ const SettingsNetwork = () => {
try {
await axios.post('/api/v1/settings/network', {
csrfProtection: values.csrfProtection,
forceIpv4First: values.forceIpv4First,
trustProxy: values.trustProxy,
proxy: {
enabled: values.proxyEnabled,
@@ -193,6 +198,29 @@ const SettingsNetwork = () => {
</Tooltip>
</div>
</div>
<div className="form-row">
<label htmlFor="forceIpv4First" className="checkbox-label">
<span className="mr-2">
{intl.formatMessage(messages.forceIpv4First)}
</span>
<SettingsBadge badgeType="advanced" className="mr-2" />
<SettingsBadge badgeType="restartRequired" />
<SettingsBadge badgeType="experimental" />
<span className="label-tip">
{intl.formatMessage(messages.forceIpv4FirstTip)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="forceIpv4First"
name="forceIpv4First"
onChange={() => {
setFieldValue('forceIpv4First', !values.forceIpv4First);
}}
/>
</div>
</div>
<div className="form-row">
<label htmlFor="proxyEnabled" className="checkbox-label">
<span className="mr-2">

View File

@@ -978,10 +978,13 @@
"components.Settings.SettingsMain.validationUrl": "You must provide a valid URL",
"components.Settings.SettingsMain.validationUrlTrailingSlash": "URL must not end in a trailing slash",
"components.Settings.SettingsMain.youtubeUrl": "YouTube URL",
"components.Settings.SettingsMain.youtubeUrlTip": "Base URL for YouTube videos if a self-hosted YouTube instance is used.",
"components.Settings.SettingsNetwork.csrfProtection": "Enable CSRF Protection",
"components.Settings.SettingsNetwork.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!",
"components.Settings.SettingsNetwork.csrfProtectionTip": "Set external API access to read-only (requires HTTPS)",
"components.Settings.SettingsNetwork.docs": "documentation",
"components.Settings.SettingsNetwork.forceIpv4First": "Force IPv4 Resolution First",
"components.Settings.SettingsNetwork.forceIpv4FirstTip": "Force Jellyseerr to resolve IPv4 addresses first instead of IPv6",
"components.Settings.SettingsNetwork.network": "Network",
"components.Settings.SettingsNetwork.networkDisclaimer": "Network parameters from your container/system should be used instead of these settings. See the {docs} for more information.",
"components.Settings.SettingsNetwork.networksettings": "Network Settings",
@@ -1213,7 +1216,7 @@
"components.Setup.librarieserror": "Validation failed. Please toggle the libraries again to continue.",
"components.Setup.servertype": "Choose Server Type",
"components.Setup.setup": "Setup",
"components.Setup.signin": "Sign in to your account",
"components.Setup.signin": "Sign In",
"components.Setup.signinMessage": "Get started by signing in",
"components.Setup.signinWithEmby": "Enter your Emby details",
"components.Setup.signinWithJellyfin": "Enter your Jellyfin details",