fix: add force min/max TTL in network settings

This commit is contained in:
gauthier-th
2025-08-19 13:55:08 +02:00
parent f958b11d51
commit aeb8023027
7 changed files with 173 additions and 30 deletions

View File

@@ -195,6 +195,10 @@
"bypassFilter": "",
"bypassLocalAddresses": true
},
"dnsCache": true
"dnsCache": {
"enabled": false,
"forceMinTtl": 0,
"forceMaxTtl": -1
}
}
}

View File

@@ -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:

View File

@@ -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

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 = () => {
</div>
</div>
<div className="form-row">
<label htmlFor="dnsCache" className="checkbox-label">
<label htmlFor="dnsCacheEnabled" className="checkbox-label">
<span className="mr-2">
{intl.formatMessage(messages.dnsCache)}
</span>
@@ -246,15 +254,72 @@ const SettingsNetwork = () => {
>
<Field
type="checkbox"
id="dnsCache"
name="dnsCache"
id="dnsCacheEnabled"
name="dnsCacheEnabled"
onChange={() => {
setFieldValue('dnsCache', !values.dnsCache);
setFieldValue(
'dnsCacheEnabled',
!values.dnsCacheEnabled
);
}}
/>
</Tooltip>
</div>
</div>
{values.dnsCacheEnabled && (
<>
<div className="mr-2 ml-4">
<div className="form-row">
<label
htmlFor="dnsCacheForceMinTtl"
className="checkbox-label"
>
{intl.formatMessage(messages.dnsCacheForceMinTtl)}
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field
id="dnsCacheForceMinTtl"
name="dnsCacheForceMinTtl"
type="text"
/>
</div>
{errors.dnsCacheForceMinTtl &&
touched.dnsCacheForceMinTtl &&
typeof errors.dnsCacheForceMinTtl === 'string' && (
<div className="error">
{errors.dnsCacheForceMinTtl}
</div>
)}
</div>
</div>
<div className="form-row">
<label
htmlFor="dnsCacheForceMaxTtl"
className="checkbox-label"
>
{intl.formatMessage(messages.dnsCacheForceMaxTtl)}
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field
id="dnsCacheForceMaxTtl"
name="dnsCacheForceMaxTtl"
type="text"
/>
</div>
{errors.dnsCacheForceMaxTtl &&
touched.dnsCacheForceMaxTtl &&
typeof errors.dnsCacheForceMaxTtl === 'string' && (
<div className="error">
{errors.dnsCacheForceMaxTtl}
</div>
)}
</div>
</div>
</div>
</>
)}
<div className="form-row">
<label htmlFor="proxyEnabled" className="checkbox-label">
<span className="mr-2">