secubox-openwrt/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/realtime.js
CyberMind-FR 8b0d75a8d9 fix(netdata-dashboard): correct API module imports in all views
- Changed from 'require netdata-dashboard.api as api' to 'require netdata-dashboard/api as API'
- Updated all api. references to API. in realtime.js, system.js, network.js, and processes.js
- Resolves "api.getAllData is not a function" and "api.getCpu is not a function" errors

The dot notation (netdata-dashboard.api) doesn't work with LuCI's module loader.
The correct syntax uses slash (netdata-dashboard/api) and follows the convention
of uppercase API for consistency with other modules.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 11:03:14 +01:00

329 lines
12 KiB
JavaScript

'use strict';
'require view';
'require secubox-theme/theme as Theme';
'require poll';
'require dom';
'require ui';
'require netdata-dashboard/api as API';
return view.extend({
title: _('Netdata Dashboard'),
// Store history for sparklines
history: {
cpu: [],
memory: [],
network_rx: [],
network_tx: []
},
maxHistory: 60,
load: function() {
return API.getAllData();
},
addToHistory: function(key, value) {
this.history[key].push(value);
if (this.history[key].length > this.maxHistory) {
this.history[key].shift();
}
},
renderGauge: function(percent, label, size) {
size = size || 140;
var radius = (size - 20) / 2;
var circumference = 2 * Math.PI * radius;
var offset = circumference - (percent / 100 * circumference);
var statusClass = API.getStatusClass(percent);
return E('div', { 'class': 'nd-gauge', 'style': 'width:' + size + 'px;height:' + size + 'px' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('svg', { 'class': 'nd-gauge-svg', 'width': size, 'height': size, 'viewBox': '0 0 ' + size + ' ' + size }, [
E('circle', {
'class': 'nd-gauge-bg',
'cx': size/2, 'cy': size/2, 'r': radius
}),
E('circle', {
'class': 'nd-gauge-fill ' + statusClass,
'cx': size/2, 'cy': size/2, 'r': radius,
'stroke-dasharray': circumference,
'stroke-dashoffset': offset
})
]),
E('div', { 'class': 'nd-gauge-text' }, [
E('div', { 'class': 'nd-gauge-value ' + statusClass }, percent + '%'),
E('div', { 'class': 'nd-gauge-label' }, label)
])
]);
},
renderSparkline: function(data, color, height) {
height = height || 60;
var width = 200;
if (!data || data.length < 2) {
return E('div', { 'class': 'nd-sparkline', 'style': 'height:' + height + 'px' });
}
var max = Math.max.apply(null, data) || 1;
var min = Math.min.apply(null, data);
var range = max - min || 1;
var points = data.map(function(v, i) {
var x = (i / (data.length - 1)) * width;
var y = height - ((v - min) / range) * (height - 10) - 5;
return x + ',' + y;
});
var areaPoints = '0,' + height + ' ' + points.join(' ') + ' ' + width + ',' + height;
return E('svg', { 'class': 'nd-sparkline', 'width': width, 'height': height, 'viewBox': '0 0 ' + width + ' ' + height }, [
E('defs', {}, [
E('linearGradient', { 'id': 'sparkGrad-' + color, 'x1': '0%', 'y1': '0%', 'x2': '0%', 'y2': '100%' }, [
E('stop', { 'offset': '0%', 'style': 'stop-color:' + color + ';stop-opacity:0.3' }),
E('stop', { 'offset': '100%', 'style': 'stop-color:' + color + ';stop-opacity:0' })
])
]),
E('polygon', { 'class': 'nd-sparkline-area', 'points': areaPoints, 'fill': 'url(#sparkGrad-' + color + ')' }),
E('polyline', { 'class': 'nd-sparkline-line', 'points': points.join(' '), 'stroke': color, 'fill': 'none', 'stroke-width': '2' }),
E('circle', { 'class': 'nd-sparkline-dot', 'cx': width, 'cy': points[points.length-1].split(',')[1], 'r': '3', 'fill': color })
]);
},
render: function(data) {
var self = this;
var stats = data.stats || {};
var system = data.system || {};
var memory = data.memory || {};
var network = data.network || {};
var disk = data.disk || {};
var sensors = data.sensors || {};
// Add to history
this.addToHistory('cpu', stats.cpu_percent || 0);
this.addToHistory('memory', stats.memory_percent || 0);
this.addToHistory('network_rx', stats.network_rx || 0);
this.addToHistory('network_tx', stats.network_tx || 0);
var cpuClass = API.getStatusClass(stats.cpu_percent || 0);
var memClass = API.getStatusClass(stats.memory_percent || 0);
var diskClass = API.getStatusClass(stats.disk_percent || 0);
var temp = stats.temperature || 0;
var tempClass = API.getTempClass(temp);
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' }, ['Net', E('span', {}, 'data')])
]),
E('div', { 'class': 'nd-header-info' }, [
E('div', { 'class': 'nd-hostname' }, system.hostname || 'OpenWrt'),
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 ' + cpuClass }, (stats.cpu_percent || 0) + '%'),
E('div', { 'class': 'nd-quick-stat-label' }, 'CPU')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value ' + memClass }, (stats.memory_percent || 0) + '%'),
E('div', { 'class': 'nd-quick-stat-label' }, 'Memory')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value ' + diskClass }, (stats.disk_percent || 0) + '%'),
E('div', { 'class': 'nd-quick-stat-label' }, 'Disk')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value info' }, stats.load || '0.00'),
E('div', { 'class': 'nd-quick-stat-label' }, 'Load')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value ' + tempClass }, temp + '°C'),
E('div', { 'class': 'nd-quick-stat-label' }, 'Temp')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value info' }, stats.processes || 0),
E('div', { 'class': 'nd-quick-stat-label' }, 'Processes')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value info' }, stats.connections || 0),
E('div', { 'class': 'nd-quick-stat-label' }, 'Connections')
]),
E('div', { 'class': 'nd-quick-stat' }, [
E('div', { 'class': 'nd-quick-stat-value good' }, API.formatUptime(stats.uptime || 0)),
E('div', { 'class': 'nd-quick-stat-label' }, 'Uptime')
])
]),
// Charts Grid
E('div', { 'class': 'nd-charts-grid' }, [
// CPU Gauge
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' }, '⚡'),
'CPU Usage'
]),
E('div', { 'class': 'nd-chart-value' }, (stats.cpu_percent || 0) + '%')
]),
E('div', { 'class': 'nd-chart-body', 'style': 'display:flex;align-items:center;justify-content:space-around' }, [
this.renderGauge(stats.cpu_percent || 0, 'CPU', 140),
E('div', { 'style': 'text-align:center' }, [
this.renderSparkline(this.history.cpu, '#3fb950', 60),
E('div', { 'style': 'margin-top:8px;font-size:11px;color:#8b949e' }, 'Last 60 samples')
])
])
]),
// Memory
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' }, '🧠'),
'Memory Usage'
]),
E('div', { 'class': 'nd-chart-value' }, API.formatKB(memory.used || 0) + ' / ' + API.formatKB(memory.total || 0))
]),
E('div', { 'class': 'nd-chart-body' }, [
E('div', { 'class': 'nd-stacked-bar' }, [
E('div', { 'class': 'nd-stacked-segment', 'style': 'width:' + (memory.pct_used || 0) + '%;background:#f85149' }),
E('div', { 'class': 'nd-stacked-segment', 'style': 'width:' + (memory.pct_buffers || 0) + '%;background:#d29922' }),
E('div', { 'class': 'nd-stacked-segment', 'style': 'width:' + (memory.pct_cached || 0) + '%;background:#3fb950' })
]),
E('div', { 'class': 'nd-stacked-legend' }, [
E('div', { 'class': 'nd-legend-item' }, [
E('span', { 'class': 'nd-legend-dot', 'style': 'background:#f85149' }),
'Used: ' + API.formatKB(memory.used || 0)
]),
E('div', { 'class': 'nd-legend-item' }, [
E('span', { 'class': 'nd-legend-dot', 'style': 'background:#d29922' }),
'Buffers: ' + API.formatKB(memory.buffers || 0)
]),
E('div', { 'class': 'nd-legend-item' }, [
E('span', { 'class': 'nd-legend-dot', 'style': 'background:#3fb950' }),
'Cached: ' + API.formatKB(memory.cached || 0)
]),
E('div', { 'class': 'nd-legend-item' }, [
E('span', { 'class': 'nd-legend-dot', 'style': 'background:#21262d' }),
'Free: ' + API.formatKB(memory.free || 0)
])
]),
E('div', { 'style': 'margin-top:16px;text-align:center' },
this.renderSparkline(this.history.memory, '#58a6ff', 50)
)
])
]),
// Network
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' }, '🌐'),
'Network Traffic'
])
]),
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(stats.network_rx || 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(stats.network_tx || 0)),
E('div', { 'class': 'nd-network-label' }, 'Transmitted')
])
]),
E('div', { 'style': 'margin-top:16px;display:flex;gap:20px;justify-content:center' }, [
this.renderSparkline(this.history.network_rx, '#3fb950', 40),
this.renderSparkline(this.history.network_tx, '#58a6ff', 40)
])
])
]),
// Disk
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' }, '💾'),
'Disk Usage'
])
]),
E('div', { 'class': 'nd-chart-body' },
(disk.filesystems || []).slice(0, 4).map(function(fs) {
var pct = fs.pct_used || 0;
var diskStatus = pct >= 90 ? 'danger' : (pct >= 70 ? 'warning' : '');
return E('div', { 'class': 'nd-disk-item' }, [
E('div', { 'class': 'nd-disk-header' }, [
E('span', { 'class': 'nd-disk-mount' }, fs.mount),
E('span', { 'class': 'nd-disk-size' }, API.formatKB(fs.used) + ' / ' + API.formatKB(fs.size))
]),
E('div', { 'class': 'nd-disk-bar' }, [
E('div', { 'class': 'nd-disk-fill ' + diskStatus, 'style': 'width:' + pct + '%' })
])
]);
})
)
])
]),
// System Info
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' }, '🖥️'),
'System Information'
])
]),
E('div', { 'class': 'nd-chart-body' }, [
E('div', { 'class': 'nd-info-grid' }, [
E('div', { 'class': 'nd-info-item' }, [
E('div', { 'class': 'nd-info-label' }, 'Hostname'),
E('div', { 'class': 'nd-info-value' }, system.hostname || 'N/A')
]),
E('div', { 'class': 'nd-info-item' }, [
E('div', { 'class': 'nd-info-label' }, 'Model'),
E('div', { 'class': 'nd-info-value' }, system.model || 'N/A')
]),
E('div', { 'class': 'nd-info-item' }, [
E('div', { 'class': 'nd-info-label' }, 'Kernel'),
E('div', { 'class': 'nd-info-value' }, system.kernel || 'N/A')
]),
E('div', { 'class': 'nd-info-item' }, [
E('div', { 'class': 'nd-info-label' }, 'Architecture'),
E('div', { 'class': 'nd-info-value' }, system.arch || 'N/A')
]),
E('div', { 'class': 'nd-info-item' }, [
E('div', { 'class': 'nd-info-label' }, 'OpenWrt Version'),
E('div', { 'class': 'nd-info-value' }, system.version || 'N/A')
]),
E('div', { 'class': 'nd-info-item' }, [
E('div', { 'class': 'nd-info-label' }, 'Uptime'),
E('div', { 'class': 'nd-info-value' }, system.uptime_formatted || 'N/A')
])
])
])
])
]);
// Include CSS
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
});