import Button from '@app/components/Common/Button'; import LoadingSpinner from '@app/components/Common/LoadingSpinner'; import PageTitle from '@app/components/Common/PageTitle'; import Tooltip from '@app/components/Common/Tooltip'; import SettingsBadge from '@app/components/Settings/SettingsBadge'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline'; import type { NetworkSettings } from '@server/lib/settings'; import { Field, Form, Formik } from 'formik'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR, { mutate } from 'swr'; import * as Yup from 'yup'; const messages = defineMessages('components.Settings.SettingsNetwork', { toastSettingsSuccess: 'Settings saved successfully!', toastSettingsFailure: 'Something went wrong while saving settings.', network: 'Network', networksettings: 'Network Settings', networksettingsDescription: 'Configure network settings for your Jellyseerr instance.', csrfProtection: 'Enable CSRF Protection', csrfProtectionTip: 'Set external API access to read-only (requires HTTPS)', csrfProtectionHoverTip: 'Do NOT enable this setting unless you understand what you are doing!', trustProxy: 'Enable Proxy Support', trustProxyTip: 'Allow Jellyseerr to correctly register client IP addresses behind a proxy', proxyEnabled: 'HTTP(S) Proxy', proxyHostname: 'Proxy Hostname', proxyPort: 'Proxy Port', proxySsl: 'Use SSL For Proxy', proxyUser: 'Proxy Username', proxyPassword: 'Proxy Password', proxyBypassFilter: 'Proxy Ignored Addresses', proxyBypassFilterTip: "Use ',' as a separator, and '*.' as a wildcard for subdomains", proxyBypassLocalAddresses: 'Bypass Proxy for Local Addresses', validationProxyPort: 'You must provide a valid port', advancedNetworkSettings: 'Advanced Network Settings', 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 = () => { const { addToast } = useToasts(); const intl = useIntl(); const { data, error, mutate: revalidate, } = useSWR('/api/v1/settings/network'); const NetworkSettingsSchema = Yup.object().shape({ proxyPort: Yup.number().when('proxyEnabled', { is: (proxyEnabled: boolean) => proxyEnabled, then: Yup.number().required( intl.formatMessage(messages.validationProxyPort) ), }), }); if (!data && !error) { return ; } return ( <>

{intl.formatMessage(messages.networksettings)}

{intl.formatMessage(messages.networksettingsDescription)}

{ try { const res = await fetch('/api/v1/settings/network', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ csrfProtection: values.csrfProtection, forceIpv4First: values.forceIpv4First, trustProxy: values.trustProxy, proxy: { enabled: values.proxyEnabled, hostname: values.proxyHostname, port: values.proxyPort, useSsl: values.proxySsl, user: values.proxyUser, password: values.proxyPassword, bypassFilter: values.proxyBypassFilter, bypassLocalAddresses: values.proxyBypassLocalAddresses, }, }), }); if (!res.ok) throw new Error(); mutate('/api/v1/settings/public'); mutate('/api/v1/status'); addToast(intl.formatMessage(messages.toastSettingsSuccess), { autoDismiss: true, appearance: 'success', }); } catch (e) { addToast(intl.formatMessage(messages.toastSettingsFailure), { autoDismiss: true, appearance: 'error', }); } finally { revalidate(); } }} > {({ errors, touched, isSubmitting, isValid, values, setFieldValue, }) => { return (
{ setFieldValue('trustProxy', !values.trustProxy); }} />
{ setFieldValue( 'csrfProtection', !values.csrfProtection ); }} />
{ setFieldValue('proxyEnabled', !values.proxyEnabled); }} />
{values.proxyEnabled && ( <>
{errors.proxyHostname && touched.proxyHostname && typeof errors.proxyHostname === 'string' && (
{errors.proxyHostname}
)}
{errors.proxyPort && touched.proxyPort && typeof errors.proxyPort === 'string' && (
{errors.proxyPort}
)}
{ setFieldValue('proxySsl', !values.proxySsl); }} />
{errors.proxyUser && touched.proxyUser && typeof errors.proxyUser === 'string' && (
{errors.proxyUser}
)}
{errors.proxyPassword && touched.proxyPassword && typeof errors.proxyPassword === 'string' && (
{errors.proxyPassword}
)}
{errors.proxyBypassFilter && touched.proxyBypassFilter && typeof errors.proxyBypassFilter === 'string' && (
{errors.proxyBypassFilter}
)}
{ setFieldValue( 'proxyBypassLocalAddresses', !values.proxyBypassLocalAddresses ); }} />
)}

{intl.formatMessage(messages.advancedNetworkSettings)}

{intl.formatMessage(messages.networkDisclaimer, { docs: ( {intl.formatMessage(messages.docs)} ), })}

{ setFieldValue('forceIpv4First', !values.forceIpv4First); }} />
); }}
); }; export default SettingsNetwork;