Add state management, WebSocket polling, html.js templating, and refactor pages
- lib/state.py: per-subsystem collectors with versioned state store - daemon/server.py: state refresh on request, batch routing updates - webui/static/hoover/html.js: new html tag template helper via htm.js - webui/static/hoover/websocket.js: real-time state change notifications - webui/static/hoover/vdom.js: VDOM improvements for keyed diff - All frontend pages refactored to use html templates - Add tests for state management and polling - Update docs and AGENTS.md
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { h, PageHeader, definePage, getModel, renderGuard, ServiceStatus, StatCard } from '/static/hoover/index.js?v=7';
|
||||
import { html, PageHeader, definePage, getModel, renderGuard, ServiceStatus, StatCard } from '/static/hoover/index.js?v=7';
|
||||
|
||||
export default definePage({
|
||||
init() {
|
||||
@@ -21,41 +21,33 @@ export default definePage({
|
||||
const dmsk = d.dnsmasq?.status || {};
|
||||
const wP = (d.wg || {}).peers || [];
|
||||
|
||||
const stats = html`<div class="grid grid-4">
|
||||
<${StatCard} label="Active Zones" value=${Object.keys(fwZones).length}
|
||||
meta=${Object.keys(fwZones).join(', ') || 'None'} />
|
||||
<${StatCard} label="Interfaces Up" value=${upC + '/' + nCount}
|
||||
meta=${upI.map(i => i.name).join(', ') || 'None up'} />
|
||||
<${StatCard} label="WireGuard" value=${String(d.wg?.state || 'unknown')}
|
||||
meta=${wP.length + ' peers'} />
|
||||
<${StatCard} label="Certificates" value=${certs.length}
|
||||
meta=${certW.length + ' expiring/expired'} />
|
||||
</div>`;
|
||||
|
||||
const services = html`<div class="grid grid-2">
|
||||
<div class="card">
|
||||
<div class="card-header">Services</div>
|
||||
<div class="card-body">
|
||||
<ul class="service-list">
|
||||
<li><${ServiceStatus} state=${dmsk.state || 'down'} label="Dnsmasq" /></li>
|
||||
<li><${ServiceStatus} state=${d.wg?.state || 'down'} label="WireGuard" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
return [
|
||||
PageHeader({ title: 'Dashboard', subtitle: 'System overview' }),
|
||||
h('div', { class: 'grid grid-4' },
|
||||
StatCard({
|
||||
label: 'Active Zones',
|
||||
value: Object.keys(fwZones).length,
|
||||
meta: Object.keys(fwZones).join(', ') || 'None',
|
||||
}),
|
||||
StatCard({
|
||||
label: 'Interfaces Up',
|
||||
value: upC + '/' + nCount,
|
||||
meta: upI.map(i => i.name).join(', ') || 'None up',
|
||||
}),
|
||||
StatCard({
|
||||
label: 'WireGuard',
|
||||
value: String(d.wg?.state || 'unknown'),
|
||||
meta: wP.length + ' peers',
|
||||
}),
|
||||
StatCard({
|
||||
label: 'Certificates',
|
||||
value: certs.length,
|
||||
meta: certW.length + ' expiring/expired',
|
||||
}),
|
||||
),
|
||||
h('div', { class: 'grid grid-2' },
|
||||
h('div', { class: 'card' },
|
||||
h('div', { class: 'card-header' }, 'Services'),
|
||||
h('div', { class: 'card-body' },
|
||||
h('ul', { class: 'service-list' },
|
||||
h('li', null, ServiceStatus({ state: dmsk.state || 'down', label: 'Dnsmasq' })),
|
||||
h('li', null, ServiceStatus({ state: d.wg?.state || 'down', label: 'WireGuard' })),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
stats,
|
||||
services,
|
||||
];
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user