refactor: introduce model layer for centralized data synchronization

Add hoover model.js as a central reactive store per subsystem, replacing
per-component data fetching with a single source of truth.

- Add hoover/model.js with modelRegister, modelFetch, and WS invalidation
- Refactor websocket.js to route messages to model refresh (drop per-component
  subscribe/unsubscribe)
- Simplify component.js by removing WS subscription management
- Add refresh option to apiSubmit, deprecate refactorLoad and checkAbort
- Rewrite all pages to use getModel() instead of inline data fetching
- Bootstrap model registrations in app.js
- Add GET /api/firewall/state endpoint
- Fix restart-services.sh restart order and add service health verification
- Update hoover.md docs with model layer architecture
This commit is contained in:
2026-06-22 22:54:29 +00:00
parent 633505e7dc
commit b673e87c9b
27 changed files with 952 additions and 838 deletions
+7 -18
View File
@@ -1,27 +1,16 @@
import { h, PageHeader, apiFetch, definePage, renderGuard, refactorLoad, ServiceStatus, StatCard } from '/static/hoover/index.js?v=6';
import { h, PageHeader, definePage, getModel, renderGuard, ServiceStatus, StatCard } from '/static/hoover/index.js?v=7';
export default definePage({
init() {
return { data: null };
},
subscribe: ['firewall', 'dnsmasq', 'wireguard', 'acme', 'networkd'],
async load(state, abortController, entry) {
await refactorLoad(state,
s => s.data,
async (s, sig, isAborted) => {
const res = await apiFetch('/api/status/all', { signal: sig });
if (isAborted()) return;
if (res.ok) s.data = res.data;
else s.error = res.error;
},
{ entry, abortController },
);
return {
status: getModel('status'),
};
},
render(state) {
const guard = renderGuard(state, 'Dashboard', 'System overview', state.data);
const guard = renderGuard(state.status, 'Dashboard', 'System overview', state.status.data);
if (guard) return guard;
const d = state.data;
const d = state.status.data;
const fwZones = (d.firewall?.zones) || {};
const net = d.net || {};
const nCount = Object.keys(net).length;
@@ -69,4 +58,4 @@ export default definePage({
),
];
},
});
});