From 00d317d64561265a77499dc38a149745e4a2222b Mon Sep 17 00:00:00 2001 From: Wim Brand Date: Tue, 20 Jan 2026 01:02:17 +0100 Subject: [PATCH] Add host metrics dashboard --- svelte/package-lock.json | 22 +-- svelte/package.json | 2 +- svelte/src/admin_panel/HostMetrics.svelte | 131 ++++++++++++++++++ .../src/admin_panel/HostMetricsGraph.svelte | 101 ++++++++++++++ svelte/src/admin_panel/HostMetricsLib.ts | 50 +++++++ svelte/src/admin_panel/Router.svelte | 6 + svelte/tsconfig.json | 5 +- 7 files changed, 298 insertions(+), 19 deletions(-) create mode 100644 svelte/src/admin_panel/HostMetrics.svelte create mode 100644 svelte/src/admin_panel/HostMetricsGraph.svelte create mode 100644 svelte/src/admin_panel/HostMetricsLib.ts diff --git a/svelte/package-lock.json b/svelte/package-lock.json index bd06ab9..d3a7b26 100644 --- a/svelte/package-lock.json +++ b/svelte/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", "behave-js": "^1.5.0", - "chart.js": "^4.4.6", + "chart.js": "^4.5.1", "country-data-list": "^1.4.0", "pure-color": "^1.3.0", "rollup-plugin-includepaths": "^0.2.4", @@ -65,7 +65,6 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -2507,7 +2506,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", @@ -2559,7 +2557,6 @@ "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2576,7 +2573,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2776,7 +2772,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -2820,9 +2815,9 @@ "license": "CC-BY-4.0" }, "node_modules/chart.js": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", - "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -3535,7 +3530,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3562,7 +3556,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3708,7 +3701,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -4045,7 +4037,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.39.11.tgz", "integrity": "sha512-8MxWVm2+3YwrFbPaxOlT1bbMi6OTenrAgks6soZfiaS8Fptk4EVyRIFhJc3RpO264EeSNwgjWAdki0ufg4zkGw==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -4136,7 +4127,6 @@ "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", "devOptional": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -4183,8 +4173,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/typescript": { "version": "5.9.3", @@ -4288,7 +4277,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", diff --git a/svelte/package.json b/svelte/package.json index 1e618fc..35622c2 100644 --- a/svelte/package.json +++ b/svelte/package.json @@ -26,7 +26,7 @@ "dependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", "behave-js": "^1.5.0", - "chart.js": "^4.4.6", + "chart.js": "^4.5.1", "country-data-list": "^1.4.0", "pure-color": "^1.3.0", "rollup-plugin-includepaths": "^0.2.4", diff --git a/svelte/src/admin_panel/HostMetrics.svelte b/svelte/src/admin_panel/HostMetrics.svelte new file mode 100644 index 0000000..b9f4b33 --- /dev/null +++ b/svelte/src/admin_panel/HostMetrics.svelte @@ -0,0 +1,131 @@ + + +{#if loaded} +
+ + + + + + + + + + +
+ + {#each groups as group (group.title)} + + {#snippet header()} +
{group.title}
+ {/snippet} + +
+ {#each group.graphs as graph (graph.metric)} + + {/each} +
+
+ {/each} +{/if} + + diff --git a/svelte/src/admin_panel/HostMetricsGraph.svelte b/svelte/src/admin_panel/HostMetricsGraph.svelte new file mode 100644 index 0000000..9639788 --- /dev/null +++ b/svelte/src/admin_panel/HostMetricsGraph.svelte @@ -0,0 +1,101 @@ + + +
+
{metric}
+ +
+ + diff --git a/svelte/src/admin_panel/HostMetricsLib.ts b/svelte/src/admin_panel/HostMetricsLib.ts new file mode 100644 index 0000000..8da8e9a --- /dev/null +++ b/svelte/src/admin_panel/HostMetricsLib.ts @@ -0,0 +1,50 @@ +import { loading_finish, loading_start } from "lib/Loading"; +import { get_endpoint } from "lib/PixeldrainAPI"; +import hsl2rgb from "pure-color/convert/hsl2rgb"; +import rgb2hex from "pure-color/convert/rgb2hex"; + +let host_colours: { [key: string]: string } = {} +export const host_colour = (id: string): string => { + let host_count: number = Object.keys(host_colours).length + if (host_colours[id] === undefined) { + host_colours[id] = "" + } else { + return host_colours[id] + } + + const colour_interval = 360 / (host_count + 1) + + var i = 0 + for (const host of Object.keys(host_colours).sort()) { + host_colours[host] = rgb2hex(hsl2rgb([i * colour_interval, 100, 70])) + i++ + } + + return host_colours[id] +} + +let host_names: { [key: string]: string } = {} +export const host_label = async (id: string): Promise => { + if (id === "00000000-0000-0000-0000-000000000000") { + return "task scheduler" + } + if (host_names[id] === undefined) { + return id + } + + return host_names[id] +} + +export const load_host_names = async () => { + loading_start() + try { + const req = await fetch(get_endpoint() + "/status") + const j = await req.json() + + for (const peer of j.peers) { + host_names[peer.id] = peer.hostname + } + } finally { + loading_finish() + } +} diff --git a/svelte/src/admin_panel/Router.svelte b/svelte/src/admin_panel/Router.svelte index fc55518..5f1940e 100644 --- a/svelte/src/admin_panel/Router.svelte +++ b/svelte/src/admin_panel/Router.svelte @@ -10,6 +10,7 @@ import MollieSettlements from "./MollieSettlements.svelte"; import PayPalTaxes from "./PayPalTaxes.svelte"; import UserBans from "./user_bans/UserBans.svelte"; import InvoiceVat from "./InvoiceVAT.svelte"; +import HostMetrics from "./HostMetrics.svelte"; let pages = [ { @@ -47,6 +48,11 @@ let pages = [ title: "User Management", icon: "person", component: UserManagement, + }, { + path: "/admin/host_metrics", + title: "Host Metrics", + icon: "area_chart", + component: HostMetrics, }, { path: "/admin/mollie_settlements", title: "Prepaid accounting", diff --git a/svelte/tsconfig.json b/svelte/tsconfig.json index 4b5bf03..78861e7 100644 --- a/svelte/tsconfig.json +++ b/svelte/tsconfig.json @@ -10,5 +10,8 @@ "noUnusedLocals": true, "module": "preserve", "moduleResolution": "bundler" - } + }, + "include": [ + "src/" + ] }