ws: migrate push stream to data streaming
- daemon: send full snapshot on connect; versions/tick now carry the full state of one subsystem (subsystem + data); no legacy updated/subsystems payloads; refresh_state and POST /status/refresh broadcast per-subsystem versions with data - client: modelSet() patches models in place; onMessage/topic refresh retired; 3s initial-load fallback via new POST /api/status/refresh - schema: lib/schema.py TypedDicts + hoover/schema.js defaults + docs/state-model.md as single source of truth for state shapes - system: poll at 1s, volatile metrics registered, dashboard uses a dedicated system model (status model removed) - firewall: refuse to strip both https and ssh from the default zone (409, force override via UI confirm); set_zone_services persists services to the declarative config; collector exposes default_zone - UI: pages migrate to flat state shapes; post-mutation modelFetch refreshes removed (WS delta covers it) - tests: ws snapshot/delta/broadcast, refresh-state, schema types, model-set/js ws handler and reconnect fallback
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { html, PageHeader, Table, renderGuardMulti, enc, $val, apiFetch, toast, definePage, getModel, modelFetch, StatusText, QuickModal, ZoneSelect } from '/static/hoover/index.js';
|
||||
import { html, PageHeader, Table, renderGuardMulti, enc, $val, apiFetch, toast, definePage, getModel, StatusText, QuickModal, ZoneSelect } from '/static/hoover/index.js';
|
||||
|
||||
async function changeZone(name, zone, state) {
|
||||
const r = await apiFetch('/api/firewall/zones/' + enc(zone) + '/interfaces', {
|
||||
@@ -7,8 +7,7 @@ async function changeZone(name, zone, state) {
|
||||
});
|
||||
if (r.ok) {
|
||||
toast(name + ' \u2192 ' + zone, 'success');
|
||||
modelFetch('firewall');
|
||||
modelFetch('network');
|
||||
// No modelFetch — daemon broadcasts both subsystems via WS delta.
|
||||
} else {
|
||||
toast(r.error || 'Failed', 'error');
|
||||
}
|
||||
@@ -33,7 +32,6 @@ const cfgModalFn = QuickModal({
|
||||
}),
|
||||
successMsg: 'Config saved',
|
||||
},
|
||||
refresh: ['firewall', 'network'],
|
||||
});
|
||||
|
||||
export default definePage({
|
||||
@@ -49,8 +47,11 @@ export default definePage({
|
||||
|
||||
const fwZones = state.firewall.data?.zones || {};
|
||||
const netData = state.network.data?.interfaces || {};
|
||||
const zones = fwZones.available || [];
|
||||
const activeZones = fwZones.active || {};
|
||||
const zones = Object.keys(fwZones);
|
||||
const activeZones = state.firewall.data?.active_zones || {};
|
||||
// Per-interface config lives in the top-level config (flat runtime
|
||||
// entries carry no per-interface config).
|
||||
const netCfgIfaces = state.network.data?.config?.interfaces || {};
|
||||
|
||||
// Loopback has no networkd config to manage — show real NICs only.
|
||||
const ifaces = Object.entries(netData).filter(([name]) => name !== 'lo').map(([name, entry]) => {
|
||||
@@ -63,11 +64,11 @@ export default definePage({
|
||||
}
|
||||
return {
|
||||
name,
|
||||
mac: entry?.runtime?.mac || null,
|
||||
ips: [...(entry?.config?.addresses || []), ...(entry?.runtime?.addresses || [])],
|
||||
state: (entry?.runtime?.state || '').startsWith('routable') || (entry?.runtime?.state || '').startsWith('carrier') ? 'up' : 'down',
|
||||
mac: entry?.mac || null,
|
||||
ips: [...(netCfgIfaces[name]?.addresses || []), ...(entry?.addresses || [])],
|
||||
state: (entry?.state || '').startsWith('routable') || (entry?.state || '').startsWith('carrier') ? 'up' : 'down',
|
||||
zone,
|
||||
config: entry?.config || {},
|
||||
config: netCfgIfaces[name] || {},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user