diff --git a/webui/static/hoover/components/modal.js b/webui/static/hoover/components/modal.js index 98806f6..6570d50 100644 --- a/webui/static/hoover/components/modal.js +++ b/webui/static/hoover/components/modal.js @@ -243,6 +243,7 @@ export function QuickModal(props = {}) { ]; } formModal(inner, title, fields, actions); + if (props.postRender) props.postRender(inner, data); }); }; } diff --git a/webui/static/pages/dhcp.js b/webui/static/pages/dhcp.js index 94431ce..93c106f 100644 --- a/webui/static/pages/dhcp.js +++ b/webui/static/pages/dhcp.js @@ -1,6 +1,6 @@ import { html, PageHeader, ConfirmDelete, Tabs, Table, ServiceStatus, renderGuardMulti, esc, enc, $val, apiFetch, toast, definePage, getModel, ActionButton, ActionGroup, QuickModal } from '/static/hoover/index.js?v=7'; -function makeAddRange(activeZones) { +function makeAddRange(activeZones, interfaces) { const opts = [ ['', '(global)'], ...Object.entries(activeZones || {}) @@ -13,6 +13,27 @@ function makeAddRange(activeZones) { }), ]; + const ranges = {}; + (interfaces || []).forEach(iface => { + for (const cidr of (iface.ips || [])) { + if (cidr.indexOf('/') === -1) continue; + const parts = cidr.split('/'); + const addr = parts[0].split('.').map(Number); + const prefix = parseInt(parts[1], 10); + if (prefix > 30 || prefix < 8) continue; + const mask = (0xFFFFFFFF << (32 - prefix)) >>> 0; + const netA = ((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]) & mask; + const broadcast = (netA | (~mask >>> 0)) >>> 0; + const s = (netA + 100) >>> 0; + const e = Math.min((netA + 200) >>> 0, broadcast - 1); + const toIp = (n) => [n >> 24 & 0xFF, n >> 16 & 0xFF, n >> 8 & 0xFF, n & 0xFF].join('.'); + if (s <= e) { + ranges[iface.name] = { start: toIp(s), end: toIp(e) }; + } + break; + } + }); + return QuickModal({ title: 'Add DHCP Range', fields: [ @@ -21,6 +42,23 @@ function makeAddRange(activeZones) { { label: 'End IP', id: 'r-end', placeholder: '192.168.1.200' }, { label: 'Lease Time', id: 'r-lease', placeholder: '12h' }, ], + postRender: (inner) => { + const select = inner.querySelector('#r-iface'); + const startInput = inner.querySelector('#r-start'); + const endInput = inner.querySelector('#r-end'); + if (select && startInput && endInput) { + select.addEventListener('change', () => { + const r = ranges[select.value]; + if (r) { + startInput.value = r.start; + endInput.value = r.end; + } else { + startInput.value = ''; + endInput.value = ''; + } + }); + } + }, submit: { url: '/api/dhcp/ranges', body: () => ({ @@ -131,7 +169,7 @@ export default definePage({ const tabNames = ['ranges', 'leases', 'dns', 'active']; const actions = ActionGroup( - html``, + html``, html``, html``, ActionButton({