diff --git a/packages/backend/src/index.dto.ts b/packages/backend/src/index.dto.ts
index e4dab74..d41ab7d 100644
--- a/packages/backend/src/index.dto.ts
+++ b/packages/backend/src/index.dto.ts
@@ -13,3 +13,4 @@ export {
type GetClusterLogsDto,
} from './clusters/dto/get-cluster-logs.dto.js';
export { GetClusterStatsSchema, type GetClusterStatsDto } from './clusters/dto/get-cluster-stats.dto.js';
+export { type GetAggregateStatsDto } from './clusters/dto/get-aggregate-stats.dto.js';
diff --git a/packages/frontend/src/hooks.server.ts b/packages/frontend/src/hooks.server.ts
new file mode 100644
index 0000000..3da0e53
--- /dev/null
+++ b/packages/frontend/src/hooks.server.ts
@@ -0,0 +1,11 @@
+import { redirect, type HandleClientError } from "@sveltejs/kit";
+
+export const handleError: HandleClientError = async ({ status }) => {
+ switch (status) {
+ case 404:
+ return redirect(303, "/clusters");
+
+ default:
+ break;
+ }
+};
diff --git a/packages/frontend/src/lib/remotes/auth.remote.ts b/packages/frontend/src/lib/remotes/auth.remote.ts
index 3b244c0..30df2f5 100644
--- a/packages/frontend/src/lib/remotes/auth.remote.ts
+++ b/packages/frontend/src/lib/remotes/auth.remote.ts
@@ -45,7 +45,7 @@ export const login = form(loginSchema, async (data, issue) => {
const { token } = await response.json();
cookies.set("token", token, { path: "/" });
- return redirect(303, "/");
+ return redirect(303, "/clusters");
}
case 400:
return error(400, "Invalid fields");
diff --git a/packages/frontend/src/lib/remotes/clusters.remote.ts b/packages/frontend/src/lib/remotes/clusters.remote.ts
index bf5ae59..2f75e36 100644
--- a/packages/frontend/src/lib/remotes/clusters.remote.ts
+++ b/packages/frontend/src/lib/remotes/clusters.remote.ts
@@ -1,6 +1,7 @@
import { command, getRequestEvent, query } from "$app/server";
import { env } from "$env/dynamic/private";
import {
+ type GetAggregateStatsDto,
type GetClusterDto,
type GetClusterLogsDto,
type GetClusterStatsDto,
@@ -226,3 +227,34 @@ export const getClusterStatsLive = query.live(
}
},
);
+
+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), {
+ headers: {
+ Accept: "text/event-stream",
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ switch (response.status) {
+ case 200: {
+ if (!response.body) 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;
+ break;
+ }
+ case 401:
+ return error(401, "Unauthorized");
+
+ default:
+ return error(500, "An error occurred");
+ }
+});
diff --git a/packages/frontend/src/lib/utils/formatBytes.ts b/packages/frontend/src/lib/utils/formatBytes.ts
index cb32466..fcd4c97 100644
--- a/packages/frontend/src/lib/utils/formatBytes.ts
+++ b/packages/frontend/src/lib/utils/formatBytes.ts
@@ -1,5 +1,5 @@
export default function formatBytes(bytes: number) {
const suffixes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.max(0, Math.floor(Math.log(bytes) / Math.log(1024)));
- return (!bytes && "0 Bytes") || (bytes / Math.pow(1024, i)).toFixed(2) + " " + suffixes[i];
+ return (!bytes && "0 Bytes") || (bytes / Math.pow(1024, i)).toPrecision(3) + " " + suffixes[i];
}
diff --git a/packages/frontend/src/routes/(app)/+layout.svelte b/packages/frontend/src/routes/(app)/clusters/+layout.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/+layout.svelte
rename to packages/frontend/src/routes/(app)/clusters/+layout.svelte
diff --git a/packages/frontend/src/routes/(app)/+page.svelte b/packages/frontend/src/routes/(app)/clusters/+page.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/+page.svelte
rename to packages/frontend/src/routes/(app)/clusters/+page.svelte
diff --git a/packages/frontend/src/routes/(app)/components/ClusterActions.svelte b/packages/frontend/src/routes/(app)/clusters/components/ClusterActions.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/ClusterActions.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/ClusterActions.svelte
diff --git a/packages/frontend/src/routes/(app)/components/ClusterCPU.svelte b/packages/frontend/src/routes/(app)/clusters/components/ClusterCPU.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/ClusterCPU.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/ClusterCPU.svelte
diff --git a/packages/frontend/src/routes/(app)/components/ClusterLogs.svelte b/packages/frontend/src/routes/(app)/clusters/components/ClusterLogs.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/ClusterLogs.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/ClusterLogs.svelte
diff --git a/packages/frontend/src/routes/(app)/components/ClusterMemory.svelte b/packages/frontend/src/routes/(app)/clusters/components/ClusterMemory.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/ClusterMemory.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/ClusterMemory.svelte
diff --git a/packages/frontend/src/routes/(app)/components/ClusterShards.svelte b/packages/frontend/src/routes/(app)/clusters/components/ClusterShards.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/ClusterShards.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/ClusterShards.svelte
diff --git a/packages/frontend/src/routes/(app)/components/ClusterStatus.svelte b/packages/frontend/src/routes/(app)/clusters/components/ClusterStatus.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/ClusterStatus.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/ClusterStatus.svelte
diff --git a/packages/frontend/src/routes/(app)/components/Header.svelte b/packages/frontend/src/routes/(app)/clusters/components/Header.svelte
similarity index 100%
rename from packages/frontend/src/routes/(app)/components/Header.svelte
rename to packages/frontend/src/routes/(app)/clusters/components/Header.svelte
diff --git a/packages/frontend/src/routes/(app)/stats/+layout.svelte b/packages/frontend/src/routes/(app)/stats/+layout.svelte
new file mode 100644
index 0000000..58dccd8
--- /dev/null
+++ b/packages/frontend/src/routes/(app)/stats/+layout.svelte
@@ -0,0 +1,9 @@
+
+
+
{label}
+ {#each value as value} +{value}
+ {/each} +