()
| 5859 | // the LAN path can be tested even when WiFi carries the default route. |
| 5860 | const sel = document.getElementById('vpn-egress-iface'); |
| 5861 | if (sel) { |
| 5862 | fetchAPI('/api/net/interfaces').then(x => { |
| 5863 | (x.interfaces || []).forEach(i => { |
| 5864 | const o = document.createElement('option'); |
| 5865 | o.value = i.name; |
| 5866 | o.textContent = i.name + (i.type && i.type !== 'ethernet' ? ' (' + i.type + ')' : ''); |
| 5867 | sel.appendChild(o); |
| 5868 | }); |
| 5869 | // A scoped identity view should test the same interface's egress. |
| 5870 | if (d.interface) sel.value = d.interface; |
| 5871 | }).catch(() => {}); |
| 5872 | } |
| 5873 | } catch (e) { |
| 5874 | out.innerHTML = '<p class="text-red-400">Failed: ' + escapeHtml(e.message) + '</p>'; |
| 5875 | } |
| 5876 | } |
| 5877 | |
| 5878 | async function checkVpnEgress() { |
| 5879 | const cell = document.getElementById('vpn-egress-result'); |
| 5880 | if (!cell) return; |
| 5881 | const sel = document.getElementById('vpn-egress-iface'); |
| 5882 | const iface = sel && sel.value ? sel.value : ''; |
| 5883 | const label = iface ? 'via ' + escapeHtml(iface) : 'default route'; |
| 5884 | cell.innerHTML = '<span class="text-gray-400">checking egress ' + label + '… (public IP, known-VPN IP list, Tor exit)</span>'; |
| 5885 | try { |
| 5886 | const d = await fetchAPI('/api/net/vpn-check' + (iface ? '?interface=' + encodeURIComponent(iface) : '')); |
| 5887 | const ifNote = d.interface ? ' <span class="text-gray-500">[' + escapeHtml(d.interface) + ']</span>' : ''; |
| 5888 | const why = (d.reasons && d.reasons.length) |
| 5889 | ? ' <span class="text-gray-500">(' + d.reasons.map(escapeHtml).join('; ') + ')</span>' : ''; |
| 5890 | if (d.verdict === 'vpn') { |
| 5891 | cell.innerHTML = '<span class="text-amber-300">yes</span>' + ifNote + why; |
| 5892 | } else if (d.verdict === 'likely') { |
| 5893 | cell.innerHTML = '<span class="text-amber-300">likely</span>' + ifNote + why; |
| 5894 | } else if (d.verdict === 'no') { |
| 5895 | const via = [d.isp, d.public_ip].filter(Boolean).map(escapeHtml).join(' · '); |
| 5896 | const note = d.ip_list_checked ? '; egress IP not in the known-VPN list' : ''; |
| 5897 | cell.innerHTML = '<span class="text-gray-400">no</span>' + ifNote |
| 5898 | + (via ? ' <span class="text-gray-500">(egress via ' + via + note + ')</span>' : ''); |
| 5899 | } else { |
| 5900 | cell.innerHTML = '<span class="text-gray-500">unknown</span>' + ifNote + why; |
| 5901 | } |
| 5902 | } catch (e) { |
| 5903 | cell.innerHTML = '<span class="text-red-400">check failed: ' + escapeHtml(e.message) + '</span>'; |
no test coverage detected