feat(dnscache): dns cache entries are now flushable

This commit is contained in:
fallenbagel
2025-02-23 04:18:11 +08:00
committed by gauthier-th
parent 18e935d0bb
commit bb47dc6c02
5 changed files with 78 additions and 9 deletions

View File

@@ -3062,6 +3062,21 @@ paths:
responses:
'204':
description: 'Flushed cache'
/settings/cache/dns/{dnsEntry}/flush:
post:
summary: Flush a specific DNS cache entry
description: Flushes a specific DNS cache entry
tags:
- settings
parameters:
- in: path
name: dnsEntry
required: true
schema:
type: string
responses:
'204':
description: 'Flushed dns cache'
/settings/logs:
get:
summary: Returns logs

View File

@@ -786,6 +786,20 @@ settingsRoutes.post<{ cacheId: AvailableCacheIds }>(
}
);
settingsRoutes.post<{ dnsEntry: string }>(
'/cache/dns/:dnsEntry/flush',
(req, res, next) => {
const dnsEntry = req.params.dnsEntry;
if (dnsCache) {
dnsCache.clear(dnsEntry);
return res.status(204).send();
}
next({ status: 404, message: 'Cache not found.' });
}
);
settingsRoutes.post(
'/initialize',
isAuthenticated(Permission.ADMIN),

View File

@@ -895,7 +895,25 @@ class DnsCacheManager {
};
}
clear() {
clearHostname(hostname: string): void {
if (!hostname || hostname.length === 0) {
return;
}
if (this.cache.has(hostname)) {
this.cache.delete(hostname);
logger.debug(`Cleared DNS cache entry for ${hostname}`, {
label: 'DNSCache',
});
}
}
clear(hostname?: string): void {
if (hostname && hostname.length > 0) {
this.clearHostname(hostname);
return;
}
this.cache.clear();
this.stats.hits = 0;
this.stats.misses = 0;

View File

@@ -54,16 +54,18 @@ const messages: { [messageName: string]: MessageDescriptor } = defineMessages(
cachekeys: 'Total Keys',
cacheksize: 'Key Size',
cachevsize: 'Value Size',
flushcache: 'Flush Cache',
dnsCache: 'DNS Cache',
dnsCacheDescription:
'Jellyseerr caches DNS lookups to optimize performance and avoid making unnecessary API calls.',
dnsCacheFlushed: '{hostname} dns cache flushed.',
dnscachename: 'Hostname',
dnscacheactiveaddress: 'Active Address',
dnscachehits: 'Hits',
dnscachemisses: 'Misses',
dnscacheage: 'Age',
dnscachenetworkerrors: 'Network Errors',
flushcache: 'Flush Cache',
flushdnscache: 'Flush DNS Cache',
unknownJob: 'Unknown Job',
'plex-recently-added-scan': 'Plex Recently Added Scan',
'plex-full-scan': 'Plex Full Library Scan',
@@ -252,6 +254,18 @@ const SettingsJobs = () => {
cacheRevalidate();
};
const flushDnsCache = async (hostname: string) => {
const res = await fetch(`/api/v1/settings/cache/dns/${hostname}/flush`, {
method: 'POST',
});
if (!res.ok) throw new Error();
addToast(intl.formatMessage(messages.dnscacheflushed, { hostname }), {
appearance: 'success',
autoDismiss: true,
});
cacheRevalidate();
};
const scheduleJob = async () => {
const jobScheduleCron = ['0', '0', '*', '*', '*', '*'];
@@ -295,6 +309,13 @@ const SettingsJobs = () => {
}
};
const formatAge = (milliseconds: number): string => {
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}m ${remainingSeconds}s`;
};
return (
<>
<PageTitle
@@ -597,6 +618,7 @@ const SettingsJobs = () => {
<Table.TH>
{intl.formatMessage(messages.dnscachenetworkerrors)}
</Table.TH>
<Table.TH></Table.TH>
</tr>
</thead>
<Table.TBody>
@@ -611,19 +633,17 @@ const SettingsJobs = () => {
<Table.TD>
{intl.formatNumber(cacheData?.dnsCache.stats.misses ?? 0)}
</Table.TD>
<Table.TD>
{intl.formatNumber(Math.floor(data.age / 1000))}s
</Table.TD>
<Table.TD>{formatAge(data.age)}</Table.TD>
<Table.TD>{intl.formatNumber(data.networkErrors)}</Table.TD>
{/* <Table.TD alignText="right">
<Table.TD alignText="right">
<Button
buttonType="danger"
onClick={() => flushCache(cache)}
onClick={() => flushDnsCache(hostname)}
>
<TrashIcon />
<span>{intl.formatMessage(messages.flushcache)}</span>
<span>{intl.formatMessage(messages.flushdnscache)}</span>
</Button>
</Table.TD> */}
</Table.TD>
</tr>
)
)}

View File

@@ -875,6 +875,7 @@
"components.Settings.SettingsJobsCache.command": "Command",
"components.Settings.SettingsJobsCache.dnsCache": "DNS Cache",
"components.Settings.SettingsJobsCache.dnsCacheDescription": "Jellyseerr caches DNS lookups to optimize performance and avoid making unnecessary API calls.",
"components.Settings.SettingsJobsCache.dnsCacheFlushed": "{hostname} dns cache flushed.",
"components.Settings.SettingsJobsCache.dnscacheactiveaddress": "Active Address",
"components.Settings.SettingsJobsCache.dnscacheage": "Age",
"components.Settings.SettingsJobsCache.dnscachehits": "Hits",
@@ -891,6 +892,7 @@
"components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Every {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}",
"components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Every {jobScheduleSeconds, plural, one {second} other {{jobScheduleSeconds} seconds}}",
"components.Settings.SettingsJobsCache.flushcache": "Flush Cache",
"components.Settings.SettingsJobsCache.flushdnscache": "Flush DNS Cache",
"components.Settings.SettingsJobsCache.image-cache-cleanup": "Image Cache Cleanup",
"components.Settings.SettingsJobsCache.imagecache": "Image Cache",
"components.Settings.SettingsJobsCache.imagecacheDescription": "When enabled in settings, Jellyseerr will proxy and cache images from pre-configured external sources. Cached images are saved into your config folder. You can find the files in <code>{appDataPath}/cache/images</code>.",