secubox-openwrt/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/network.js

191 lines
6.9 KiB
JavaScript

'use strict';
'require view';
'require secubox-theme/bootstrap as Theme';
'require poll';
'require dom';
'require ui';
'require netdata-dashboard.api as api';
return view.extend({
title: _('Network'),
load: function() {
return api.getNetwork();
},
render: function(data) {
var network = data || {};
var interfaces = network.interfaces || [];
var totalRx = 0, totalTx = 0;
interfaces.forEach(function(iface) {
totalRx += iface.rx_bytes || 0;
totalTx += iface.tx_bytes || 0;
});
var view = E('div', { 'class': 'netdata-dashboard' }, [
// Header
E('div', { 'class': 'nd-header' }, [
E('div', { 'class': 'nd-logo' }, [
E('div', { 'class': 'nd-logo-icon' }, '🌐'),
E('div', { 'class': 'nd-logo-text' }, ['Network ', E('span', {}, 'Stats')])
]),
E('div', { 'class': 'nd-header-info' }, [
E('div', { 'class': 'nd-live-badge' }, [
E('span', { 'class': 'nd-live-dot' }),
'Live'
])
])
]),
// Quick Stats
E('div', { 'class': 'nd-quick-stats' }, [
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value info' }, interfaces.length),
E('div', { 'class': 'nd-quick-stat-label' }, 'Interfaces')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value good' }, api.formatBytes(totalRx)),
E('div', { 'class': 'nd-quick-stat-label' }, 'Total RX')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value info' }, api.formatBytes(totalTx)),
E('div', { 'class': 'nd-quick-stat-label' }, 'Total TX')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value warning' }, network.conntrack_count || 0),
E('div', { 'class': 'nd-quick-stat-label' }, 'Connections')
])
]),
// Connection Tracking
network.conntrack_max ? E('div', { 'class': 'nd-chart-card' }, [
E('div', { 'class': 'nd-chart-header' }, [
E('div', { 'class': 'nd-chart-title' }, [
E('span', { 'class': 'nd-chart-title-icon' }, '🔗'),
'Connection Tracking'
])
]),
E('div', { 'class': 'nd-chart-body' }, [
E('div', { 'class': 'nd-connections' }, [
E('div', { 'class': 'nd-connections-count' }, network.conntrack_count || 0),
E('div', { 'class': 'nd-connections-max' }, '/ ' + network.conntrack_max + ' max')
]),
E('div', { 'style': 'margin-top:16px' }, [
E('div', { 'class': 'nd-disk-bar' }, [
E('div', {
'class': 'nd-disk-fill',
'style': 'width:' + Math.min(100, (network.conntrack_count / network.conntrack_max) * 100) + '%'
})
])
])
])
]) : '',
// Interfaces Table
E('div', { 'class': 'nd-chart-card', 'style': 'margin-top:16px' }, [
E('div', { 'class': 'nd-chart-header' }, [
E('div', { 'class': 'nd-chart-title' }, [
E('span', { 'class': 'nd-chart-title-icon' }, '📡'),
'Network Interfaces'
])
]),
E('div', { 'class': 'nd-chart-body', 'style': 'padding:0;overflow-x:auto' }, [
E('table', { 'class': 'nd-table' }, [
E('thead', {}, [
E('tr', {}, [
E('th', {}, 'Interface'),
E('th', {}, 'IP Address'),
E('th', {}, 'State'),
E('th', {}, 'Speed'),
E('th', {}, 'RX Bytes'),
E('th', {}, 'TX Bytes'),
E('th', {}, 'RX Packets'),
E('th', {}, 'TX Packets'),
E('th', {}, 'Errors')
])
]),
E('tbody', {},
interfaces.map(function(iface) {
var state = iface.state || 'unknown';
var stateClass = state === 'up' ? 'up' : (state === 'down' ? 'down' : 'unknown');
return E('tr', {}, [
E('td', { 'class': 'iface' }, iface.name),
E('td', { 'class': 'mono' }, iface.ip || '-'),
E('td', {}, E('span', { 'class': 'nd-status ' + stateClass }, state)),
E('td', { 'class': 'mono' }, iface.speed > 0 ? iface.speed + ' Mbps' : '-'),
E('td', { 'class': 'mono value' }, api.formatBytes(iface.rx_bytes || 0)),
E('td', { 'class': 'mono' }, api.formatBytes(iface.tx_bytes || 0)),
E('td', { 'class': 'mono' }, (iface.rx_packets || 0).toLocaleString()),
E('td', { 'class': 'mono' }, (iface.tx_packets || 0).toLocaleString()),
E('td', { 'class': 'mono' }, (iface.rx_errors || 0) + (iface.tx_errors || 0))
]);
})
)
])
])
]),
// Per-Interface Details
E('div', { 'class': 'nd-charts-grid', 'style': 'margin-top:16px' },
interfaces.slice(0, 6).map(function(iface) {
var rxPct = totalRx > 0 ? Math.round((iface.rx_bytes / totalRx) * 100) : 0;
var txPct = totalTx > 0 ? Math.round((iface.tx_bytes / totalTx) * 100) : 0;
return E('div', { 'class': 'nd-chart-card' }, [
E('div', { 'class': 'nd-chart-header' }, [
E('div', { 'class': 'nd-chart-title' }, [
E('span', { 'class': 'nd-chart-title-icon' }, '📶'),
iface.name
]),
E('span', { 'class': 'nd-status ' + (iface.state === 'up' ? 'up' : 'down') }, iface.state)
]),
E('div', { 'class': 'nd-chart-body' }, [
E('div', { 'class': 'nd-network-stats' }, [
E('div', { 'class': 'nd-network-direction' }, [
E('div', { 'class': 'nd-network-icon' }, '📥'),
E('div', { 'class': 'nd-network-value rx' }, api.formatBytes(iface.rx_bytes || 0)),
E('div', { 'class': 'nd-network-label' }, 'Received')
]),
E('div', { 'class': 'nd-network-direction' }, [
E('div', { 'class': 'nd-network-icon' }, '📤'),
E('div', { 'class': 'nd-network-value tx' }, api.formatBytes(iface.tx_bytes || 0)),
E('div', { 'class': 'nd-network-label' }, 'Transmitted')
])
]),
E('div', { 'style': 'margin-top:16px' }, [
E('div', { 'class': 'nd-bar-chart' }, [
E('div', { 'class': 'nd-bar-item' }, [
E('span', { 'class': 'nd-bar-label' }, 'Packets RX'),
E('div', { 'class': 'nd-bar-track' }, [
E('div', { 'class': 'nd-bar-fill', 'style': 'width:' + rxPct + '%' })
]),
E('span', { 'class': 'nd-bar-value' }, (iface.rx_packets || 0).toLocaleString())
]),
E('div', { 'class': 'nd-bar-item' }, [
E('span', { 'class': 'nd-bar-label' }, 'Packets TX'),
E('div', { 'class': 'nd-bar-track' }, [
E('div', { 'class': 'nd-bar-fill', 'style': 'width:' + txPct + '%' })
]),
E('span', { 'class': 'nd-bar-value' }, (iface.tx_packets || 0).toLocaleString())
])
])
]),
iface.ip ? E('div', { 'style': 'margin-top:12px;font-size:12px;color:#8b949e' }, 'IP: ' + iface.ip) : ''
])
]);
})
)
]);
var cssLink = E('link', { 'rel': 'stylesheet', 'href': L.resource('netdata-dashboard/dashboard.css') });
document.head.appendChild(cssLink);
return view;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});