This commit is contained in:
eric
2026-05-19 06:47:07 +00:00
parent e1d7251545
commit 78e1308063
63 changed files with 2527 additions and 186 deletions

View File

@@ -118,9 +118,17 @@ type HubDashboard = {
tx_bytes?: number;
rx_packets?: number;
tx_packets?: number;
rx_bps?: number;
tx_bps?: number;
sample_interval_seconds?: number;
speed_mbps?: number | null;
operstate?: string;
}>;
total_rx_bytes?: number;
total_tx_bytes?: number;
total_rx_bps?: number;
total_tx_bps?: number;
sample_interval_seconds?: number;
};
live_events?: {
pm2_tail?: string[];
@@ -221,6 +229,11 @@ function formatBytes(value?: number) {
return `${n.toFixed(n >= 10 || i === 0 ? 0 : 1)} ${u[i]}`;
}
function formatRate(value?: number) {
const text = formatBytes(value);
return text === "—" ? text : `${text}/s`;
}
function statusPill(status: string) {
if (status === "online" || status === "running")
return "bg-emerald-500/15 text-emerald-300 ring-1 ring-emerald-500/30";
@@ -1181,11 +1194,26 @@ export default function LiveControlApp() {
<div className="rounded-2xl border border-cyan-500/15 bg-gradient-to-br from-cyan-500/[0.07] to-transparent p-5">
<h3 className="flex items-center gap-2 text-sm font-semibold text-white">
<Wifi className="h-4 w-4 text-cyan-300" />
{t("网络流量(累计)", "Network I/O (cumulative)")}
{t("网络带宽 / 流量", "Network bandwidth / traffic")}
</h3>
<p className="mt-1 text-[11px] text-zinc-500">
{t("自开机以来各接口收发字节;非实时速率。", "Per-interface RX/TX since boot (not a live bitrate).")}
{t(
"实时速率由总览自动刷新间隔计算;累计值来自系统网卡计数器。",
"Live rates are calculated from dashboard refresh deltas; totals come from kernel counters.",
)}
</p>
<div className="mt-4 grid gap-3 sm:grid-cols-2">
<div className="rounded-xl border border-white/[0.06] bg-black/25 px-4 py-3 font-mono text-[11px]">
<p className="text-[10px] uppercase tracking-[0.08em] text-cyan-300/70">{t("实时下行", "Live down")}</p>
<p className="mt-1 text-lg font-semibold text-cyan-100">{formatRate(dash.network.total_rx_bps)}</p>
<p className="mt-1 text-zinc-500">{t("累计", "Total")}: {formatBytes(dash.network.total_rx_bytes)}</p>
</div>
<div className="rounded-xl border border-white/[0.06] bg-black/25 px-4 py-3 font-mono text-[11px]">
<p className="text-[10px] uppercase tracking-[0.08em] text-cyan-300/70">{t("实时上行", "Live up")}</p>
<p className="mt-1 text-lg font-semibold text-cyan-100">{formatRate(dash.network.total_tx_bps)}</p>
<p className="mt-1 text-zinc-500">{t("累计", "Total")}: {formatBytes(dash.network.total_tx_bytes)}</p>
</div>
</div>
<div className="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{(dash.network.interfaces || []).slice(0, 6).map((iface) => (
<div
@@ -1198,11 +1226,13 @@ export default function LiveControlApp() {
{iface.speed_mbps ? `${iface.speed_mbps} Mbps` : t("未知速率", "unknown speed")}
</p>
<p className="mt-2 text-zinc-500">
↓ {formatBytes(iface.rx_bytes)} <span className="text-zinc-600">·</span>{" "}
↓ {formatRate(iface.rx_bps)} <span className="text-zinc-600">·</span>{" "}
{formatBytes(iface.rx_bytes)} <span className="text-zinc-600">·</span>{" "}
{iface.rx_packets?.toLocaleString() ?? "—"} pkt
</p>
<p className="mt-0.5 text-zinc-500">
↑ {formatBytes(iface.tx_bytes)} <span className="text-zinc-600">·</span>{" "}
↑ {formatRate(iface.tx_bps)} <span className="text-zinc-600">·</span>{" "}
{formatBytes(iface.tx_bytes)} <span className="text-zinc-600">·</span>{" "}
{iface.tx_packets?.toLocaleString() ?? "—"} pkt
</p>
</div>