From 23f0ba995cb66af69b7bea004276a44e1b314046 Mon Sep 17 00:00:00 2001 From: d00f Date: Wed, 5 Aug 2026 20:38:47 +0200 Subject: [PATCH 1/3] css: align read-only values with the input fields The System Settings rows all pair a label with a form control, except Model, which is a bare span. The inputs carry padding and a left margin, so their text starts about 32px further in than the model name did - close enough to look like a mistake and far enough to see it. Give read-only values a class with the same metrics instead of styling the model specifically; any other value shown without a control gets the alignment for free. --- html/style.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/html/style.css b/html/style.css index 84d3a2e..9bc8b5d 100644 --- a/html/style.css +++ b/html/style.css @@ -82,6 +82,8 @@ object, img { .isNOK{ color: #900;} .isOK{ color: #090;} .ip{padding:8px 16px;margin-bottom: 1em;margin-left: 1em} +/* read-only values line up with the text inside the input fields */ +.rotext{display:inline-block;padding:8px 16px;margin-bottom: 1em;margin-left: 1em} .row {display: flex;} .rcol {flex: 90%;} .lcol {flex: 10%;} From 1a55a134c76b61a486a23fff0a655fb35d1c9dc8 Mon Sep 17 00:00:00 2001 From: d00f Date: Wed, 5 Aug 2026 20:38:47 +0200 Subject: [PATCH 2/3] system: pick the management VLAN from System Settings `vlan mgmt` existed on the CLI, but nothing reported which VLAN currently carries management, so the setting was invisible from the web UI - the only way to find out was to read the startup config. Report it in information.json and offer it where the other switch-wide addressing settings live, between Gateway and Language, as a picker filled from the configured VLANs. When management is untagged there is no VLAN to select, so that state shows as a disabled entry rather than inventing an id the switch would reject. Confirm before applying, and say what will happen rather than echoing the action back: the switch starts tagging its own frames, and if the port you came in on does not carry that VLAN the page becomes unreachable and the way back is the console. Cancelling puts the picker back where it was. --- html/i18n.js | 12 ++++++++++++ html/system.html | 6 +++++- html/system.js | 42 ++++++++++++++++++++++++++++++++++++++++++ httpd/page_impl.c | 6 +++++- 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/html/i18n.js b/html/i18n.js index a8d43cb..791074d 100644 --- a/html/i18n.js +++ b/html/i18n.js @@ -144,6 +144,10 @@ var LANG = { sys_netmask: 'Netmask:', sys_gateway: 'Gateway:', sys_language: 'Language:', + sys_mgmt_vlan: 'Management VLAN:', + sys_mgmt_untagged: 'untagged', + sys_mgmt_confirm: 'Move switch management to VLAN ', + sys_mgmt_warn: 'The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console. Continue?', sys_ip_note: 'When updating the above settings, remember to point your browser to the new IP afterwards:', sys_update: 'Update Settings', sys_save_label: 'Save all current settings to Flash:', @@ -326,6 +330,10 @@ var LANG = { sys_netmask: 'ネットマスク:', sys_gateway: 'ゲートウェイ:', sys_language: '言語:', + sys_mgmt_vlan: 'Management VLAN:', + sys_mgmt_untagged: 'untagged', + sys_mgmt_confirm: 'Move switch management to VLAN ', + sys_mgmt_warn: 'The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console. Continue?', sys_ip_note: '上記設定を変更した場合は、ブラウザで新しい IP にアクセスしてください:', sys_update: '設定更新', sys_save_label: '現在の設定をフラッシュに保存:', @@ -508,6 +516,10 @@ var LANG = { sys_netmask: '子网掩码:', sys_gateway: '网关:', sys_language: '语言:', + sys_mgmt_vlan: 'Management VLAN:', + sys_mgmt_untagged: 'untagged', + sys_mgmt_confirm: 'Move switch management to VLAN ', + sys_mgmt_warn: 'The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console. Continue?', sys_ip_note: '更新上述设置后,请使用新的 IP 地址重新访问管理界面:', sys_update: '更新设置', sys_save_label: '将当前全部设置保存到 Flash:', diff --git a/html/system.html b/html/system.html index 4b147c1..9c07fdc 100644 --- a/html/system.html +++ b/html/system.html @@ -32,7 +32,7 @@
-
+
@@ -46,6 +46,10 @@
+
+
+
+
diff --git a/html/system.js b/html/system.js index 0add17f..cb48a40 100644 --- a/html/system.js +++ b/html/system.js @@ -133,6 +133,7 @@ function fetchIP() { document.getElementById("gw").value=s.ip_gateway; document.getElementById("hostname").value=s.hostname; document.getElementById("model").textContent=s.hw_ver; + loadMgmtVlan(parseInt(s.mgmt_vlan, 10) || 0); clearInterval(systemInterval); // Fetch and populate the config textbox fetchConfig().then((configText) => { @@ -165,3 +166,44 @@ window.addEventListener("load", function() { if (langSel) langSel.value = rtlLang; systemInterval = setInterval(fetchIP, 1000); }); + + +var mgmtVlanCurrent = 0; + +/* Populate the management-VLAN picker from the configured VLANs. If management + * is untagged there is no VLAN to select, so show that as a disabled entry + * rather than inventing an id the switch would reject. */ +function loadMgmtVlan(cur) { + var sel = document.getElementById('mgmtvlan'); + if (!sel) return; + mgmtVlanCurrent = cur; + fetch('/vlanlist').then(function(r) { return r.json(); }).then(function(list) { + sel.innerHTML = ''; + if (!cur) { + var none = document.createElement('option'); + none.value = 0; none.disabled = true; + none.textContent = t('sys_mgmt_untagged'); + sel.appendChild(none); + } + for (var i = 0; i < list.length; i++) { + var o = document.createElement('option'); + o.value = list[i].id; + o.textContent = list[i].name ? (list[i].id + ' (' + list[i].name + ')') : list[i].id; + sel.appendChild(o); + } + sel.value = cur; + }).catch(function(err) { console.error('VLAN list failed:', err); }); +} + +function mgmtVlanChanged() { + var sel = document.getElementById('mgmtvlan'); + var id = parseInt(sel.value, 10); + if (!id || id === mgmtVlanCurrent) return; + if (!confirm(t('sys_mgmt_confirm') + id + '.\n\n' + t('sys_mgmt_warn'))) { + sel.value = mgmtVlanCurrent; + return; + } + fetch('/cmd', { method: 'POST', body: 'vlan ' + id + ' mgmt' }) + .then(function() { mgmtVlanCurrent = id; }) + .catch(function(err) { console.error('Set management VLAN failed:', err); sel.value = mgmtVlanCurrent; }); +} diff --git a/httpd/page_impl.c b/httpd/page_impl.c index cb9125b..c78c2e1 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -26,6 +26,7 @@ extern __code const struct machine machine; extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE]; extern __xdata uint16_t slen; +extern __xdata uint16_t management_vlan; extern __xdata uint16_t cont_len; extern __xdata uint32_t cont_addr; extern __code uint8_t * __code hex; @@ -264,7 +265,10 @@ void send_basic_info(void) slen += strtox(outbuf + slen, BUILD_DATE); slen += strtox(outbuf + slen, "\",\"hw_ver\":\""); slen += strtox(outbuf + slen, machine.machine_name); - slen += strtox(outbuf + slen, "\",\"flash_size\":\""); + /* VLAN carrying switch management, 0 = untagged. */ + slen += strtox(outbuf + slen, "\",\"mgmt_vlan\":"); + itoa16_html(management_vlan); + slen += strtox(outbuf + slen, ",\"flash_size\":\""); string_to_html(get_flash_size_str()); if (machine.n_sfp) { From e30976be763e2226c26205e33486f600a0465171 Mon Sep 17 00:00:00 2001 From: d00f Date: Sat, 8 Aug 2026 18:17:43 +0200 Subject: [PATCH 3/3] httpd: move the management VLAN into /vlanlist Review feedback on #306. The value used to ride in /information.json and the picker fetched /vlanlist separately, so the page needed both requests to mean anything. /vlanlist now answers {"mgmt":N,"vlan":[...]} and the picker reads both from the one response. Both consumers in vlan.js were taught the new shape, and /information.json no longer carries mgmt_vlan. The truncation guard now reserves 141 bytes instead of 139: the closing grew to two bytes with the wrapping object, and the comma in front of a non-first entry was never counted, so the worst case could land one byte past outbuf even before this change. char_to_html() does not check. The comments added on this branch are gone as well, style.css and system.js both, since these files are served byte for byte. page_impl.rel stays at DSEG 5, OSEG 0, BSEG 3 and the image reports the same 10183 bytes of XDATA before and after. --- html/style.css | 1 - html/system.js | 13 ++++++------- html/vlan.js | 4 ++-- httpd/page_impl.c | 12 ++++++------ 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/html/style.css b/html/style.css index 9bc8b5d..c03be99 100644 --- a/html/style.css +++ b/html/style.css @@ -82,7 +82,6 @@ object, img { .isNOK{ color: #900;} .isOK{ color: #090;} .ip{padding:8px 16px;margin-bottom: 1em;margin-left: 1em} -/* read-only values line up with the text inside the input fields */ .rotext{display:inline-block;padding:8px 16px;margin-bottom: 1em;margin-left: 1em} .row {display: flex;} .rcol {flex: 90%;} diff --git a/html/system.js b/html/system.js index cb48a40..f9fd37e 100644 --- a/html/system.js +++ b/html/system.js @@ -133,7 +133,7 @@ function fetchIP() { document.getElementById("gw").value=s.ip_gateway; document.getElementById("hostname").value=s.hostname; document.getElementById("model").textContent=s.hw_ver; - loadMgmtVlan(parseInt(s.mgmt_vlan, 10) || 0); + loadMgmtVlan(); clearInterval(systemInterval); // Fetch and populate the config textbox fetchConfig().then((configText) => { @@ -170,14 +170,13 @@ window.addEventListener("load", function() { var mgmtVlanCurrent = 0; -/* Populate the management-VLAN picker from the configured VLANs. If management - * is untagged there is no VLAN to select, so show that as a disabled entry - * rather than inventing an id the switch would reject. */ -function loadMgmtVlan(cur) { +function loadMgmtVlan() { var sel = document.getElementById('mgmtvlan'); if (!sel) return; - mgmtVlanCurrent = cur; - fetch('/vlanlist').then(function(r) { return r.json(); }).then(function(list) { + fetch('/vlanlist').then(function(r) { return r.json(); }).then(function(d) { + var cur = d.mgmt || 0; + var list = d.vlan || []; + mgmtVlanCurrent = cur; sel.innerHTML = ''; if (!cur) { var none = document.createElement('option'); diff --git a/html/vlan.js b/html/vlan.js index 8e18f27..2ae5efa 100644 --- a/html/vlan.js +++ b/html/vlan.js @@ -110,7 +110,7 @@ async function loadVlanTable() { var resp; try { resp = await fetch('/vlanlist'); } catch(e) { return; } if (!resp.ok) return; - var vlans = await resp.json(); + var vlans = (await resp.json()).vlan || []; for (var i = 0; i < vlans.length; i++) { var v = vlans[i]; var vresp; @@ -181,7 +181,7 @@ function loadVlanList() { sel.style.display = 'none'; return; } - var vlans = JSON.parse(this.responseText); + var vlans = JSON.parse(this.responseText).vlan || []; if (!vlans.length) { sel.style.display = 'none'; return; diff --git a/httpd/page_impl.c b/httpd/page_impl.c index c78c2e1..44332e5 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -265,10 +265,7 @@ void send_basic_info(void) slen += strtox(outbuf + slen, BUILD_DATE); slen += strtox(outbuf + slen, "\",\"hw_ver\":\""); slen += strtox(outbuf + slen, machine.machine_name); - /* VLAN carrying switch management, 0 = untagged. */ - slen += strtox(outbuf + slen, "\",\"mgmt_vlan\":"); - itoa16_html(management_vlan); - slen += strtox(outbuf + slen, ",\"flash_size\":\""); + slen += strtox(outbuf + slen, "\",\"flash_size\":\""); string_to_html(get_flash_size_str()); if (machine.n_sfp) { @@ -861,7 +858,9 @@ void send_vlanlist(void) uint8_t first = 1; slen = strtox(outbuf, HTTP_RESPONCE_JSON); - char_to_html('['); + slen += strtox(outbuf + slen, "{\"mgmt\":"); + itoa16_html(management_vlan); + slen += strtox(outbuf + slen, ",\"vlan\":["); for (i = 1; i < 4095; i++) { if (vlan_get(i) < 0) @@ -869,7 +868,7 @@ void send_vlanlist(void) if (!(sfr_data[0] & 0x02)) /* bit 1: VLAN table entry valid */ continue; - if (slen + 139 > TCP_OUTBUF_SIZE) /* 138 bytes worst-case entry + 1 byte for closing ']' */ + if (slen + 141 > TCP_OUTBUF_SIZE) /* comma + 138-byte worst-case entry + closing "]}" */ break; if (!first) @@ -891,4 +890,5 @@ void send_vlanlist(void) } char_to_html(']'); + char_to_html('}'); }