|
| 1 | +import { regionName } from "./regions"; |
| 2 | + |
| 3 | +import type { NetcheckConnectivity, NetcheckReport } from "@repo/shared"; |
| 4 | + |
| 5 | +/** Maps to a `tone-*` CSS class so color lives in the stylesheet, not here. */ |
| 6 | +export type Tone = "good" | "bad" | "warn" | "neutral"; |
| 7 | + |
| 8 | +export interface ConnectivityItem { |
| 9 | + label: string; |
| 10 | + value: string; |
| 11 | + tone: Tone; |
| 12 | +} |
| 13 | + |
| 14 | +type Outcome = [value: string, tone: Tone]; |
| 15 | + |
| 16 | +/** Connectivity facts derived from the embedded tailscale netcheck probe. */ |
| 17 | +export function buildConnectivityItems( |
| 18 | + report: NetcheckReport, |
| 19 | +): ConnectivityItem[] { |
| 20 | + const probe = report.derp.netcheck; |
| 21 | + if (!probe) { |
| 22 | + return []; |
| 23 | + } |
| 24 | + |
| 25 | + // Tones: bad = real problem, warn = works but suboptimal, neutral = optional. |
| 26 | + const items: ConnectivityItem[] = [ |
| 27 | + boolItem("UDP", probe.UDP, { |
| 28 | + true: ["Reachable", "good"], |
| 29 | + false: ["Blocked", "bad"], |
| 30 | + }), |
| 31 | + boolItem("IPv4", probe.IPv4, { |
| 32 | + true: ["Yes", "good"], |
| 33 | + false: ["No", "warn"], |
| 34 | + }), |
| 35 | + boolItem("IPv6", probe.IPv6, { |
| 36 | + true: ["Yes", "good"], |
| 37 | + false: ["No", "neutral"], |
| 38 | + }), |
| 39 | + boolItem("NAT mapping", probe.MappingVariesByDestIP, { |
| 40 | + true: ["Varies by destination (hard NAT)", "warn"], |
| 41 | + false: ["Consistent (easy NAT)", "good"], |
| 42 | + }), |
| 43 | + boolItem("Hairpinning", probe.HairPinning, { |
| 44 | + true: ["Supported", "good"], |
| 45 | + false: ["Not supported", "neutral"], |
| 46 | + }), |
| 47 | + portMappingItem(probe), |
| 48 | + ]; |
| 49 | + |
| 50 | + const preferred = preferredRegionName(report); |
| 51 | + if (preferred) { |
| 52 | + items.push({ label: "Preferred relay", value: preferred, tone: "good" }); |
| 53 | + } |
| 54 | + return items; |
| 55 | +} |
| 56 | + |
| 57 | +/** Renders a boolean probe field; a missing value is a neutral "Unknown". */ |
| 58 | +function boolItem( |
| 59 | + label: string, |
| 60 | + state: boolean | null | undefined, |
| 61 | + cases: { true: Outcome; false: Outcome }, |
| 62 | +): ConnectivityItem { |
| 63 | + if (typeof state !== "boolean") { |
| 64 | + return { label, value: "Unknown", tone: "neutral" }; |
| 65 | + } |
| 66 | + const [value, tone] = state ? cases.true : cases.false; |
| 67 | + return { label, value, tone }; |
| 68 | +} |
| 69 | + |
| 70 | +function portMappingItem(probe: NetcheckConnectivity): ConnectivityItem { |
| 71 | + const fields = [ |
| 72 | + [probe.UPnP, "UPnP"], |
| 73 | + [probe.PMP, "NAT-PMP"], |
| 74 | + [probe.PCP, "PCP"], |
| 75 | + ] as const; |
| 76 | + const detected = fields.filter(([on]) => on).map(([, name]) => name); |
| 77 | + if (detected.length > 0) { |
| 78 | + return { label: "Port mapping", value: detected.join(", "), tone: "good" }; |
| 79 | + } |
| 80 | + // A null field means "could not determine", so report "None detected" only |
| 81 | + // once a protocol was actually probed. |
| 82 | + const probed = fields.some(([on]) => typeof on === "boolean"); |
| 83 | + return { |
| 84 | + label: "Port mapping", |
| 85 | + value: probed ? "None detected" : "Unknown", |
| 86 | + tone: "neutral", |
| 87 | + }; |
| 88 | +} |
| 89 | + |
| 90 | +function preferredRegionName(report: NetcheckReport): string | undefined { |
| 91 | + const id = report.derp.netcheck?.PreferredDERP; |
| 92 | + if (!id) { |
| 93 | + return undefined; |
| 94 | + } |
| 95 | + return regionName(report.derp.regions[String(id)], id); |
| 96 | +} |
0 commit comments