/** * 仪表盘页的数据整理。 * * 「拿到内核数据之后怎么摆」的纯逻辑,放在这里而不是 DashboardPage.vue 的 * computed 里,才能被 bun test 直接测到。流量窗口和每路吞吐的聚合在 * shared/telemetry.ts——那些是采集层的事,跨页面共用。 */ /** 内核运行模式在界面上怎么念。不在表里的原样显示,不猜它的意思。 */ export function modeLabel(mode: unknown): string { switch (mode) { case 'rule': return '规则' case 'global': return '全局' case 'direct': return '直连' default: return typeof mode === 'string' && mode ? mode : '—' } } /** 一个监听中的代理端口。name 用配置键原词,和路由页默认入站的命名一致。 */ export interface KernelPort { name: string port: number } const PORT_KEYS = ['port', 'socks-port', 'mixed-port', 'redir-port', 'tproxy-port'] as const /** 从内核 /configs 的运行时设置里挑出正在监听的代理端口。0 和缺省都表示没启用。 */ export function kernelPorts(configs: Record): KernelPort[] { const result: KernelPort[] = [] for (const key of PORT_KEYS) { const raw = configs[key] const port = typeof raw === 'number' ? raw : Number(raw) if (Number.isFinite(port) && port > 0) result.push({ name: key, port }) } return result } /** * 入站声明的端口在探测公网 IP 时用哪个数。 * 新式入站的端口可以写 "200,302" 或 "401-429",探测取第一个数字;解析不出返回 null。 */ export function firstPort(port: string): number | null { const match = /\d+/.exec(port) if (!match) return null const value = Number(match[0]) return value >= 1 && value <= 65535 ? value : null }