diff --git a/backend/src/routes/network.ts b/backend/src/routes/network.ts index 4c6f031..52c88dc 100644 --- a/backend/src/routes/network.ts +++ b/backend/src/routes/network.ts @@ -1,9 +1,13 @@ // GET /api/network — best-effort discovery of LAN addresses other departments // can use to reach temetro, for the Settings "Network access" panel. // -// Caveat: inside Docker's default bridge network this sees the container's IPs, -// not the host's LAN IP, so the frontend prefers the address the browser is -// actually using (window.location) and treats this as a fallback/hint. +// Caveat: inside Docker's network this sees the container's bridge IP (e.g. +// 172.x), not the host's LAN IP. Surfacing that bogus address looked like an +// error in Settings, so when we detect we're in a container we return NO +// addresses — the frontend then prefers the address the browser is actually +// using (window.location) and otherwise shows a helpful "open via the server's +// IP" hint instead of an unreachable container IP. +import { existsSync } from "node:fs"; import { networkInterfaces } from "node:os"; import { Router } from "express"; @@ -19,16 +23,24 @@ function frontendPort(): number { } } +// True when running inside a container: the interface IPs are the container's +// bridge network, not the host's reachable LAN address. +function inContainer(): boolean { + return existsSync("/.dockerenv") || process.env.RUNNING_IN_DOCKER === "true"; +} + const router = Router(); router.get("/", (_req, res) => { const port = frontendPort(); const addresses: string[] = []; - for (const iface of Object.values(networkInterfaces())) { - for (const net of iface ?? []) { - // Node <18 reports family as "IPv4"; >=18 may report the number 4. - const isV4 = net.family === "IPv4" || (net.family as unknown) === 4; - if (isV4 && !net.internal) addresses.push(net.address); + if (!inContainer()) { + for (const iface of Object.values(networkInterfaces())) { + for (const net of iface ?? []) { + // Node <18 reports family as "IPv4"; >=18 may report the number 4. + const isV4 = net.family === "IPv4" || (net.family as unknown) === 4; + if (isV4 && !net.internal) addresses.push(net.address); + } } } res.json({ diff --git a/frontend/components/settings/settings-version.tsx b/frontend/components/settings/settings-version.tsx index 11c6117..205fd7e 100644 --- a/frontend/components/settings/settings-version.tsx +++ b/frontend/components/settings/settings-version.tsx @@ -66,7 +66,9 @@ export function VersionPanel() { useEffect(() => { if (localShareUrl) return; getNetworkInfo() - .then((n) => setNetworkUrls(n.urls)) + // Guard against an unexpected shape so a missing/garbled response shows the + // helpful "open via the server's IP" hint rather than a broken value. + .then((n) => setNetworkUrls(Array.isArray(n?.urls) ? n.urls : [])) .catch(() => setNetworkUrls([])); }, [localShareUrl]);