633505e7dc
- Add quick modal, table, service status, and confirmation dialog components - Refactor all pages (certs, dhcp, proxy, etc.) to use new component patterns - Introduce refactor load utility and render guard for consistent UX - Add hoover documentation and update AGENTS.md, architecture, overview
112 lines
4.4 KiB
JavaScript
112 lines
4.4 KiB
JavaScript
import { h, PageHeader, Table, renderGuard, enc, $val, apiFetch, toast, definePage, refactorLoad, StatusText, QuickModal, ZoneSelect } from '/static/hoover/index.js?v=6';
|
|
|
|
async function changeZone(name, zone, state) {
|
|
const r = await apiFetch('/api/firewall/zones/' + enc(zone) + '/interfaces', {
|
|
method: 'POST',
|
|
body: { interfaces: [name] },
|
|
});
|
|
if (r.ok) {
|
|
toast(name + ' \u2192 ' + zone, 'success');
|
|
await load(state);
|
|
} else {
|
|
toast(r.error || 'Failed', 'error');
|
|
}
|
|
}
|
|
|
|
const cfgModalFn = QuickModal({
|
|
title: (d) => 'Config: ' + d.name,
|
|
fields: (d) => {
|
|
const cfg = d.config || {};
|
|
return [
|
|
{ label: 'Addresses (comma-separated)', id: 'cfg-addrs', value: (cfg.addresses || []).join(', '), placeholder: '192.168.1.1/24' },
|
|
{ label: 'Gateway', id: 'cfg-gw', value: cfg.gateway || '' },
|
|
{ label: 'DNS (comma-separated)', id: 'cfg-dns', value: (cfg.dns || []).join(', '), placeholder: '1.1.1.1, 8.8.8.8' },
|
|
];
|
|
},
|
|
submit: {
|
|
url: (d) => '/api/network/interfaces/' + enc(d.name),
|
|
body: () => ({
|
|
addresses: ($val('cfg-addrs') || '').split(',').map(s => s.trim()).filter(Boolean),
|
|
gateway: ($val('cfg-gw') || '').trim() || undefined,
|
|
dns: ($val('cfg-dns') || '').split(',').map(s => s.trim()).filter(Boolean),
|
|
}),
|
|
successMsg: 'Config saved',
|
|
},
|
|
reload: (s) => load(s),
|
|
});
|
|
|
|
async function load(state, abortController, entry) {
|
|
await refactorLoad(state,
|
|
s => s.ifaces?.length,
|
|
async (s, sig, isAborted) => {
|
|
const [fw, net] = await Promise.all([
|
|
apiFetch('/api/firewall/zones', { signal: sig }),
|
|
apiFetch('/api/network/interfaces', { signal: sig }),
|
|
]);
|
|
if (isAborted()) return;
|
|
if (fw.ok) s.zones = fw.data?.available || [];
|
|
else s.error = fw.error;
|
|
if (net.ok) {
|
|
const ifaceZone = {};
|
|
for (const [zoneName, ifaces] of Object.entries(fw.data?.active || {})) {
|
|
for (const name of (ifaces || [])) ifaceZone[name] = zoneName;
|
|
}
|
|
const ifacesObj = net.data?.interfaces || {};
|
|
s.ifaces = Object.entries(ifacesObj).map(([name, entry]) => ({
|
|
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',
|
|
zone: ifaceZone[name] || null,
|
|
config: entry?.config || {},
|
|
}));
|
|
} else if (!s.error) {
|
|
s.error = net.error;
|
|
}
|
|
},
|
|
{ entry, abortController },
|
|
);
|
|
}
|
|
|
|
export default definePage({
|
|
init() {
|
|
return { ifaces: [], zones: [] };
|
|
},
|
|
subscribe: ['firewall', 'networkd'],
|
|
load,
|
|
render(state) {
|
|
const guard = renderGuard(state, 'Interfaces', 'Network interface management', state.ifaces.length);
|
|
if (guard) return guard;
|
|
|
|
const rows = state.ifaces.map(iface => {
|
|
return h('tr', { key: iface.name },
|
|
h('td', null, h('strong', null, iface.name)),
|
|
h('td', { class: 'text-muted' }, String(iface.mac || 'N/A')),
|
|
h('td', null, (iface.ips || []).join(', ') || 'N/A'),
|
|
h('td', null, StatusText({ status: iface.state })),
|
|
h('td', null,
|
|
ZoneSelect({
|
|
zones: state.zones,
|
|
value: iface.zone,
|
|
onChange: (z) => changeZone(iface.name, z, state),
|
|
}),
|
|
h('button', {
|
|
class: 'btn btn-sm btn-outline',
|
|
style: 'margin-left:8px',
|
|
'on:click': () => cfgModalFn(iface),
|
|
}, 'Config'),
|
|
),
|
|
);
|
|
});
|
|
|
|
return [
|
|
PageHeader({ title: 'Interfaces', subtitle: 'Network interface management' }),
|
|
Table({
|
|
columns: ['Name', 'MAC', 'IPs', 'State', 'Zone / Actions'],
|
|
rows,
|
|
emptyText: 'No interfaces found',
|
|
}),
|
|
];
|
|
},
|
|
});
|