import { html, PageHeader, Table, renderGuardMulti, enc, $val, apiFetch, toast, definePage, getModel, modelFetch, StatusText, QuickModal, ZoneSelect } from '/static/hoover/index.js?v=10'; 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'); modelFetch('firewall'); modelFetch('network'); } 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', }, refresh: ['firewall', 'network'], }); export default definePage({ init() { return { firewall: getModel('firewall'), network: getModel('network'), }; }, render(state) { const guard = renderGuardMulti('Interfaces', 'Network interface management', state.firewall, state.network); if (guard) return guard; const fwZones = state.firewall.data?.zones || {}; const netData = state.network.data?.interfaces || {}; const zones = fwZones.available || []; const activeZones = fwZones.active || {}; const ifaces = Object.entries(netData).map(([name, entry]) => { let zone = null; for (const [zoneName, zIfaces] of Object.entries(activeZones)) { if ((zIfaces || []).includes(name)) { zone = zoneName; break; } } 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', zone, config: entry?.config || {}, }; }); const rows = ifaces.map(iface => html`