From a0e4b2c45d00750a8c1e6b7bcc02fa6028c39121 Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:00:22 +0000 Subject: [PATCH 1/2] feat(frontend): live area stack chart for aggregate stats --- .../lib/components/LiveAreaStackChart.svelte | 89 +++++++++++++++++++ .../src/lib/remotes/clusters.remote.ts | 9 +- .../src/routes/(app)/stats/+layout.svelte | 5 +- .../src/routes/(app)/stats/+page.svelte | 71 +++------------ .../routes/(app)/stats/components/CPU.svelte | 45 ++++++++++ .../components/LiveAreaStackChart.svelte | 36 ++++++++ .../(app)/stats/components/Memory.svelte | 45 ++++++++++ 7 files changed, 237 insertions(+), 63 deletions(-) create mode 100644 packages/frontend/src/lib/components/LiveAreaStackChart.svelte create mode 100644 packages/frontend/src/routes/(app)/stats/components/CPU.svelte create mode 100644 packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte create mode 100644 packages/frontend/src/routes/(app)/stats/components/Memory.svelte diff --git a/packages/frontend/src/lib/components/LiveAreaStackChart.svelte b/packages/frontend/src/lib/components/LiveAreaStackChart.svelte new file mode 100644 index 0000000..6a77570 --- /dev/null +++ b/packages/frontend/src/lib/components/LiveAreaStackChart.svelte @@ -0,0 +1,89 @@ + + + + + ({ + key, + data: data?.[key], + color: `var(--color-${color}-${shades[index % (shades.length - 1)]})`, + }))} + props={merge["props"]>, NonNullable["props"]>>( + { + area: { curve: curveCatmullRom }, + tooltip: { + root: { class: "preset-filled-surface-100-900!" }, + header: { + format: (x) => new Date(x).toLocaleTimeString(), + }, + }, + }, + props.props ?? {}, + )} +/> diff --git a/packages/frontend/src/lib/remotes/clusters.remote.ts b/packages/frontend/src/lib/remotes/clusters.remote.ts index 9c9891c..0720b7c 100644 --- a/packages/frontend/src/lib/remotes/clusters.remote.ts +++ b/packages/frontend/src/lib/remotes/clusters.remote.ts @@ -243,12 +243,10 @@ export const getClustersLive = query.live(async function* () { }); export const getClustersStatsLive = query.live(async function* () { - const token = getRequestEvent().cookies.get("token"); - - const response = await fetch(new URL("/clusters/stats/stream?interval=2", env.API_URL), { + const response = await fetch(new URL("/clusters/stats/stream?interval=1", env.API_URL), { headers: { Accept: "text/event-stream", - Authorization: `Bearer ${token}`, + Authorization: `Bearer ${getRequestEvent().cookies.get("token")}`, }, }); @@ -265,7 +263,8 @@ export const getClustersStatsLive = query.live(async function* () { // @ts-ignore svelte-check false positive .values(); - for await (const chunk of chunks) yield JSON.parse(chunk.data) as GetAggregateStatsDto; + for await (const chunk of chunks) + yield { ...(JSON.parse(chunk.data) as GetAggregateStatsDto), date: new Date() }; return; } case 401: diff --git a/packages/frontend/src/routes/(app)/stats/+layout.svelte b/packages/frontend/src/routes/(app)/stats/+layout.svelte index 58dccd8..05cb1d0 100644 --- a/packages/frontend/src/routes/(app)/stats/+layout.svelte +++ b/packages/frontend/src/routes/(app)/stats/+layout.svelte @@ -1,9 +1,12 @@ -
+{@render appbar(["Statistics"])} + +
{@render children()}
diff --git a/packages/frontend/src/routes/(app)/stats/+page.svelte b/packages/frontend/src/routes/(app)/stats/+page.svelte index b31e380..6483956 100644 --- a/packages/frontend/src/routes/(app)/stats/+page.svelte +++ b/packages/frontend/src/routes/(app)/stats/+page.svelte @@ -1,66 +1,23 @@ -{#snippet card( - label: string, - icon: typeof Icon, - percentage: number, - value: string[], -)} - {@const Icon = icon} +
+
+ +
+
-
- - -
-

{label}

- {#each value as value} -

{value}

- {/each} -
-
- - - - - - + +
-{/snippet} - -
- {@render card("CPU", CpuIcon, cpu, [cpu.toPrecision(2) + "%"])} - {@render card("Memory", MemoryStickIcon, memory, [ - memory.toPrecision(2) + "%", - formatBytes(memoryUsage), - ])}
diff --git a/packages/frontend/src/routes/(app)/stats/components/CPU.svelte b/packages/frontend/src/routes/(app)/stats/components/CPU.svelte new file mode 100644 index 0000000..aaa470f --- /dev/null +++ b/packages/frontend/src/routes/(app)/stats/components/CPU.svelte @@ -0,0 +1,45 @@ + + + + + diff --git a/packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte b/packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte new file mode 100644 index 0000000..c2103f4 --- /dev/null +++ b/packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte @@ -0,0 +1,36 @@ + + + + + ({ + key, + color: `var(--color-${color}-${shades[index % (shades.length - 1)]})`, + }))} +/> diff --git a/packages/frontend/src/routes/(app)/stats/components/Memory.svelte b/packages/frontend/src/routes/(app)/stats/components/Memory.svelte new file mode 100644 index 0000000..a8990a5 --- /dev/null +++ b/packages/frontend/src/routes/(app)/stats/components/Memory.svelte @@ -0,0 +1,45 @@ + + + + + From 01a75566e6ba4fa9ec3f86d48f6df87139e49b61 Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:11:38 +0000 Subject: [PATCH 2/2] fix(frontend): pull request fixes --- .../src/lib/remotes/clusters.remote.ts | 65 ++++++++++--------- .../src/routes/(app)/stats/+page.svelte | 2 +- .../routes/(app)/stats/components/CPU.svelte | 3 +- .../components/LiveAreaStackChart.svelte | 36 ---------- .../(app)/stats/components/Memory.svelte | 2 +- 5 files changed, 39 insertions(+), 69 deletions(-) delete mode 100644 packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte diff --git a/packages/frontend/src/lib/remotes/clusters.remote.ts b/packages/frontend/src/lib/remotes/clusters.remote.ts index 0720b7c..97509a7 100644 --- a/packages/frontend/src/lib/remotes/clusters.remote.ts +++ b/packages/frontend/src/lib/remotes/clusters.remote.ts @@ -5,6 +5,7 @@ import { ClusterIdParamSchema, GetClusterLogsQuerySchema, GetClusterSchema, + SseIntervalQuerySchema, type GetAggregateStatsDto, type GetClusterDto, type GetClusterLogsDto, @@ -242,39 +243,45 @@ export const getClustersLive = query.live(async function* () { } }); -export const getClustersStatsLive = query.live(async function* () { - const response = await fetch(new URL("/clusters/stats/stream?interval=1", env.API_URL), { - headers: { - Accept: "text/event-stream", - Authorization: `Bearer ${getRequestEvent().cookies.get("token")}`, - }, - }); +export const getClustersStatsLive = query.live( + SseIntervalQuerySchema, + async function* ({ interval }) { + const response = await fetch( + new URL(`/clusters/stats/stream?interval=${interval}`, env.API_URL), + { + headers: { + Accept: "text/event-stream", + Authorization: `Bearer ${getRequestEvent().cookies.get("token")}`, + }, + }, + ); - switch (response.status) { - case 200: { - if (!response.body) { - console.error(await response.text()); - return error(500, "An error occurred"); + switch (response.status) { + case 200: { + if (!response.body) { + console.error(await response.text()); + return error(500, "An error occurred"); + } + + const chunks = response.body + .pipeThrough(new TextDecoderStream()) + .pipeThrough(new EventSourceParserStream()) + // @ts-ignore svelte-check false positive + .values(); + + for await (const chunk of chunks) + yield { ...(JSON.parse(chunk.data) as GetAggregateStatsDto), date: new Date() }; + return; } + case 401: + return redirect(303, "/login"); - const chunks = response.body - .pipeThrough(new TextDecoderStream()) - .pipeThrough(new EventSourceParserStream()) - // @ts-ignore svelte-check false positive - .values(); - - for await (const chunk of chunks) - yield { ...(JSON.parse(chunk.data) as GetAggregateStatsDto), date: new Date() }; - return; + default: + console.error(await response.text()); + return error(500, "An error occurred"); } - case 401: - return redirect(303, "/login"); - - default: - console.error(await response.text()); - return error(500, "An error occurred"); - } -}); + }, +); export const startClusters = query.batch( "unchecked", diff --git a/packages/frontend/src/routes/(app)/stats/+page.svelte b/packages/frontend/src/routes/(app)/stats/+page.svelte index 6483956..47314d7 100644 --- a/packages/frontend/src/routes/(app)/stats/+page.svelte +++ b/packages/frontend/src/routes/(app)/stats/+page.svelte @@ -8,7 +8,7 @@
- +
{ let unmounted = false; (async () => { - for await (const { date, ...stats } of getClustersStatsLive()) { - console.log(unmounted); + for await (const { date, ...stats } of getClustersStatsLive({ interval: 1 })) { if (unmounted) break; value = Object.fromEntries( Object.entries(stats as GetAggregateStatsDto).map(([key, { cpuPercentage }]) => [ diff --git a/packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte b/packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte deleted file mode 100644 index c2103f4..0000000 --- a/packages/frontend/src/routes/(app)/stats/components/LiveAreaStackChart.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - - - - ({ - key, - color: `var(--color-${color}-${shades[index % (shades.length - 1)]})`, - }))} -/> diff --git a/packages/frontend/src/routes/(app)/stats/components/Memory.svelte b/packages/frontend/src/routes/(app)/stats/components/Memory.svelte index a8990a5..c1b9c6c 100644 --- a/packages/frontend/src/routes/(app)/stats/components/Memory.svelte +++ b/packages/frontend/src/routes/(app)/stats/components/Memory.svelte @@ -12,7 +12,7 @@ onMount(() => { let unmounted = false; (async () => { - for await (const { date, ...stats } of getClustersStatsLive()) { + for await (const { date, ...stats } of getClustersStatsLive({ interval: 1 })) { if (unmounted) break; value = Object.fromEntries( Object.entries(stats as GetAggregateStatsDto).map(([key, { memory }]) => [