(rawStatus, isoTimestamp)
| 4139 | function _wifidefUpdateRunUI() { |
| 4140 | const scan = document.getElementById('wifidef-scan-btn'); |
| 4141 | const stop = document.getElementById('wifidef-stop-btn'); |
| 4142 | if (scan) scan.classList.toggle('hidden', _wifidef.continuous); |
| 4143 | if (stop) stop.classList.toggle('hidden', !_wifidef.continuous); |
| 4144 | } |
| 4145 | |
| 4146 | // Must match wifi_defense.py `_BUILD`. If the running service reports something |
| 4147 | // else, the webapp is executing an OLD wifi_defense module (service not restarted |
| 4148 | // after a git pull) — the #1 cause of "the fix didn't work in the web UI". |
| 4149 | const WIFIDEF_BUILD = '20260718-airtime-ssid'; |
| 4150 | |
| 4151 | function _wifidefFillIfaces() { |
| 4152 | fetch('/api/wifidef/interfaces').then(r => r.json()).then(d => { |
| 4153 | const sel = document.getElementById('wifidef-iface'); |
| 4154 | const ifs = (d && d.interfaces) || []; |
| 4155 | _wifidef.monitor = d.active_monitor || null; |
| 4156 | _wifidef.mode = d.mode || null; |
| 4157 | _wifidef.dedicated = !!d.dedicated; |
| 4158 | const st = document.getElementById('wifidef-status'); |
| 4159 | if (d.build && d.build !== WIFIDEF_BUILD && st) { |
| 4160 | st.innerHTML = '<span class="text-amber-400">⚠ WiFi Defense service is running old code (build ' |
| 4161 | + _esc(d.build) + ', page expects ' + WIFIDEF_BUILD |
| 4162 | + '). Restart it: <code>sudo systemctl restart ragnar</code></span>'; |
| 4163 | } |
| 4164 | if (!ifs.length) { sel.innerHTML = '<option value="">No wireless adapter</option>'; } |
| 4165 | else { |
| 4166 | sel.innerHTML = ifs.map(i => |
| 4167 | `<option value="${i.iface}"${i.monitor_capable ? '' : ' disabled'}>${i.iface}${i.monitor_capable ? '' : ' (no monitor)'}${i.is_monitor ? ' • MONITOR' : ''}</option>`).join(''); |
| 4168 | const cap = ifs.find(i => i.monitor_capable) || ifs[0]; |
| 4169 | // Never let the base selection be our own monitor vif; base_iface is |
| 4170 | // only trusted if it's actually in the (vif-excluded) adapter list. |
| 4171 | const baseOk = d.base_iface && ifs.some(i => i.iface === d.base_iface); |
| 4172 | if (!_wifidef.iface || !ifs.some(i => i.iface === _wifidef.iface && i.monitor_capable)) |
| 4173 | _wifidef.iface = (baseOk ? d.base_iface : cap.iface); |
| 4174 | sel.value = _wifidef.iface; |
| 4175 | sel.onchange = () => { _wifidef.iface = sel.value; }; |
| 4176 | } |
| 4177 | _wifidefUpdateMonBtn(); |
| 4178 | }).catch(() => {}); |
| 4179 | } |
| 4180 | |
| 4181 | function _wifidefUpdateMonBtn() { |
| 4182 | const btn = document.getElementById('wifidef-mon-btn'); |
| 4183 | if (!btn) return; |
| 4184 | // A boot-dedicated adapter is owned by the systemd unit, not the UI: it's |
| 4185 | // always in monitor mode by design, so don't offer a toggle that would hand |
| 4186 | // it back to NetworkManager and defeat the whole point. |
| 4187 | if (_wifidef.dedicated) { |
| 4188 | btn.textContent = 'Dedicated monitor (' + (_wifidef.monitor || '') + ') — boot-managed'; |
| 4189 | btn.classList.remove('text-red-300'); |
| 4190 | btn.disabled = true; |
| 4191 | btn.classList.add('opacity-60', 'cursor-not-allowed'); |
| 4192 | return; |
| 4193 | } |
| 4194 | btn.disabled = false; |
| 4195 | btn.classList.remove('opacity-60', 'cursor-not-allowed'); |
| 4196 | if (_wifidef.monitor) { |
| 4197 | btn.textContent = 'Disable monitor (' + _wifidef.monitor + ')'; |
| 4198 | btn.classList.add('text-red-300'); |
no test coverage detected