feat(netmgrd): 播放器流量统计细化 — A.实时速率去lo(uplink_totals) C1.netlink sock_diag TCP流公网/内网字节(pub_bytes)=status+panel展示+诚实标注;UDP暂不计(tcp_only),netmgrd宿主70/70pass
This commit is contained in:
1 parent
dce12c63a2
commit
26a140e00f
2 files changed
+325
-26
No files matched your search
@@ -747,6 +747,12 @@
|
||||
} else {
|
||||
rows += homeKv('DNS', '<span class="home-chip mut">—</span>');
|
||||
}
|
||||
const pb = (nd && nd.pub_bytes) || null;
|
||||
if (pb && pb.total) {
|
||||
rows += homeKv('公网流量', '↓ ' + fmtBytes(pb.total.rx || 0) + ' · ↑ ' + fmtBytes(pb.total.tx || 0) + '<span class="muted">(TCP)</span>');
|
||||
} else {
|
||||
rows += homeKv('公网流量', '<span class="home-chip mut">—</span>');
|
||||
}
|
||||
return homeCard('WiFi / DNS', '<div class="home-card-grid">' + rows + '</div>');
|
||||
}
|
||||
function healthCard() {
|
||||
@@ -1269,6 +1275,7 @@
|
||||
meta += (ifd.rssi != null) ? '信号 ' + esc(ifd.rssi) + ' dBm' : '信号 —';
|
||||
if (ifd.dns && ifd.dns.length) meta += ' · DNS ' + esc(ifd.dns.join(' / '));
|
||||
meta += '</div>';
|
||||
h += '<p class="muted" style="margin-bottom:8px">RX / TX 为接口累计字节(含局域网/面板轮询/DNS 等内网往返),非手机热点公网消耗。</p>';
|
||||
h += meta;
|
||||
h += '<table class="table"><thead><tr><th>接口</th><th>类型</th><th>状态</th><th>IP 地址</th><th>网关</th><th>RX / TX</th></tr></thead><tbody>';
|
||||
if (!ifs.length) h += '<tr><td colspan="6" class="empty">无接口数据</td></tr>';
|
||||
@@ -1289,11 +1296,46 @@
|
||||
h += '</div>';
|
||||
return h;
|
||||
}
|
||||
/* 公网流量卡(netmgrd pub_bytes:netlink sock_diag 按 TCP 连接累积,远端公网/内网分流)。
|
||||
* 口径诚实:仅 TCP;UDP/DNS/mDNS/ARP 不计;内网本链路(面板轮询等)归 lan。 */
|
||||
function pubBytesCard(pb) {
|
||||
if (!pb) return '';
|
||||
const t = pb.total || {};
|
||||
let h = '<div class="card"><h3 class="card-title">公网流量</h3>';
|
||||
h += '<p class="muted">按 TCP 连接计量(远端公网才计入);UDP/DNS/mDNS/ARP 不计。接口累计含内网往返,非公网消耗。</p>';
|
||||
h += '<div class="home-sub" style="margin-bottom:8px">公网累计:↓ ' + fmtBytes(t.rx || 0) + ' · ↑ ' + fmtBytes(t.tx || 0) + '</div>';
|
||||
const ents = pb.by_entity || [];
|
||||
if (ents.length) {
|
||||
h += '<table class="table"><thead><tr><th>实体</th><th>公网 ↓ / ↑</th><th>内网 ↓ / ↑</th></tr></thead><tbody>';
|
||||
ents.forEach(e => {
|
||||
h += '<tr><td class="net-entity">' + netBadge(e.entity) + '</td>' +
|
||||
'<td class="muted">' + fmtBytes((e.pub && e.pub[0]) || 0) + ' / ' + fmtBytes((e.pub && e.pub[1]) || 0) + '</td>' +
|
||||
'<td class="muted">' + fmtBytes((e.lan && e.lan[0]) || 0) + ' / ' + fmtBytes((e.lan && e.lan[1]) || 0) + '</td></tr>';
|
||||
});
|
||||
h += '</tbody></table>';
|
||||
}
|
||||
const flows = pb.by_flow || [];
|
||||
if (flows.length) {
|
||||
h += '<div class="home-sub" style="margin-top:10px">公网流 Top</div>';
|
||||
h += '<table class="table"><thead><tr><th>远端</th><th>实体</th><th>↓ / ↑</th></tr></thead><tbody>';
|
||||
flows.forEach(f => {
|
||||
h += '<tr><td class="mono">' + esc(f.ip || '') + ':' + esc('' + f.port) +
|
||||
(f.domain ? ' <span class="muted">' + esc(f.domain) + '</span>' : '') + '</td>' +
|
||||
'<td class="net-entity">' + netBadge(f.entity) + '</td>' +
|
||||
'<td class="muted">' + fmtBytes(f.rx || 0) + ' / ' + fmtBytes(f.tx || 0) + '</td></tr>';
|
||||
});
|
||||
h += '</tbody></table>';
|
||||
}
|
||||
h += '</div>';
|
||||
return h;
|
||||
}
|
||||
|
||||
/* WiFi / 蓝牙摘要(/api/net/status:E0 概览,含实时速率)。接口细分已迁 netIfaceCards。 */
|
||||
function netStatusCards(d) {
|
||||
let h = '';
|
||||
if (d && d.rate && (d.rate.rx_bps != null || d.rate.tx_bps != null))
|
||||
h += '<div class="card"><p class="muted">实时速率:↓ ' + fmtBytes(d.rate.rx_bps) + '/s · ↑ ' + fmtBytes(d.rate.tx_bps) + '/s</p></div>';
|
||||
h += '<div class="card"><p class="muted">实时速率(上行接口,不含回环):↓ ' + fmtBytes(d.rate.rx_bps) + '/s · ↑ ' + fmtBytes(d.rate.tx_bps) + '/s</p></div>';
|
||||
if (d && d.pub_bytes) h += pubBytesCard(d.pub_bytes);
|
||||
// WiFi 摘要
|
||||
const w = (d && d.wifi) || {};
|
||||
h += '<div class="card"><h3 class="card-title">WiFi</h3><table class="table"><tbody>';
|
||||
|
||||
Reference in new issue
Block a user