Diagnose network button: raw TCP probe matrix
The Komodo probe answers a specific question (can we resolve a port from Komodo's API), but leaves ambiguity when it fails: is DNS broken, is the route blocked, is the target port unreachable, or is the target refusing? A 10-second timeout looks the same for all of those. Adds a second button, "diagnose network", that fires parallel raw TCP connect attempts (no HTTP, no auth, no bytes sent) at a fixed matrix of candidate hosts against the configured Komodo port: - 127.0.0.1 (sanity — container loopback) - host.docker.internal (bridge-gateway alias) - 172.17.0.1 (raw default bridge gateway) - the KOMODO_ADDRESS host (if different) - RCON_HOST (if different) Each row shows resolved IP, outcome (connected/refused/timeout/dns-fail/ unreachable/error), elapsed ms, and detail. Together they pinpoint which network layer the traffic dies at without guessing. New helper: src/lib/server/integrations/netProbe.ts — DNS lookup + net. createConnection with explicit timeout, classifying errors by errno (ECONNREFUSED, EHOSTUNREACH, ENOTFOUND). Runs trials in parallel so the whole diagnostic completes in one probe-timeout budget. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getServer } from '$lib/server/fleet/servers.js';
|
||||
import { config } from '$lib/server/config.js';
|
||||
import { tcpProbeAll } from '$lib/server/integrations/netProbe.js';
|
||||
|
||||
/**
|
||||
* Multi-target TCP reachability probe. Fires parallel connect attempts at a
|
||||
* fixed matrix of candidate hosts + the configured KOMODO_ADDRESS host, so
|
||||
* we can see at a glance which network paths reach anything and where
|
||||
* packets die (DNS, routing, or refused-at-port).
|
||||
*/
|
||||
export const POST: RequestHandler = async ({ params }) => {
|
||||
const name = params.name;
|
||||
if (!name) return json({ ok: false, error: 'missing server name' }, { status: 400 });
|
||||
|
||||
const row = getServer(name);
|
||||
if (!row) return json({ ok: false, error: `unknown server: ${name}` }, { status: 404 });
|
||||
|
||||
// Parse the configured Komodo URL to extract host+port.
|
||||
let komodoHost: string | null = null;
|
||||
let komodoPort: number | null = null;
|
||||
if (config.komodo.address) {
|
||||
try {
|
||||
const u = new URL(config.komodo.address);
|
||||
komodoHost = u.hostname;
|
||||
komodoPort = u.port ? Number.parseInt(u.port, 10) : u.protocol === 'https:' ? 443 : 80;
|
||||
} catch {
|
||||
/* leave null */
|
||||
}
|
||||
}
|
||||
|
||||
const probePort = komodoPort ?? 9120;
|
||||
|
||||
// Build the trial matrix. Every trial hits the same target port (the
|
||||
// Komodo port) via a different network path, so we can see which paths
|
||||
// reach the same service.
|
||||
const trials: Array<{ label: string; host: string; port: number }> = [
|
||||
{ label: `127.0.0.1:${probePort} (container loopback)`, host: '127.0.0.1', port: probePort },
|
||||
{ label: `host.docker.internal:${probePort} (bridge gateway alias)`, host: 'host.docker.internal', port: probePort },
|
||||
{ label: `172.17.0.1:${probePort} (default bridge gateway)`, host: '172.17.0.1', port: probePort }
|
||||
];
|
||||
|
||||
if (komodoHost && !trials.some((t) => t.host === komodoHost)) {
|
||||
trials.push({
|
||||
label: `${komodoHost}:${probePort} (KOMODO_ADDRESS)`,
|
||||
host: komodoHost,
|
||||
port: probePort
|
||||
});
|
||||
}
|
||||
|
||||
// RCON's default host is worth testing too — even though RCON is UDP,
|
||||
// if TCP to the same host also fails, it's a routing/firewall issue,
|
||||
// not a UDP-specific one.
|
||||
const rconHost = row.rcon_host ?? config.rcon.host;
|
||||
if (rconHost && !trials.some((t) => t.host === rconHost)) {
|
||||
trials.push({
|
||||
label: `${rconHost}:${probePort} (RCON_HOST)`,
|
||||
host: rconHost,
|
||||
port: probePort
|
||||
});
|
||||
}
|
||||
|
||||
const results = await tcpProbeAll(trials, config.httpTimeoutMs);
|
||||
|
||||
return json({
|
||||
ok: true,
|
||||
config: {
|
||||
komodoAddress: config.komodo.address || null,
|
||||
komodoHost,
|
||||
komodoPort,
|
||||
rconHost,
|
||||
probeTimeoutMs: config.httpTimeoutMs
|
||||
},
|
||||
results
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user