Full health check: fix startup crash, add subnet input to network discovery

Server fixes:
- database.js: auto-create data/ directory on startup — was crashing on
  fresh clone because the gitignored directory didn't exist
- better-sqlite3 upgraded to latest (13.x) for Node 26 compatibility

Network discovery:
- discoverNetwork() accepts optional subnet param; scans that range instead
  of auto-detecting when provided
- New GET /api/network-subnets endpoint returns detected local interfaces
  so UI can offer quick-select buttons
- Discovery modal: subnet text input + Scan button + auto-detect quick-select;
  local subnet buttons populate once the modal is open
- server/index.js: pass subnet from request body to discoverNetwork()

Infra:
- React client runs on PORT=3456 in dev to avoid port conflicts
- .claude/launch.json added for one-command preview via Express (serves
  the pre-built client, no port conflict with the preview tool)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-08-06 17:21:34 -04:00
1 parent 59078362da
commit f119168399
14 files changed
+119 -427

No files matched your search

+18 -9
View File
@@ -101,20 +101,29 @@ async function getArpHosts() {
});
}
async function discoverNetwork() {
const subnets = getLocalSubnets();
async function discoverNetwork(customSubnet = null) {
const arpHosts = await getArpHosts();
const arpIps = new Set(arpHosts.map(h => h.ip));
// Collect all IPs to scan: ARP table + subnet range
const allIps = new Map();
for (const h of arpHosts) allIps.set(h.ip, { ip: h.ip, hostname: h.hostname });
for (const subnet of subnets) {
if (!subnet.prefix) continue;
const range = subnetToRange(subnet.prefix);
for (const ip of range) {
if (!allIps.has(ip)) allIps.set(ip, { ip, hostname: null });
if (customSubnet) {
// Scan the user-specified subnet only (no ARP seeding, still probe all IPs in range)
const range = subnetToRange(customSubnet);
for (const ip of range) allIps.set(ip, { ip, hostname: null });
// Supplement with ARP hostnames for any IPs that happen to be in the ARP table
for (const h of arpHosts) {
if (allIps.has(h.ip) && h.hostname) allIps.get(h.ip).hostname = h.hostname;
}
} else {
// Auto-detect: ARP table + all local subnets
for (const h of arpHosts) allIps.set(h.ip, { ip: h.ip, hostname: h.hostname });
for (const subnet of getLocalSubnets()) {
if (!subnet.prefix) continue;
const range = subnetToRange(subnet.prefix);
for (const ip of range) {
if (!allIps.has(ip)) allIps.set(ip, { ip, hostname: null });
}
}
}