/** * 仪表盘页的数据整理。 * * 流量窗口、折线坐标、端口列表这些是「拿到内核数据之后怎么摆」的纯逻辑, * 放在这里而不是 DashboardPage.vue 的 computed 里,才能被 bun test 直接测到。 */ /** 流量曲线保留的秒数。接口每秒吐一条,所以窗口长度就是样本数。 */ export const WINDOW = 60 export interface TrafficSample { /** 当前每秒上行字节数 */ up: number /** 当前每秒下行字节数 */ down: number /** 累计上传字节数 */ upTotal: number /** 累计下载字节数 */ downTotal: number } /** 追加一条样本,窗口满了就把最旧的挤掉。 */ export function pushSample(samples: TrafficSample[], sample: TrafficSample): TrafficSample[] { return [...samples, sample].slice(-WINDOW) } /** * 两条曲线共用的纵轴上限。 * * 共用一个刻度,上行下行的差距才看得出来;各画各的刻度会让一条几乎不动的线 * 看起来像在剧烈波动。下限取 1 只为避免全 0 时除以 0。 */ export function chartPeak(samples: TrafficSample[]): number { let peak = 1 for (const s of samples) { if (s.up > peak) peak = s.up if (s.down > peak) peak = s.down } return peak } /** * 一条曲线的 SVG polyline 点串。0 映射到 height,peak 映射到 0。 * * 最新样本钉在右缘,窗口不满时曲线从左边空白处长出来,而不是把已有数据拉伸到 * 整个宽度——拉伸会让「刚开始采集」和「一直很闲」长得一样。 */ export function linePoints(values: number[], peak: number, width: number, height: number): string { const scale = Math.max(peak, 1) const step = width / (WINDOW - 1) return values .map((value, i) => { const x = width - (values.length - 1 - i) * step const y = height - (value / scale) * height return `${x.toFixed(1)},${y.toFixed(1)}` }) .join(' ') } /** 内核运行模式在界面上怎么念。不在表里的原样显示,不猜它的意思。 */ 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 }