secubox-openwrt/package/secubox/luci-app-wireguard-dashboard/htdocs/luci-static/resources/view/wireguard-dashboard/traffic.js
CyberMind-FR db3a41928e fix(luci): Fix require syntax in all LuCI views - use slashes instead of dots
All 'require module.submodule' directives changed to 'require module/submodule'
to match LuCI's module loading convention.

Affected packages:
- luci-app-auth-guardian
- luci-app-glances
- luci-app-localai
- luci-app-magicmirror2
- luci-app-mitmproxy
- luci-app-mmpm
- luci-app-mqtt-bridge
- luci-app-ndpid
- luci-app-network-modes
- luci-app-secubox-admin
- luci-app-secubox-portal
- luci-app-wireguard-dashboard

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:15:21 +01:00

215 lines
7.7 KiB
JavaScript

'use strict';
'require view';
'require secubox-theme/theme as Theme';
'require poll';
'require dom';
'require ui';
'require wireguard-dashboard/api as api';
return view.extend({
title: _('WireGuard Traffic'),
pollInterval: 5,
pollActive: true,
load: function() {
return api.getTraffic();
},
updateTrafficStats: function(traffic) {
var totalRx = traffic.total_rx || 0;
var totalTx = traffic.total_tx || 0;
var totalTraffic = totalRx + totalTx;
// Update totals
var rxEl = document.querySelector('.wg-traffic-total-rx');
var txEl = document.querySelector('.wg-traffic-total-tx');
var totalEl = document.querySelector('.wg-traffic-total');
if (rxEl) {
rxEl.textContent = api.formatBytes(totalRx);
rxEl.classList.add('wg-value-updated');
setTimeout(function() { rxEl.classList.remove('wg-value-updated'); }, 500);
}
if (txEl) {
txEl.textContent = api.formatBytes(totalTx);
txEl.classList.add('wg-value-updated');
setTimeout(function() { txEl.classList.remove('wg-value-updated'); }, 500);
}
if (totalEl) {
totalEl.textContent = api.formatBytes(totalTraffic);
}
// Update per-interface stats
var interfaces = traffic.interfaces || [];
interfaces.forEach(function(iface) {
var card = document.querySelector('.wg-interface-card[data-iface="' + iface.name + '"]');
if (!card) return;
var ifaceTotal = (iface.total_rx || 0) + (iface.total_tx || 0);
var rxPct = totalTraffic > 0 ? ((iface.total_rx || 0) / totalTraffic * 100) : 0;
var txPct = totalTraffic > 0 ? ((iface.total_tx || 0) / totalTraffic * 100) : 0;
// Update traffic values
var rxSpan = card.querySelector('.wg-iface-rx');
var txSpan = card.querySelector('.wg-iface-tx');
var totalSpan = card.querySelector('.wg-iface-total');
if (rxSpan) rxSpan.textContent = '↓ ' + api.formatBytes(iface.total_rx || 0);
if (txSpan) txSpan.textContent = '↑ ' + api.formatBytes(iface.total_tx || 0);
if (totalSpan) totalSpan.textContent = api.formatBytes(ifaceTotal) + ' total';
// Update progress bars
var rxBar = card.querySelector('.wg-traffic-bar-rx');
var txBar = card.querySelector('.wg-traffic-bar-tx');
if (rxBar) rxBar.style.width = rxPct + '%';
if (txBar) txBar.style.width = txPct + '%';
});
},
startPolling: function() {
var self = this;
this.pollActive = true;
poll.add(L.bind(function() {
if (!this.pollActive) return Promise.resolve();
return api.getTraffic().then(L.bind(function(data) {
this.updateTrafficStats(data || {});
}, this));
}, this), this.pollInterval);
},
stopPolling: function() {
this.pollActive = false;
poll.stop();
},
render: function(data) {
var self = this;
var traffic = data || {};
var interfaces = traffic.interfaces || [];
var totalRx = traffic.total_rx || 0;
var totalTx = traffic.total_tx || 0;
var totalTraffic = totalRx + totalTx;
var view = E('div', { 'class': 'wireguard-dashboard' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
// Header
E('div', { 'class': 'wg-header' }, [
E('div', { 'class': 'wg-logo' }, [
E('div', { 'class': 'wg-logo-icon' }, '📊'),
E('div', { 'class': 'wg-logo-text' }, ['Traffic ', E('span', {}, 'Statistics')])
])
]),
// Total Stats
E('div', { 'class': 'wg-quick-stats' }, [
E('div', { 'class': 'wg-quick-stat', 'style': '--stat-gradient: linear-gradient(135deg, #10b981, #34d399)' }, [
E('div', { 'class': 'wg-quick-stat-header' }, [
E('span', { 'class': 'wg-quick-stat-icon' }, '📥'),
E('span', { 'class': 'wg-quick-stat-label' }, 'Total Downloaded')
]),
E('div', { 'class': 'wg-quick-stat-value wg-traffic-total-rx' }, api.formatBytes(totalRx)),
E('div', { 'class': 'wg-quick-stat-sub' }, 'All interfaces combined')
]),
E('div', { 'class': 'wg-quick-stat', 'style': '--stat-gradient: linear-gradient(135deg, #0ea5e9, #38bdf8)' }, [
E('div', { 'class': 'wg-quick-stat-header' }, [
E('span', { 'class': 'wg-quick-stat-icon' }, '📤'),
E('span', { 'class': 'wg-quick-stat-label' }, 'Total Uploaded')
]),
E('div', { 'class': 'wg-quick-stat-value wg-traffic-total-tx' }, api.formatBytes(totalTx)),
E('div', { 'class': 'wg-quick-stat-sub' }, 'All interfaces combined')
]),
E('div', { 'class': 'wg-quick-stat' }, [
E('div', { 'class': 'wg-quick-stat-header' }, [
E('span', { 'class': 'wg-quick-stat-icon' }, '📈'),
E('span', { 'class': 'wg-quick-stat-label' }, 'Total Traffic')
]),
E('div', { 'class': 'wg-quick-stat-value wg-traffic-total' }, api.formatBytes(totalTraffic)),
E('div', { 'class': 'wg-quick-stat-sub' }, 'RX + TX combined')
])
]),
// Per-interface traffic
E('div', { 'class': 'wg-card' }, [
E('div', { 'class': 'wg-card-header' }, [
E('div', { 'class': 'wg-card-title' }, [
E('span', { 'class': 'wg-card-title-icon' }, '🔗'),
'Traffic by Interface'
])
]),
E('div', { 'class': 'wg-card-body' },
interfaces.length > 0 ?
interfaces.map(function(iface) {
var ifaceTotal = (iface.total_rx || 0) + (iface.total_tx || 0);
var rxPct = totalTraffic > 0 ? ((iface.total_rx || 0) / totalTraffic * 100) : 0;
var txPct = totalTraffic > 0 ? ((iface.total_tx || 0) / totalTraffic * 100) : 0;
return E('div', { 'class': 'wg-interface-card', 'data-iface': iface.name, 'style': 'margin-bottom: 16px' }, [
E('div', { 'class': 'wg-interface-header' }, [
E('div', { 'class': 'wg-interface-name' }, [
E('div', { 'class': 'wg-interface-icon' }, '🌐'),
E('div', {}, [
E('h3', {}, iface.name),
E('p', { 'class': 'wg-iface-total' }, api.formatBytes(ifaceTotal) + ' total')
])
])
]),
E('div', { 'class': 'wg-traffic-bar' }, [
E('div', { 'class': 'wg-traffic-bar-header' }, [
E('span', { 'class': 'wg-iface-rx', 'style': 'color: #10b981' }, '↓ ' + api.formatBytes(iface.total_rx || 0)),
E('span', { 'class': 'wg-iface-tx', 'style': 'color: #0ea5e9' }, '↑ ' + api.formatBytes(iface.total_tx || 0))
]),
E('div', { 'class': 'wg-traffic-bar-track' }, [
E('div', { 'class': 'wg-traffic-bar-rx', 'style': 'width:' + rxPct + '%' }),
E('div', { 'class': 'wg-traffic-bar-tx', 'style': 'width:' + txPct + '%' })
])
]),
(iface.peers || []).length > 0 ? E('div', { 'class': 'wg-table-container', 'style': 'margin-top: 16px' }, [
E('table', { 'class': 'wg-table' }, [
E('thead', {}, [
E('tr', {}, [
E('th', {}, 'Peer'),
E('th', {}, 'Downloaded'),
E('th', {}, 'Uploaded'),
E('th', {}, 'Total')
])
]),
E('tbody', {},
(iface.peers || []).map(function(peer) {
var peerTotal = (peer.rx_bytes || 0) + (peer.tx_bytes || 0);
return E('tr', {}, [
E('td', { 'class': 'mono key' }, peer.short_key + '...'),
E('td', { 'class': 'mono', 'style': 'color: #10b981' }, api.formatBytes(peer.rx_bytes || 0)),
E('td', { 'class': 'mono', 'style': 'color: #0ea5e9' }, api.formatBytes(peer.tx_bytes || 0)),
E('td', { 'class': 'mono' }, api.formatBytes(peerTotal))
]);
})
)
])
]) : ''
]);
}) :
E('div', { 'class': 'wg-empty' }, [
E('div', { 'class': 'wg-empty-icon' }, '📊'),
E('div', { 'class': 'wg-empty-text' }, 'No traffic data available')
])
)
])
]);
// Include CSS
var cssLink = E('link', { 'rel': 'stylesheet', 'href': L.resource('wireguard-dashboard/dashboard.css') });
document.head.appendChild(cssLink);
// Start auto-refresh
this.startPolling();
return view;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});