b673e87c9b
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
59 lines
2.0 KiB
JavaScript
59 lines
2.0 KiB
JavaScript
import { h, PageHeader, Tabs, esc, definePage, renderGuard, modelFetch, getModel } from '/static/hoover/index.js?v=7';
|
|
|
|
const logTabs = [
|
|
{ key: 'journal', label: 'Journal' },
|
|
{ key: 'nginx-access', label: 'Nginx Access' },
|
|
{ key: 'nginx-error', label: 'Nginx Error' },
|
|
{ key: 'dnsmasq', label: 'Dnsmasq' },
|
|
{ key: 'app', label: 'App' },
|
|
];
|
|
|
|
export default definePage({
|
|
init() {
|
|
return {
|
|
logs: getModel('logs'),
|
|
activeTab: 'journal',
|
|
};
|
|
},
|
|
render(state) {
|
|
const logData = state.logs.data;
|
|
const stale = logData?.tab !== state.activeTab;
|
|
const guard = renderGuard(state.logs, 'Logs', 'System and service logs', stale ? undefined : logData?.data);
|
|
if (guard) return guard;
|
|
|
|
const lines = logData.data || [];
|
|
const tab = logTabs.find(t => t.key === state.activeTab) || logTabs[0];
|
|
const lineVnodes = lines.map((line, i) =>
|
|
h('div', { class: 'log-line', key: i }, esc(line))
|
|
);
|
|
|
|
const tabsBody = Tabs({
|
|
state,
|
|
tabs: logTabs.map(t => t.key),
|
|
formatLabel: (k) => {
|
|
const t = logTabs.find(t => t.key === k);
|
|
return t ? t.label : k.charAt(0).toUpperCase() + k.slice(1);
|
|
},
|
|
onTabClick: (key) => modelFetch('logs', key),
|
|
});
|
|
|
|
return [
|
|
PageHeader({ title: 'Logs', subtitle: 'System and service logs' }),
|
|
h('div', { class: 'card', key: 'log-card' },
|
|
tabsBody,
|
|
h('div', { class: 'card-header' },
|
|
h('span', null, tab.label),
|
|
h('button', {
|
|
class: 'btn btn-sm btn-outline',
|
|
style: 'float:right;',
|
|
'on:click': () => modelFetch('logs', state.activeTab),
|
|
}, '\u21BB'),
|
|
),
|
|
h('div', { class: 'card-body log-body' },
|
|
h('pre', null, lineVnodes),
|
|
),
|
|
),
|
|
];
|
|
},
|
|
});
|