dhcp: auto-populate IP range when selecting interface

- QuickModal: add postRender callback for DOM-level initialization
- dhcp: derive start/end from interface CIDR on change
This commit is contained in:
2026-06-28 13:04:51 +00:00
parent 391466664e
commit baa441fa13
2 changed files with 41 additions and 2 deletions
+1
View File
@@ -243,6 +243,7 @@ export function QuickModal(props = {}) {
];
}
formModal(inner, title, fields, actions);
if (props.postRender) props.postRender(inner, data);
});
};
}
+40 -2
View File
@@ -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`<button class="btn btn-primary" onClick=${() => makeAddRange(state.firewall.data?.zones?.active)(state)}>Add Range</button>`,
html`<button class="btn btn-primary" onClick=${() => makeAddRange(state.firewall.data?.zones?.active, state.firewall.data?.interfaces)(state)}>Add Range</button>`,
html`<button class="btn btn-outline" onClick=${() => addLease(state)}>Static Lease</button>`,
html`<button class="btn btn-outline" onClick=${() => addDns(state)}>DNS Record</button>`,
ActionButton({