feat: add system metrics dashboard with resource monitoring

- Add system metrics endpoint (CPU load, memory, swap, network traffic)
- Collect metrics from /proc and /sys (no subprocess required)
- Overhaul dashboard to pull from per-subsystem models
- Remove deprecated /status/all monolithic endpoint
- Improve networkd import to handle optional priority prefix
- Fix CSS duplicate .grid-4 rule and unused dashboard imports
This commit is contained in:
2026-07-15 00:24:43 +00:00
parent c21639b7f1
commit dadabd7954
11 changed files with 438 additions and 107 deletions
+21 -13
View File
@@ -1,6 +1,6 @@
import { h, render, Link, hComp, ToastContainer, connect, apiFetch, modelRegister, modelFetch, reactive } from '/static/hoover/index.js?v=8';
import { h, render, Link, hComp, ToastContainer, connect, apiFetch, modelRegister, modelFetch, reactive } from '/static/hoover/index.js?v=10';
import DashboardPage from '/static/pages/dashboard.js?v=9';
import DashboardPage from '/static/pages/dashboard.js?v=11';
import InterfacesPage from '/static/pages/interfaces.js?v=9';
import ZonesPage from '/static/pages/zones.js?v=9';
import RulesPage from '/static/pages/rules.js?v=9';
@@ -28,16 +28,6 @@ const Nav = [
{ path: '/logs', label: 'Logs' },
];
/* ── Model registration ────────────────────────────────────── */
modelRegister('status', {
subsystem: 'status',
fetch: async () => {
const r = await apiFetch('/api/status/all');
if (!r.ok) throw new Error(r.error);
return r.data;
},
});
modelRegister('firewall', {
subsystem: 'firewall',
fetch: async () => {
@@ -166,8 +156,26 @@ modelRegister('logs', {
},
});
modelRegister('status', {
subsystem: '*',
fetch: async () => {
const [pendingR, metricsR] = await Promise.allSettled([
apiFetch('/api/status/pending'),
apiFetch('/api/status/system-metrics'),
]);
const result = {};
if (pendingR.status === 'fulfilled' && pendingR.value.ok) {
result.pending = pendingR.value.data || {};
}
if (metricsR.status === 'fulfilled' && metricsR.value.ok) {
result.metrics = metricsR.value.data || {};
}
return result;
},
});
/* ── Initial fetch ─────────────────────────────────────────── */
for (const name of ['status', 'firewall', 'network', 'dnsmasq', 'nginx', 'backends', 'wireguard', 'acme']) {
for (const name of ['firewall', 'network', 'dnsmasq', 'nginx', 'backends', 'wireguard', 'acme', 'status']) {
modelFetch(name);
}
modelFetch('logs', 'journal');