diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json index 159b1a62..e30dde86 100644 --- a/cypress/config/settings.cypress.json +++ b/cypress/config/settings.cypress.json @@ -195,6 +195,10 @@ "bypassFilter": "", "bypassLocalAddresses": true }, - "dnsCache": true + "dnsCache": { + "enabled": false, + "forceMinTtl": 0, + "forceMaxTtl": -1 + } } } diff --git a/jellyseerr-api.yml b/jellyseerr-api.yml index 6aecfa58..4254ba43 100644 --- a/jellyseerr-api.yml +++ b/jellyseerr-api.yml @@ -260,9 +260,51 @@ components: csrfProtection: type: boolean example: false + forceIpv4First: + type: boolean + example: false trustProxy: type: boolean - example: true + example: false + proxy: + type: object + properties: + enabled: + type: boolean + example: false + hostname: + type: string + example: '' + port: + type: number + example: 8080 + useSsl: + type: boolean + example: false + user: + type: string + example: '' + password: + type: string + example: '' + bypassFilter: + type: string + example: '' + bypassLocalAddresses: + type: boolean + example: true + dnsCache: + type: object + properties: + enabled: + type: boolean + example: false + forceMinTtl: + type: number + example: 0 + forceMaxTtl: + type: number + example: -1 PlexLibrary: type: object properties: diff --git a/server/index.ts b/server/index.ts index 258672ae..24b7f225 100644 --- a/server/index.ts +++ b/server/index.ts @@ -25,7 +25,7 @@ import imageproxy from '@server/routes/imageproxy'; import { appDataPermissions } from '@server/utils/appDataVolume'; import { getAppVersion } from '@server/utils/appVersion'; import createCustomProxyAgent from '@server/utils/customProxyAgent'; -import dnsCache from '@server/utils/dnsCache'; +import { initializeDnsCache } from '@server/utils/dnsCache'; import restartFlag from '@server/utils/restartFlag'; import { getClientIp } from '@supercharge/request-ip'; import axios from 'axios'; @@ -83,7 +83,10 @@ app // Add DNS caching if (settings.network.dnsCache) { - dnsCache.initialize(); + initializeDnsCache({ + forceMinTtl: settings.network.dnsCache.forceMinTtl, + forceMaxTtl: settings.network.dnsCache.forceMaxTtl, + }); } // Register HTTP proxy diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 4b14a8da..52272c74 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -100,17 +100,6 @@ interface Quota { quotaDays?: number; } -export interface ProxySettings { - enabled: boolean; - hostname: string; - port: number; - useSsl: boolean; - user: string; - password: string; - bypassFilter: string; - bypassLocalAddresses: boolean; -} - export interface MainSettings { apiKey: string; applicationTitle: string; @@ -138,12 +127,29 @@ export interface MainSettings { youtubeUrl: string; } +export interface ProxySettings { + enabled: boolean; + hostname: string; + port: number; + useSsl: boolean; + user: string; + password: string; + bypassFilter: string; + bypassLocalAddresses: boolean; +} + +export interface DnsCacheSettings { + enabled: boolean; + forceMinTtl?: number; + forceMaxTtl?: number; +} + export interface NetworkSettings { csrfProtection: boolean; forceIpv4First: boolean; trustProxy: boolean; proxy: ProxySettings; - dnsCache: boolean; + dnsCache: DnsCacheSettings; } interface PublicSettings { @@ -543,7 +549,11 @@ class Settings { bypassFilter: '', bypassLocalAddresses: true, }, - dnsCache: false, + dnsCache: { + enabled: false, + forceMinTtl: 0, + forceMaxTtl: -1, + }, }, }; if (initialSettings) { diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index b2dbd1d2..db31b937 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -756,8 +756,8 @@ settingsRoutes.get('/cache', async (_req, res) => { const tmdbImageCache = await ImageProxy.getImageStats('tmdb'); const avatarImageCache = await ImageProxy.getImageStats('avatar'); - const stats = dnsCache.getStats(); - const entries = dnsCache.getCacheEntries(); + const stats = dnsCache?.getStats(); + const entries = dnsCache?.getCacheEntries(); return res.status(200).json({ apiCaches, diff --git a/server/utils/dnsCache.ts b/server/utils/dnsCache.ts index fb87c284..253b9291 100644 --- a/server/utils/dnsCache.ts +++ b/server/utils/dnsCache.ts @@ -1,9 +1,28 @@ import logger from '@server/logger'; import { DnsCacheManager } from 'dns-caching'; -const dnsCache = new DnsCacheManager({ - logger: logger, - forceMaxTtl: Number(process.env.DNS_CACHE_FORCE_MAX_TTL) || -1, - forceMinTtl: Number(process.env.DNS_CACHE_FORCE_MIN_TTL) || 0, -}); +let dnsCache: DnsCacheManager | undefined; + +export function initializeDnsCache({ + forceMinTtl, + forceMaxTtl, +}: { + forceMinTtl?: number; + forceMaxTtl?: number; +}) { + if (dnsCache) { + logger.warn('DNS Cache is already initialized', { label: 'DNS Cache' }); + return; + } + + logger.info('Initializing DNS Cache', { label: 'DNS Cache' }); + + dnsCache = new DnsCacheManager({ + logger, + forceMinTtl: typeof forceMinTtl === 'number' ? forceMinTtl * 1000 : 0, + forceMaxTtl: typeof forceMaxTtl === 'number' ? forceMaxTtl * 1000 : -1, + }); + dnsCache.initialize(); +} + export default dnsCache; diff --git a/src/components/Settings/SettingsNetwork/index.tsx b/src/components/Settings/SettingsNetwork/index.tsx index ac3d1bd9..f0ac1f13 100644 --- a/src/components/Settings/SettingsNetwork/index.tsx +++ b/src/components/Settings/SettingsNetwork/index.tsx @@ -50,6 +50,8 @@ const messages = defineMessages('components.Settings.SettingsNetwork', { 'Enable caching of DNS lookups to optimize performance and avoid making unnecessary API calls', dnsCacheHoverTip: 'Do NOT enable this if you are experiencing issues with DNS lookups', + dnsCacheForceMinTtl: 'DNS Cache Minimum TTL', + dnsCacheForceMaxTtl: 'DNS Cache Maximum TTL', }); const SettingsNetwork = () => { @@ -95,7 +97,9 @@ const SettingsNetwork = () => { initialValues={{ csrfProtection: data?.csrfProtection, forceIpv4First: data?.forceIpv4First, - dnsCache: data?.dnsCache, + dnsCacheEnabled: data?.dnsCache.enabled, + dnsCacheForceMinTtl: data?.dnsCache.forceMinTtl, + dnsCacheForceMaxTtl: data?.dnsCache.forceMaxTtl, trustProxy: data?.trustProxy, proxyEnabled: data?.proxy?.enabled, proxyHostname: data?.proxy?.hostname, @@ -114,7 +118,11 @@ const SettingsNetwork = () => { csrfProtection: values.csrfProtection, forceIpv4First: values.forceIpv4First, trustProxy: values.trustProxy, - dnsCache: values.dnsCache, + dnsCache: { + enabled: values.dnsCacheEnabled, + forceMinTtl: values.dnsCacheForceMinTtl, + forceMaxTtl: values.dnsCacheForceMaxTtl, + }, proxy: { enabled: values.proxyEnabled, hostname: values.proxyHostname, @@ -229,7 +237,7 @@ const SettingsNetwork = () => {
-
+ {values.dnsCacheEnabled && ( + <> +
+
+ +
+
+ +
+ {errors.dnsCacheForceMinTtl && + touched.dnsCacheForceMinTtl && + typeof errors.dnsCacheForceMinTtl === 'string' && ( +
+ {errors.dnsCacheForceMinTtl} +
+ )} +
+
+
+ +
+
+ +
+ {errors.dnsCacheForceMaxTtl && + touched.dnsCacheForceMaxTtl && + typeof errors.dnsCacheForceMaxTtl === 'string' && ( +
+ {errors.dnsCacheForceMaxTtl} +
+ )} +
+
+
+ + )}