secubox-openwrt/luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/logs.js
CyberMind-FR 34fe2dc26a feat: complete System Hub implementation - central control dashboard
Implements comprehensive system control and monitoring dashboard with health
metrics, service management, system logs, and backup/restore functionality.

Features:
- Real-time system monitoring with visual gauges (CPU, RAM, Disk)
- Comprehensive system information (hostname, model, uptime, kernel)
- Health metrics with temperature monitoring and storage breakdown
- Service management with start/stop/restart/enable/disable actions
- System log viewer with filtering and configurable line count
- Configuration backup creation and download (base64 encoded)
- Configuration restore from backup file
- System reboot functionality with confirmation

Components:
- RPCD backend (luci.system-hub): 10 ubus methods
  * status, get_system_info, get_health
  * list_services, service_action
  * get_logs, backup_config, restore_config
  * reboot, get_storage
- 4 JavaScript views: overview, services, logs, backup
- ACL with read/write permissions segregation
- Comprehensive README with API documentation

Technical implementation:
- System info from /proc filesystem and sysinfo
- Health metrics: CPU load, memory breakdown, disk usage, temperature
- Service control via /etc/init.d scripts
- Log retrieval via logread with filtering
- Backup/restore using sysupgrade with base64 encoding
- Visual gauges with SVG circular progress indicators
- Color-coded health status (green/orange/red)

Dashboard Features:
- Circular gauges for CPU, Memory, Disk (120px with 10px stroke)
- System information cards with detailed metrics
- Temperature monitoring with thermal zone detection
- Storage table for all mount points with progress bars
- Service table with inline action buttons
- Terminal-style log display (black bg, green text)
- File upload for backup restore
- Modal confirmations for destructive actions

Architecture follows SecuBox standards:
- RPCD naming convention (luci. prefix)
- Menu paths match view file structure
- All JavaScript in strict mode
- Form-based configuration management
- Comprehensive error handling

Dependencies: coreutils, coreutils-base64

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-24 11:02:07 +01:00

101 lines
2.6 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require system-hub/api as API';
return L.view.extend({
load: function() {
return API.getLogs(100, '');
},
render: function(logs) {
var v = E('div', { 'class': 'cbi-map' }, [
E('h2', {}, _('System Logs')),
E('div', { 'class': 'cbi-map-descr' }, _('View and filter system logs'))
]);
var section = E('div', { 'class': 'cbi-section' });
// Filter controls
var controlsDiv = E('div', { 'style': 'margin-bottom: 15px; display: flex; gap: 10px; align-items: center;' });
var filterInput = E('input', {
'type': 'text',
'class': 'cbi-input-text',
'placeholder': _('Filter logs...'),
'style': 'flex: 1;'
});
var linesSelect = E('select', { 'class': 'cbi-input-select' }, [
E('option', { 'value': '50' }, '50 lines'),
E('option', { 'value': '100', 'selected': '' }, '100 lines'),
E('option', { 'value': '200' }, '200 lines'),
E('option', { 'value': '500' }, '500 lines'),
E('option', { 'value': '1000' }, '1000 lines')
]);
var refreshBtn = E('button', {
'class': 'cbi-button cbi-button-action',
'click': L.bind(function() {
this.refreshLogs(filterInput.value, parseInt(linesSelect.value));
}, this)
}, _('Refresh'));
var clearBtn = E('button', {
'class': 'cbi-button cbi-button-neutral',
'click': function() {
filterInput.value = '';
}
}, _('Clear Filter'));
controlsDiv.appendChild(filterInput);
controlsDiv.appendChild(linesSelect);
controlsDiv.appendChild(refreshBtn);
controlsDiv.appendChild(clearBtn);
section.appendChild(controlsDiv);
// Log display
var logContainer = E('div', { 'id': 'log-container' });
section.appendChild(logContainer);
// Initial render
this.renderLogs(logContainer, logs);
v.appendChild(section);
return v;
},
renderLogs: function(container, logs) {
var logsText = logs.length > 0 ? logs.join('\n') : _('No logs available');
L.dom.content(container, [
E('pre', {
'style': 'background: #000; color: #0f0; padding: 15px; overflow: auto; max-height: 600px; font-size: 11px; font-family: monospace; border-radius: 5px;'
}, logsText)
]);
},
refreshLogs: function(filter, lines) {
ui.showModal(_('Loading Logs'), [
E('p', { 'class': 'spinning' }, _('Fetching logs...'))
]);
API.getLogs(lines, filter).then(L.bind(function(logs) {
ui.hideModal();
var container = document.getElementById('log-container');
if (container) {
this.renderLogs(container, logs);
}
}, this)).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', _('Failed to load logs: ') + err.message), 'error');
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});