From e7037ddfe9cef56260d2072d59e324186c035b62 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Thu, 25 Jun 2026 12:37:25 +0200 Subject: [PATCH] chore: remove dead code leftovers from caching rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove dead Redis exports (exampleChannel, homeAssistantEntityState, queueChannel) - Remove dead Beszel subscribeRealtimeMetrics/subscribeTopic (~100 lines) - Add missing onSettled invalidation to downloads queue + indexer-manager mutations - Rename invalidate-update-checker-cache → warm-update-checker - Delete empty cron-jobs/integrations directory - Fix stale comment and unused type imports --- apps/tasks/src/on-start/index.ts | 4 +- .../invalidate-update-checker-cache.ts | 18 --- .../tasks/src/on-start/warm-update-checker.ts | 14 +++ .../src/beszel/beszel-integration.ts | 107 ------------------ packages/redis/src/index.ts | 14 +-- packages/widgets/src/downloads/component.tsx | 8 +- .../widgets/src/indexer-manager/component.tsx | 5 +- 7 files changed, 26 insertions(+), 144 deletions(-) delete mode 100644 apps/tasks/src/on-start/invalidate-update-checker-cache.ts create mode 100644 apps/tasks/src/on-start/warm-update-checker.ts diff --git a/apps/tasks/src/on-start/index.ts b/apps/tasks/src/on-start/index.ts index cb9564d1b..cc6967dce 100644 --- a/apps/tasks/src/on-start/index.ts +++ b/apps/tasks/src/on-start/index.ts @@ -1,7 +1,7 @@ -import { invalidateUpdateCheckerCacheAsync } from "./invalidate-update-checker-cache"; import { cleanupSessionsAsync } from "./session-cleanup"; +import { warmUpdateCheckerAsync } from "./warm-update-checker"; export async function onStartAsync() { await cleanupSessionsAsync(); - await invalidateUpdateCheckerCacheAsync(); + await warmUpdateCheckerAsync(); } diff --git a/apps/tasks/src/on-start/invalidate-update-checker-cache.ts b/apps/tasks/src/on-start/invalidate-update-checker-cache.ts deleted file mode 100644 index de1be2a81..000000000 --- a/apps/tasks/src/on-start/invalidate-update-checker-cache.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createLogger } from "@homarr/core/infrastructure/logs"; -import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker"; - -const logger = createLogger({ module: "invalidateUpdateCheckerCache" }); - -/** - * Invalidates the update checker cache on startup to ensure fresh data. - * It is important as we want to avoid showing pending updates after the update to latest version. - */ -export async function invalidateUpdateCheckerCacheAsync() { - try { - const handler = updateCheckerRequestHandler.handler({}); - await handler.getDataAsync(); - logger.debug("Update checker cache refreshed"); - } catch (error) { - logger.error(new Error("Failed to invalidate update checker cache", { cause: error })); - } -} diff --git a/apps/tasks/src/on-start/warm-update-checker.ts b/apps/tasks/src/on-start/warm-update-checker.ts new file mode 100644 index 000000000..a3b9f94a9 --- /dev/null +++ b/apps/tasks/src/on-start/warm-update-checker.ts @@ -0,0 +1,14 @@ +import { createLogger } from "@homarr/core/infrastructure/logs"; +import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker"; + +const logger = createLogger({ module: "warmUpdateChecker" }); + +export async function warmUpdateCheckerAsync() { + try { + const handler = updateCheckerRequestHandler.handler({}); + await handler.getDataAsync(); + logger.debug("Update checker warmed"); + } catch (error) { + logger.error(new Error("Failed to warm update checker", { cause: error })); + } +} diff --git a/packages/integrations/src/beszel/beszel-integration.ts b/packages/integrations/src/beszel/beszel-integration.ts index bf6f3640c..186ed2203 100644 --- a/packages/integrations/src/beszel/beszel-integration.ts +++ b/packages/integrations/src/beszel/beszel-integration.ts @@ -13,13 +13,11 @@ import type { BeszelAlertHistory, BeszelAuthResponse, BeszelContainer, - BeszelContainerStats, BeszelContainerStatsRecord, BeszelSmartDevice, BeszelSystem, BeszelSystemDetails, BeszelSystemdService, - BeszelSystemStats, BeszelSystemStatsRecord, CreateAlertInput, PocketBaseListResponse, @@ -286,111 +284,6 @@ export class BeszelIntegration extends Integration { }); } - public async subscribeRealtimeMetrics( - systemId: string, - onMessage: (data: { stats: BeszelSystemStatsRecord; containerStats: BeszelContainerStatsRecord | null }) => void, - signal: AbortSignal, - ): Promise { - const session = await this.authenticateAsync(); - const sseUrl = this.url("/api/realtime"); - logger.debug("Opening Beszel SSE connection", { integrationId: this.integration.id, systemId }); - - const sseResponse = await fetchWithTrustedCertificatesAsync(sseUrl, { - headers: { Authorization: session.token }, - signal, - }); - - if (!sseResponse.ok || !sseResponse.body) { - logger.warn("Beszel SSE connection failed", { - integrationId: this.integration.id, - systemId, - status: sseResponse.status, - }); - throw new ResponseError(sseResponse); - } - logger.debug("Beszel SSE connection opened", { integrationId: this.integration.id, systemId }); - - const reader = sseResponse.body.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; - let clientId: string | null = null; - - const processLine = (line: string) => { - if (!line.startsWith("data:")) return; - const jsonStr = line.slice(5).trim(); - if (!jsonStr) return; - - try { - const parsed = JSON.parse(jsonStr) as Record; - - if (!clientId && typeof parsed.clientId === "string") { - clientId = parsed.clientId; - this.subscribeTopic(clientId, systemId, session.token).catch((err) => { - logger.warn("Failed to subscribe to rt_metrics topic, aborting SSE", { error: err }); - reader.cancel().catch(() => {}); - }); - return; - } - - if ("stats" in parsed) { - const now = new Date().toISOString(); - const envelope = { id: "", system: systemId, type: "1m" as const, created: now, updated: now }; - const stats: BeszelSystemStatsRecord = { ...envelope, stats: parsed.stats as BeszelSystemStats }; - - let containerStats: BeszelContainerStatsRecord | null = null; - const hasContainers = - parsed.container && - Array.isArray(parsed.container) && - (parsed.container as BeszelContainerStats[]).length > 0; - if (hasContainers) { - containerStats = { ...envelope, stats: parsed.container as BeszelContainerStats[] }; - } - onMessage({ stats, containerStats }); - } - } catch { - logger.debug("Failed to parse SSE message", { line }); - } - }; - - try { - while (!signal.aborted) { - const { done, value } = await reader.read(); - if (done) break; - - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split("\n"); - buffer = lines.pop() ?? ""; - for (const line of lines) { - processLine(line); - } - } - if (!signal.aborted) { - logger.warn("Beszel SSE stream ended unexpectedly", { - integrationId: this.integration.id, - systemId, - }); - throw new Error("Beszel SSE stream ended unexpectedly"); - } - } finally { - reader.cancel().catch(() => {}); - } - } - - private async subscribeTopic(clientId: string, systemId: string, token: string): Promise { - const topic = `rt_metrics?options=${JSON.stringify({ query: { system: systemId } })}`; - const response = await fetchWithTrustedCertificatesAsync(this.url("/api/realtime"), { - method: "POST", - headers: { - Authorization: token, - "Content-Type": "application/json", - }, - body: JSON.stringify({ clientId, subscriptions: [topic] }), - }); - if (!response.ok) { - throw new ResponseError(response); - } - } - protected async testingAsync(input: IntegrationTestingInput): Promise { const response = await input.fetchAsync(this.url("/api/collections/users/auth-with-password"), { method: "POST", diff --git a/packages/redis/src/index.ts b/packages/redis/src/index.ts index 830335fd8..02258a2e0 100644 --- a/packages/redis/src/index.ts +++ b/packages/redis/src/index.ts @@ -1,6 +1,6 @@ import type { LogLevel } from "@homarr/core/infrastructure/logs/constants"; -import { createListChannel, createQueueChannel, createSubPubChannel } from "./lib/channel"; +import { createListChannel, createSubPubChannel } from "./lib/channel"; export { handshakeAsync, @@ -12,23 +12,11 @@ export { invalidateIntegrationCacheAsync, } from "./lib/channel"; -export const exampleChannel = createSubPubChannel<{ message: string }>("example"); export const pingChannel = createSubPubChannel< { url: string; statusCode: number; durationMs: number } | { url: string; error: string } >("ping"); export const pingUrlChannel = createListChannel("ping-url"); -export const homeAssistantEntityState = createSubPubChannel<{ - entityId: string; - state: string; -}>("home-assistant/entity-state"); - -export const queueChannel = createQueueChannel<{ - name: string; - executionDate: Date; - data: unknown; -}>("common-queue"); - export interface LoggerMessage { message: string; level: LogLevel; diff --git a/packages/widgets/src/downloads/component.tsx b/packages/widgets/src/downloads/component.tsx index f1ab6e74c..e020daa62 100644 --- a/packages/widgets/src/downloads/component.tsx +++ b/packages/widgets/src/downloads/component.tsx @@ -117,7 +117,7 @@ export default function DownloadClientsWidget({ const { mutate: mutatePauseItem } = clientApi.widget.downloads.pauseItem.useMutation(invalidateDownloads); const { mutate: mutateDeleteItem } = clientApi.widget.downloads.deleteItem.useMutation(invalidateDownloads); - //Flatten Data array for which each element has it's integration, data (base + calculated) and actions. Memoized on data subscription + //Flatten Data array for which each element has its integration, data (base + calculated) and actions const data = useMemo( () => currentItems @@ -748,8 +748,10 @@ const ClientsControl = ({ clients, filters, setFilters, availableStatuses }: Cli "/s", ); - const { mutate: mutateResumeQueue } = clientApi.widget.downloads.resume.useMutation(); - const { mutate: mutatePauseQueue } = clientApi.widget.downloads.pause.useMutation(); + const utils = clientApi.useUtils(); + const invalidateDownloads = { onSettled: () => void utils.widget.downloads.getJobsAndStatuses.invalidate() }; + const { mutate: mutateResumeQueue } = clientApi.widget.downloads.resume.useMutation(invalidateDownloads); + const { mutate: mutatePauseQueue } = clientApi.widget.downloads.pause.useMutation(invalidateDownloads); const [opened, { open, close }] = useDisclosure(false); const t = useScopedI18n("widget.downloads"); return ( diff --git a/packages/widgets/src/indexer-manager/component.tsx b/packages/widgets/src/indexer-manager/component.tsx index e579b7319..4e3fb7c7e 100644 --- a/packages/widgets/src/indexer-manager/component.tsx +++ b/packages/widgets/src/indexer-manager/component.tsx @@ -20,7 +20,10 @@ export default function IndexerManagerWidget({ const t = useI18n(); const { data: indexersData = [] } = clientApi.widget.indexerManager.getIndexersStatus.useQuery({ integrationIds }); - const { mutate: testAll, isPending } = clientApi.widget.indexerManager.testAllIndexers.useMutation(); + const utils = clientApi.useUtils(); + const { mutate: testAll, isPending } = clientApi.widget.indexerManager.testAllIndexers.useMutation({ + onSettled: () => void utils.widget.indexerManager.getIndexersStatus.invalidate(), + }); const board = useRequiredBoard(); const hasSmallWidth = width < 256; const hasSmallHeight = height < 256;