Add network identity diagnostics
Build and Deploy Gateway / build-and-push (push) Successful in 20s
Build and Deploy Gateway / deploy (push) Successful in 13s

This commit is contained in:
2026-08-11 16:19:23 +03:00
parent 3566f4bc0b
commit 6381760b27
8 changed files with 211 additions and 9 deletions
@@ -4,6 +4,7 @@ import net from 'node:net';
import {
assessConnectivity,
CONNECTIVITY_IP_SOURCES,
CONNECTIVITY_NETWORK_SOURCE,
CONNECTIVITY_SITES,
MAX_CUSTOM_DIAGNOSTIC_SERVICES,
} from '../../shared/connectivityDiagnostics.js';
@@ -66,6 +67,16 @@ interface IpProbeResult {
error: string | null;
}
interface NetworkProbeResult {
address: string | null;
asn: string | null;
provider: string | null;
city: string | null;
country: string | null;
attempts: number;
error: string | null;
}
interface SiteProbeResult {
id: string;
label: string;
@@ -80,6 +91,7 @@ interface SiteProbeResult {
}
type DiagnosticTarget =
| { kind: 'network' }
| { kind: 'ip'; probe: IpProbe }
| { kind: 'site'; probe: SiteProbe };
@@ -261,6 +273,55 @@ async function publicIps(path: PathKind, proxyPort: number, execute: CurlExecuto
};
}
function text(value: unknown) {
return typeof value === 'string' && value.trim() ? value.trim() : null;
}
function parseNetwork(body: string): Omit<NetworkProbeResult, 'attempts' | 'error'> | null {
try {
const value = record(JSON.parse(body));
const connection = record(value.connection);
const address = text(value.ip);
if (value.success === false || !address || net.isIP(address) !== 4) return null;
const number = Number(connection.asn);
return {
address,
asn: Number.isSafeInteger(number) && number > 0 ? `AS${number}` : null,
provider: text(connection.isp) || text(connection.org),
city: text(value.city),
country: text(value.country_code) || text(value.country),
};
} catch {
return null;
}
}
async function networkProbe(
path: PathKind,
proxyPort: number,
execute: CurlExecutor,
sampleCount = 1,
): Promise<NetworkProbeResult> {
const samples: Array<RequestResult & { network: ReturnType<typeof parseNetwork> }> = [];
for (let attempt = 0; attempt < sampleCount; attempt += 1) {
const result = await request(CONNECTIVITY_NETWORK_SOURCE, path, proxyPort, execute, { body: true, ipv4: true });
samples.push({ ...result, network: result.ok ? parseNetwork(result.body) : null });
}
const selected = mostCommon(samples
.map(({ network }) => network && JSON.stringify(network))
.filter((value): value is string => Boolean(value)));
const network = selected ? JSON.parse(selected) as ReturnType<typeof parseNetwork> : null;
return {
address: network?.address || null,
asn: network?.asn || null,
provider: network?.provider || null,
city: network?.city || null,
country: network?.country || null,
attempts: samples.length,
error: network ? null : samples.at(-1)?.error || 'invalid network response',
};
}
function isPublicAddress(address: string, family: number) {
const type = family === 4 ? 'ipv4' : family === 6 ? 'ipv6' : '';
const blocked = family === 4 ? BLOCKED_IPV4_ADDRESSES : BLOCKED_IPV6_ADDRESSES;
@@ -361,7 +422,8 @@ async function probePath(
execute: CurlExecutor,
sites: SiteProbe[],
): Promise<ConnectivityPathResult> {
const [ip, siteResults] = await Promise.all([
const [network, ip, siteResults] = await Promise.all([
networkProbe(path, proxyPort, execute),
publicIps(path, proxyPort, execute),
Promise.all(sites.map((probe) => siteProbe(probe, path, proxyPort, execute))),
]);
@@ -370,6 +432,7 @@ async function probePath(
internetAvailable: Boolean(
ip.ipv4.addresses.length || ip.ipv6 || siteResults.some((site) => site.status !== 'unavailable'),
),
network,
...ip,
sites: siteResults,
};
@@ -389,6 +452,7 @@ function unavailablePath(): ConnectivityPathResult {
function resolveTarget(targetId: unknown, sites: SiteProbe[]): DiagnosticTarget | null {
if (typeof targetId !== 'string') return null;
if (targetId === CONNECTIVITY_NETWORK_SOURCE.id) return { kind: 'network' };
if (targetId.startsWith('ip:')) {
const probe = IP_PROBES.find(({ id }) => id === targetId.slice(3));
return probe ? { kind: 'ip', probe } : null;
@@ -406,6 +470,9 @@ async function probeTarget(
proxyPort: number,
execute: CurlExecutor,
): Promise<ConnectivityPathResult> {
const network = target.kind === 'network'
? await networkProbe(path, proxyPort, execute, TARGET_SAMPLE_COUNT)
: null;
const ip = target.kind === 'ip'
? await ipProbe(target.probe, path, proxyPort, execute, TARGET_SAMPLE_COUNT)
: null;
@@ -417,7 +484,8 @@ async function probeTarget(
const sites = site ? [site] : [];
return {
available: true,
internetAvailable: Boolean(ip?.address || (site && site.status !== 'unavailable')),
internetAvailable: Boolean(network?.address || ip?.address || (site && site.status !== 'unavailable')),
...(network ? { network } : {}),
ipv4: {
addresses: ipv4Sources.map(({ address }) => address).filter((value): value is string => Boolean(value)),
sources: ipv4Sources,