|
| 1 | +export type NetworkSnapshotInsight = { |
| 2 | + label: string; |
| 3 | + value: string; |
| 4 | + detail: string; |
| 5 | + tone: "ok" | "warn" | "info"; |
| 6 | +}; |
| 7 | + |
| 8 | +export function buildNetworkSnapshotInsights(text: string): NetworkSnapshotInsight[] { |
| 9 | + const lower = text.toLowerCase(); |
| 10 | + const interfaces = collectInterfaceNames(text); |
| 11 | + const egress = interfaces.filter((name) => /^(wlan|wlp|enp|rmnet|ccmni|eth|usb|rndis|pdp|ap|swlan|wwan|cell|p2p|ppp)/i.test(name)); |
| 12 | + const hasTun = interfaces.some((name) => /^(tun|utun|magicnet)/i.test(name)) || /\btun\b/i.test(text); |
| 13 | + const hasPolicyRule = hasSnapshotLine(text, /\bip rule:|from all fwmark\b|lookup \d+\b|lookup main\b/i); |
| 14 | + const hasNat = hasSnapshotLine(text, /\bmasquerade\b|\bsnat\b|\bdnat\b|-t nat\b|chain postrouting\b/i); |
| 15 | + const hasDnsRedirect = hasSnapshotLine(text, /\b(dpt:53|--dport 53|udp dpt:domain|tcp dpt:domain|redirect\b.*:53|to-ports (?:53|1053))\b/i); |
| 16 | + return [ |
| 17 | + { |
| 18 | + label: "TUN", |
| 19 | + value: hasTun ? "detected" : "not detected", |
| 20 | + detail: hasTun ? "快照中出现 TUN/MagicNet 接口线索。" : "未看到 TUN 接口线索,透明代理可能未生效或快照不完整。", |
| 21 | + tone: hasTun ? "ok" : "warn" |
| 22 | + }, |
| 23 | + { |
| 24 | + label: "出口接口", |
| 25 | + value: egress.length ? egress.slice(0, 4).join(", ") : "unknown", |
| 26 | + detail: egress.length ? "检测到常见出口接口命名线索。" : "未识别常见出口接口命名线索。", |
| 27 | + tone: egress.length ? "info" : "warn" |
| 28 | + }, |
| 29 | + { |
| 30 | + label: "策略路由", |
| 31 | + value: hasPolicyRule ? "detected" : "not detected", |
| 32 | + detail: hasPolicyRule ? "快照文本包含 policy routing 线索。" : "未看到明显 ip rule/policy route 线索。", |
| 33 | + tone: hasPolicyRule ? "ok" : "warn" |
| 34 | + }, |
| 35 | + { |
| 36 | + label: "NAT", |
| 37 | + value: hasNat ? "detected" : "not detected", |
| 38 | + detail: hasNat ? "快照文本包含 NAT/MASQUERADE 线索。" : "未看到 NAT 线索,热点转发或共享网络需继续确认。", |
| 39 | + tone: hasNat ? "ok" : "info" |
| 40 | + }, |
| 41 | + { |
| 42 | + label: "DNS 捕获", |
| 43 | + value: hasDnsRedirect ? "detected" : "not detected", |
| 44 | + detail: hasDnsRedirect ? "快照文本包含 53 端口 redirect 线索。" : "未看到 DNS redirect 线索,可结合 DNS 工具继续测试。", |
| 45 | + tone: hasDnsRedirect ? "ok" : "info" |
| 46 | + }, |
| 47 | + { |
| 48 | + label: "规模", |
| 49 | + value: `${interfaces.length} interfaces`, |
| 50 | + detail: `${text.split(/\r?\n/).filter((line) => line.trim()).length} 行有效快照文本。`, |
| 51 | + tone: lower.includes("error") || lower.includes("failed") ? "warn" : "info" |
| 52 | + } |
| 53 | + ]; |
| 54 | +} |
| 55 | + |
| 56 | +export function formatNetworkSnapshotReport(text: string, insights: NetworkSnapshotInsight[]): string { |
| 57 | + const nonEmptyLines = text.split(/\r?\n/).filter((line) => line.trim()).length; |
| 58 | + return [ |
| 59 | + "MagicNet network snapshot", |
| 60 | + `snapshot_lines=${nonEmptyLines}`, |
| 61 | + "raw_snapshot=omitted", |
| 62 | + ...insights.map((item) => `${item.label}=${item.value} tone=${item.tone} detail=${item.detail}`), |
| 63 | + ].join("\n").trim(); |
| 64 | +} |
| 65 | + |
| 66 | +export function networkInsightTone(tone: NetworkSnapshotInsight["tone"]): string { |
| 67 | + if (tone === "ok") return "border-lime-400/20 bg-lime-400/10 text-lime-100"; |
| 68 | + if (tone === "warn") return "border-amber-400/30 bg-amber-400/10 text-amber-100"; |
| 69 | + return "border-zinc-800 bg-zinc-950 text-zinc-300"; |
| 70 | +} |
| 71 | + |
| 72 | +function collectInterfaceNames(text: string): string[] { |
| 73 | + const names = text.match(/\b(?:wlan|wlp|enp|rmnet|ccmni|eth|usb|rndis|pdp|ap|swlan|wwan|cell|p2p|ppp|veth|br|bridge|tun|utun|lo|magicnet)[\w.:-]*/gi) || []; |
| 74 | + return Array.from(new Set(names.map((name) => name.replace(/:$/, "")))); |
| 75 | +} |
| 76 | + |
| 77 | +function hasSnapshotLine(text: string, pattern: RegExp): boolean { |
| 78 | + return text.split(/\r?\n/).some((line) => pattern.test(line)); |
| 79 | +} |
0 commit comments