website access
This commit is contained in:
@@ -41,6 +41,10 @@ address, not the address to put in a browser. Confirm the in-app status reads
|
|||||||
**Validated sample aggregates**, then stop the server with `Ctrl-C` after the
|
**Validated sample aggregates**, then stop the server with `Ctrl-C` after the
|
||||||
demo.
|
demo.
|
||||||
|
|
||||||
|
If the host is on a Tailscale tailnet, the server also prints a **Tailscale
|
||||||
|
(tunnel)** URL (a `100.x.y.z` address). Open that URL on any other device signed
|
||||||
|
into the same tailnet to reach the dashboard remotely, without sharing a LAN.
|
||||||
|
|
||||||
All data-contract and checksum validation remains enabled over LAN HTTP. When a
|
All data-contract and checksum validation remains enabled over LAN HTTP. When a
|
||||||
remote browser does not expose the SubtleCrypto digest API to the plain-HTTP
|
remote browser does not expose the SubtleCrypto digest API to the plain-HTTP
|
||||||
page, the dashboard uses its dependency-free SHA-256 implementation and still
|
page, the dashboard uses its dependency-free SHA-256 implementation and still
|
||||||
|
|||||||
@@ -52,12 +52,13 @@ function safeFile(requestUrl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tailscale and similar VPN tunnels show up as extra "LAN" interfaces but are not
|
// Tailscale advertises the host over its tailnet on a 100.64.0.0/10 address, so
|
||||||
// useful for an in-room demo, so keep them out of the printed URL list.
|
// label that URL clearly for remote (off-LAN) demos over the tunnel.
|
||||||
function isTunnelInterface(interfaceName, address) {
|
function interfaceLabel(interfaceName, address) {
|
||||||
if (/^(utun|tailscale|tun|ppp|ipsec)/i.test(interfaceName)) return true;
|
|
||||||
const [first, second] = address.split(".").map(Number);
|
const [first, second] = address.split(".").map(Number);
|
||||||
return first === 100 && second >= 64 && second <= 127; // 100.64.0.0/10 (CGNAT/tailnet)
|
const isTailnet =
|
||||||
|
/tailscale/i.test(interfaceName) || (first === 100 && second >= 64 && second <= 127);
|
||||||
|
return isTailnet ? "Tailscale (tunnel)" : `Other devices (${interfaceName})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayUrls(host, port) {
|
function displayUrls(host, port) {
|
||||||
@@ -72,10 +73,9 @@ function displayUrls(host, port) {
|
|||||||
for (const [interfaceName, addresses] of Object.entries(networkInterfaces())) {
|
for (const [interfaceName, addresses] of Object.entries(networkInterfaces())) {
|
||||||
for (const address of addresses || []) {
|
for (const address of addresses || []) {
|
||||||
if (address.family !== "IPv4" || address.internal || seen.has(address.address)) continue;
|
if (address.family !== "IPv4" || address.internal || seen.has(address.address)) continue;
|
||||||
if (isTunnelInterface(interfaceName, address.address)) continue;
|
|
||||||
seen.add(address.address);
|
seen.add(address.address);
|
||||||
entries.push({
|
entries.push({
|
||||||
label: `Other devices (${interfaceName})`,
|
label: interfaceLabel(interfaceName, address.address),
|
||||||
url: `http://${address.address}:${port}`,
|
url: `http://${address.address}:${port}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user