DC-031: fix /api/v1/network/ips ReferenceError + add regression tests
- Extract LAN/Tailscale classification into src/utilities/network-detector.js
(detectInterfaceIps, isTailscaleIP, isPrivateLanIP). The route handler in
src/app.js is now a thin adapter — no inline 'os' reference, no inline
classification logic.
- Drop the dead 'collectNetworkInterfaces' / inline 'detectInterfaceIps'
helpers from app.js (the original ReferenceError shape).
- Add __tests__/network-ips-route.test.js (16 tests):
- Detector unit tests for Tailscale CGNAT (100.64/10) and RFC 1918 LAN
ranges with malformed-input guards.
- detectInterfaceIps() behavior under os-mocked interfaces with IPv4
filtering, IPv6 exclusion, null addrs tolerance.
- Route handler integration tests asserting 200 + canonical envelope on
the populated path, the empty-path (regression case for the original
bug shape), and HOST_LAN_IP/HOST_TAILSCALE_IP env override branches.
- Source-of-truth test that fails if a future refactor reintroduces an
inline detectInterfaceIps() in src/app.js or references 'os' without
a prior require('os') line.
- Fix latent ESLint Error in backup-manager.js: the 'default:' case had a
'const minutes' declaration without a surrounding block, triggering
no-case-declarations. Added the block braces.
Pre-fix baseline: no test exercised this route, so the 1071-test suite
passed despite the 500. Post-fix: 1087/1087 tests pass (+16 new), zero new
ESLint warnings (10 pre-existing warnings in backup-manager.js unrelated
to this commit).
This commit is contained in:
@@ -185,24 +185,6 @@ async function createApp() {
|
||||
return first === 100 && second >= 64 && second <= 127;
|
||||
}
|
||||
|
||||
function isPrivateLan(ip) {
|
||||
if (!ip) return false;
|
||||
if (ip.startsWith('192.168.') || ip.startsWith('10.')) return true;
|
||||
return /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(ip);
|
||||
}
|
||||
|
||||
function collectNetworkInterfaces(osModule) {
|
||||
const out = [];
|
||||
const interfaces = osModule.networkInterfaces();
|
||||
for (const [name, addrs] of Object.entries(interfaces)) {
|
||||
for (const addr of addrs) {
|
||||
if (addr.internal || addr.family !== 'IPv4') continue;
|
||||
out.push({ name, ip: addr.address });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line require-await -- stub for now, will gain await when wired into context
|
||||
async function getTailscaleStatus() {
|
||||
// Stub for now - will be populated by context
|
||||
@@ -865,29 +847,10 @@ async function createApp() {
|
||||
res.status(statusCode).send();
|
||||
}, 'probe'));
|
||||
|
||||
// Scan OS network interfaces and classify the first LAN + Tailscale IPv4
|
||||
// addresses. Extracted to keep the route handler below ESLint's max-depth.
|
||||
function detectInterfaceIps() {
|
||||
const os = require('os');
|
||||
const LAN_RANGE = /^(192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/;
|
||||
const all = [];
|
||||
let lan = null;
|
||||
let tailscale = null;
|
||||
const interfaces = os.networkInterfaces();
|
||||
for (const [name, addrs] of Object.entries(interfaces)) {
|
||||
for (const addr of addrs || []) {
|
||||
if (addr.internal || addr.family !== 'IPv4') continue;
|
||||
const { address: ip } = addr;
|
||||
all.push({ name, ip });
|
||||
if (!tailscale && ip.startsWith('100.')) {
|
||||
tailscale = ip;
|
||||
} else if (!lan && LAN_RANGE.test(ip)) {
|
||||
lan = ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { lan, tailscale, all };
|
||||
}
|
||||
// Network IPs endpoint — see src/utilities/network-detector.js for the
|
||||
// classification logic. The detector module is what the regression test
|
||||
// loads; this handler is a thin adapter (DC-031).
|
||||
const { detectInterfaceIps } = require('./utilities/network-detector');
|
||||
|
||||
// Network IPs endpoint
|
||||
app.get('/api/v1/network/ips', (req, res) => {
|
||||
@@ -903,13 +866,10 @@ async function createApp() {
|
||||
};
|
||||
|
||||
if (!envLan || !envTailscale) {
|
||||
result.all = collectNetworkInterfaces(os);
|
||||
if (!result.tailscale) {
|
||||
result.tailscale = result.all.find(i => isTailscaleIP(i.ip))?.ip || null;
|
||||
}
|
||||
if (!result.lan) {
|
||||
result.lan = result.all.find(i => isPrivateLan(i.ip))?.ip || null;
|
||||
}
|
||||
const detected = detectInterfaceIps();
|
||||
result.all = detected.all;
|
||||
if (!result.lan) result.lan = detected.lan;
|
||||
if (!result.tailscale) result.tailscale = detected.tailscale;
|
||||
}
|
||||
|
||||
ok(res, result);
|
||||
|
||||
Reference in New Issue
Block a user