From d13effdda04169b9d7d4aedab0134071bedec5ef Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 15 Jun 2026 15:15:06 +0200 Subject: [PATCH] fix(beszel): address review findings for live streaming - Skip toReversed() for live data (already chronological from buffer) - Reset live buffer on systemId/integration change to avoid stale data - Throw on unexpected SSE stream end so subscription emits error - Delete unused time-period.ts config (duplicate of server-side config) --- .../src/beszel/beszel-integration.ts | 3 +++ packages/widgets/src/beszel/_shared/chart.tsx | 6 ++++-- .../widgets/src/beszel/_shared/time-period.ts | 19 ------------------- .../src/beszel/_shared/use-live-stats.ts | 14 ++++++++++++-- 4 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 packages/widgets/src/beszel/_shared/time-period.ts diff --git a/packages/integrations/src/beszel/beszel-integration.ts b/packages/integrations/src/beszel/beszel-integration.ts index d0766b7fe..9066b9cad 100644 --- a/packages/integrations/src/beszel/beszel-integration.ts +++ b/packages/integrations/src/beszel/beszel-integration.ts @@ -329,6 +329,9 @@ export class BeszelIntegration extends Integration { processLine(line); } } + if (!signal.aborted) { + throw new Error("Beszel SSE stream ended unexpectedly"); + } } finally { reader.cancel().catch(() => {}); } diff --git a/packages/widgets/src/beszel/_shared/chart.tsx b/packages/widgets/src/beszel/_shared/chart.tsx index 02c3c728d..08112d2f8 100644 --- a/packages/widgets/src/beszel/_shared/chart.tsx +++ b/packages/widgets/src/beszel/_shared/chart.tsx @@ -83,7 +83,8 @@ export const useSystemChartData = ( useMemo(() => { if (!systemStats) return []; const fmt = live ? formatTimeLive : formatTime; - return [...systemStats].toReversed().map((r) => ({ + const ordered = live ? systemStats : [...systemStats].toReversed(); + return ordered.map((r) => ({ time: fmt(r.created), ...mapFn(r.stats), })); @@ -123,7 +124,8 @@ export const useDockerChartData = ( const extract = defaultContainerExtractors[metric]; if (!extract) return []; const fmt = live ? formatTimeLive : formatTime; - return [...containerStats].toReversed().map((record) => { + const ordered = live ? containerStats : [...containerStats].toReversed(); + return ordered.map((record) => { const point: Record = { time: fmt(record.created) }; for (const name of containerNames) { point[name] = extract(record.stats.find((c) => c.n === name)); diff --git a/packages/widgets/src/beszel/_shared/time-period.ts b/packages/widgets/src/beszel/_shared/time-period.ts deleted file mode 100644 index 83ecba855..000000000 --- a/packages/widgets/src/beszel/_shared/time-period.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const beszelTimePeriods = ["1m", "1h", "12h", "24h", "1w", "30d"] as const; -export type BeszelTimePeriod = (typeof beszelTimePeriods)[number]; - -export const beszelTimePeriodConfig: Record< - BeszelTimePeriod, - { - beszelStatType: string; - perPage: number; - refetchMs: number; - serverCacheSeconds: number; - } -> = { - "1m": { beszelStatType: "1m", perPage: 60, refetchMs: 0, serverCacheSeconds: 5 }, - "1h": { beszelStatType: "1m", perPage: 60, refetchMs: 60_000, serverCacheSeconds: 60 }, - "12h": { beszelStatType: "10m", perPage: 72, refetchMs: 300_000, serverCacheSeconds: 300 }, - "24h": { beszelStatType: "20m", perPage: 72, refetchMs: 600_000, serverCacheSeconds: 600 }, - "1w": { beszelStatType: "120m", perPage: 84, refetchMs: 1_800_000, serverCacheSeconds: 1800 }, - "30d": { beszelStatType: "480m", perPage: 90, refetchMs: 3_600_000, serverCacheSeconds: 3600 }, -}; diff --git a/packages/widgets/src/beszel/_shared/use-live-stats.ts b/packages/widgets/src/beszel/_shared/use-live-stats.ts index 6dab58e96..639c69ce5 100644 --- a/packages/widgets/src/beszel/_shared/use-live-stats.ts +++ b/packages/widgets/src/beszel/_shared/use-live-stats.ts @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { clientApi } from "@homarr/api/client"; import type { BeszelContainerStatsRecord, BeszelSystemStatsRecord } from "@homarr/integrations/types"; @@ -12,10 +12,20 @@ interface LiveStatsData { containerStats: BeszelContainerStatsRecord[]; } -export const useLiveStats = (integrationIds: string[], systemId: string, includeDocker: boolean, enabled: boolean) => { +export const useLiveStats = ( + integrationIds: string[], + systemId: string, + includeDocker: boolean, + enabled: boolean, +) => { const bufferRef = useRef({ systemStats: [], containerStats: [] }); const [data, setData] = useState(null); + useEffect(() => { + bufferRef.current = { systemStats: [], containerStats: [] }; + setData(null); + }, [systemId, enabled, ...integrationIds]); + const onData = useCallback( (incoming: { stats: BeszelSystemStatsRecord; containerStats: BeszelContainerStatsRecord | null }) => { const buf = bufferRef.current;