secubox-openwrt/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js
CyberMind-FR a6477b8710 feat: Version 0.4.1 - Enhanced network modes and system improvements
Major Enhancements:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Network Modes Module:
- Added 3 new network modes:
  * Double NAT mode (doublenat.js) - Cascaded router configuration
  * Multi-WAN mode (multiwan.js) - Load balancing and failover
  * VPN Relay mode (vpnrelay.js) - VPN gateway configuration
- Enhanced existing modes:
  * Access Point improvements
  * Travel mode refinements
  * Router mode enhancements
  * Relay mode updates
  * Sniffer mode optimizations
- Updated wizard with new mode options
- Enhanced API with new mode support
- Improved dashboard CSS styling
- Updated helpers for new modes
- Extended RPCD backend functionality
- Updated menu structure for new modes
- Enhanced UCI configuration

System Hub Module:
- Added dedicated logs.css stylesheet
- Enhanced logs.js view with better styling
- Improved overview.css responsive design
- Enhanced services.css for better UX
- Updated overview.js with theme integration
- Improved services.js layout

SecuBox Dashboard:
- Enhanced dashboard.css with theme variables
- Improved dashboard.js responsiveness
- Better integration with global theme

Files Changed:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Network Modes (17 files):
  Modified: api.js, dashboard.css, helpers.js, menu, config, RPCD backend
  Modified Views: accesspoint, overview, relay, router, sniffer, travel, wizard
  New Views: doublenat, multiwan, vpnrelay

System Hub (6 files):
  New: logs.css
  Modified: overview.css, services.css, logs.js, overview.js, services.js

SecuBox (2 files):
  Modified: dashboard.css, dashboard.js

Total: 25 files changed (21 modified, 4 new)

Technical Improvements:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Global theme CSS variable usage
- Responsive design enhancements
- Improved error handling
- Better mode validation
- Enhanced user feedback
- Optimized CSS performance
- Improved accessibility

Network Mode Capabilities:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Router Mode - Standard routing
2. Access Point Mode - WiFi AP with bridge
3. Relay Mode - WiFi repeater/extender
4. Travel Mode - Portable router configuration
5. Sniffer Mode - Network monitoring
6. Double NAT Mode - Cascaded NAT for network isolation (NEW)
7. Multi-WAN Mode - Multiple uplinks with load balancing (NEW)
8. VPN Relay Mode - VPN gateway and tunnel endpoint (NEW)

Breaking Changes:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
None - All changes are backward compatible

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 18:12:28 +01:00

433 lines
13 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require dom';
'require poll';
'require secubox/api as API';
'require secubox-theme/theme as Theme';
// Load theme resources once
document.head.appendChild(E('link', {
'rel': 'stylesheet',
'type': 'text/css',
'href': L.resource('secubox-theme/secubox-theme.css')
}));
Theme.init({ theme: 'dark', language: 'en' });
return view.extend({
dashboardData: null,
healthData: null,
alertsData: null,
activeCategory: 'all',
load: function() {
return this.refreshData();
},
refreshData: function() {
var self = this;
return Promise.all([
API.getDashboardData(),
API.getSystemHealth(),
API.getAlerts()
]).then(function(data) {
self.dashboardData = data[0] || {};
self.healthData = data[1] || {};
self.alertsData = data[2] || {};
return data;
});
},
render: function() {
var container = E('div', { 'class': 'secubox-dashboard' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/dashboard.css') }),
this.renderHeader(),
this.renderStatsGrid(),
this.renderMainLayout()
]);
var self = this;
poll.add(function() {
return self.refreshData().then(function() {
self.updateDynamicSections();
});
}, 30);
return container;
},
renderHeader: function() {
var status = this.dashboardData.status || {};
return E('header', { 'class': 'sb-header' }, [
E('div', { 'class': 'sb-header-info' }, [
E('div', { 'class': 'sb-header-icon' }, '🚀'),
E('div', {}, [
E('h1', { 'class': 'sb-title' }, 'SecuBox Control Center'),
E('p', { 'class': 'sb-subtitle' }, 'Security · Network · System Automation')
])
]),
E('div', { 'class': 'sb-header-meta' }, [
this.renderBadge('v' + (status.version || '0.0.0')),
this.renderBadge('⏱ ' + API.formatUptime(status.uptime)),
this.renderBadge('🖥 ' + (status.hostname || 'SecuBox'), 'sb-badge-ghost')
])
]);
},
renderBadge: function(text, extraClass) {
return E('span', { 'class': 'sb-badge ' + (extraClass || '') }, text);
},
renderStatsGrid: function() {
var modules = this.dashboardData.modules || [];
var counts = this.dashboardData.counts || {};
var alertsCount = (this.alertsData.alerts || []).length;
var healthScore = (this.healthData.overall && this.healthData.overall.score) || 0;
var stats = [
{ label: _('Total Modules'), value: counts.total || modules.length, icon: '📦', id: 'sb-stat-total' },
{ label: _('Active Services'), value: counts.running || 0, icon: '🟢', id: 'sb-stat-running' },
{ label: _('System Health'), value: healthScore + '/100', icon: '❤️', id: 'sb-stat-health' },
{ label: _('Alerts'), value: alertsCount, icon: '⚠️', id: 'sb-stat-alerts' }
];
return E('section', { 'class': 'sb-stats-grid' },
stats.map(function(stat) {
return E('div', { 'class': 'sb-stat-card' }, [
E('div', { 'class': 'sb-stat-icon' }, stat.icon),
E('div', { 'class': 'sb-stat-content' }, [
E('div', { 'class': 'sb-stat-value', 'id': stat.id }, stat.value),
E('div', { 'class': 'sb-stat-label' }, stat.label)
])
]);
})
);
},
renderMainLayout: function() {
return E('div', { 'class': 'sb-main-grid' }, [
E('div', { 'class': 'sb-grid-left' }, [
this.renderModulesSection(),
this.renderSystemHealthPanel()
]),
E('div', { 'class': 'sb-grid-right' }, [
this.renderQuickActions(),
this.renderAlertsTimeline()
])
]);
},
renderModulesSection: function() {
var self = this;
var filters = [
{ id: 'all', label: _('All Modules') },
{ id: 'security', label: _('Security') },
{ id: 'network', label: _('Network') },
{ id: 'services', label: _('Services') }
];
return E('section', { 'class': 'sb-card' }, [
E('div', { 'class': 'sb-card-header' }, [
E('div', {}, [
E('h2', {}, _('Module Overview')),
E('p', { 'class': 'sb-card-subtitle' }, _('Monitor, configure, and access every SecuBox module'))
]),
E('div', { 'class': 'sb-filter-tabs' }, filters.map(function(filter) {
return E('button', {
'class': 'sb-filter-tab' + (self.activeCategory === filter.id ? ' active' : ''),
'type': 'button',
'click': function() {
self.activeCategory = filter.id;
self.updateModuleGrid();
}
}, filter.label);
}))
]),
E('div', { 'class': 'sb-module-grid', 'id': 'sb-module-grid' },
this.getFilteredModules().map(this.renderModuleCard, this))
]);
},
getFilteredModules: function() {
var modules = this.dashboardData.modules || [];
if (this.activeCategory === 'all')
return modules;
return modules.filter(function(module) {
return (module.category || '').toLowerCase() === this.activeCategory;
}, this);
},
renderModuleCard: function(module) {
var status = module.status || 'unknown';
var statusLabel = status === 'active' ? _('Running') :
status === 'error' ? _('Error') :
status === 'disabled' ? _('Disabled') : _('Unknown');
return E('div', { 'class': 'sb-module-card sb-status-' + status }, [
E('div', { 'class': 'sb-module-header' }, [
E('div', { 'class': 'sb-module-icon' }, module.icon || '📦'),
E('div', {}, [
E('h3', {}, module.name || module.id),
E('p', {}, module.description || '')
]),
E('span', { 'class': 'sb-status-pill' }, statusLabel)
]),
E('div', { 'class': 'sb-module-meta' }, [
E('span', {}, _('Version: ') + (module.version || '0.0.0')),
E('span', {}, _('Category: ') + (module.category || 'other'))
]),
E('div', { 'class': 'sb-module-actions' }, [
E('button', {
'class': 'sb-btn sb-btn-ghost',
'type': 'button',
'click': function() {
window.location.href = L.url('admin/secubox/modules');
}
}, _('View Details')),
E('button', {
'class': 'sb-btn sb-btn-primary',
'type': 'button',
'click': function() {
if (module.route) {
window.location.href = L.url(module.route);
}
}
}, _('Configure'))
])
]);
},
renderSystemHealthPanel: function() {
var self = this;
var health = this.healthData || {};
var cpu = health.cpu || {};
var memory = health.memory || {};
var disk = health.disk || {};
var network = health.network || {};
var metrics = [
{
id: 'cpu',
label: _('CPU Usage'),
value: cpu.percent || 0,
detail: _('Load: ') + (cpu.load_1min || '0.00'),
icon: '🔥'
},
{
id: 'memory',
label: _('Memory'),
value: memory.percent || 0,
detail: API.formatBytes((memory.used_kb || 0) * 1024) + ' / ' +
API.formatBytes((memory.total_kb || 0) * 1024),
icon: '💾'
},
{
id: 'disk',
label: _('Storage'),
value: disk.percent || 0,
detail: API.formatBytes((disk.used_kb || 0) * 1024) + ' / ' +
API.formatBytes((disk.total_kb || 0) * 1024),
icon: '💿'
},
{
id: 'network',
label: _('Network'),
value: network.wan_up ? 100 : 0,
detail: network.wan_up ? _('WAN online') : _('WAN offline'),
icon: '🌐'
}
];
return E('section', { 'class': 'sb-card' }, [
E('div', { 'class': 'sb-card-header' }, [
E('h2', {}, _('System Health Dashboard')),
E('p', { 'class': 'sb-card-subtitle' }, _('Real-time telemetry for CPU, RAM, Disk, Network'))
]),
E('div', { 'class': 'sb-health-grid' },
metrics.map(function(metric) {
return self.renderHealthMetric(metric);
}))
]);
},
renderHealthMetric: function(metric) {
var percent = Math.min(100, Math.max(0, Math.round(metric.value)));
var severity = percent < 70 ? 'ok' : percent < 90 ? 'warn' : 'danger';
return E('div', { 'class': 'sb-health-metric' }, [
E('div', { 'class': 'sb-health-icon' }, metric.icon),
E('div', { 'class': 'sb-health-info' }, [
E('div', { 'class': 'sb-health-label' }, metric.label),
E('div', { 'class': 'sb-health-detail', 'id': 'sb-health-detail-' + metric.id }, metric.detail)
]),
E('div', { 'class': 'sb-health-bar' }, [
E('div', {
'class': 'sb-health-progress sb-' + severity,
'id': 'sb-health-bar-' + metric.id,
'style': 'width:' + percent + '%'
})
]),
E('span', { 'class': 'sb-health-percent', 'id': 'sb-health-percent-' + metric.id }, percent + '%')
]);
},
renderQuickActions: function() {
var self = this;
var actions = [
{ id: 'restart_services', label: _('Restart All Services'), icon: '🔄', variant: 'orange' },
{ id: 'update_packages', label: _('Update Packages'), icon: '⬆️', variant: 'blue' },
{ id: 'view_logs', label: _('View System Logs'), icon: '📜', variant: 'indigo' },
{ id: 'export_config', label: _('Export Configuration'), icon: '📦', variant: 'green' }
];
return E('section', { 'class': 'sb-card' }, [
E('div', { 'class': 'sb-card-header' }, [
E('h2', {}, _('Quick Actions')),
E('p', { 'class': 'sb-card-subtitle' }, _('Frequently performed maintenance tasks'))
]),
E('div', { 'class': 'sb-actions-grid' },
actions.map(function(action) {
return E('button', {
'class': 'sb-action-btn sb-' + action.variant,
'type': 'button',
'click': function() {
self.runQuickAction(action.id);
}
}, [
E('span', { 'class': 'sb-action-icon' }, action.icon),
E('span', { 'class': 'sb-action-label' }, action.label)
]);
}))
]);
},
runQuickAction: function(actionId) {
ui.showModal(_('Executing action...'), [
E('p', { 'class': 'spinning' }, _('Running ') + actionId + ' ...')
]);
return API.quickAction(actionId).then(function(result) {
ui.hideModal();
if (result && result.success === false) {
ui.addNotification(null, E('p', {}, result.message || _('Action failed')), 'error');
} else {
ui.addNotification(null, E('p', {}, _('Action completed successfully')), 'info');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', {}, err.message || err), 'error');
});
},
renderAlertsTimeline: function() {
var alerts = (this.alertsData.alerts || []).slice(0, 10);
return E('section', { 'class': 'sb-card' }, [
E('div', { 'class': 'sb-card-header' }, [
E('h2', {}, _('Alert Timeline')),
E('p', { 'class': 'sb-card-subtitle' }, _('Latest security and system events'))
]),
alerts.length === 0 ?
E('div', { 'class': 'sb-empty-state' }, [
E('div', { 'class': 'sb-empty-icon' }, '🎉'),
E('p', {}, _('No alerts in the last 24 hours'))
]) :
E('div', { 'class': 'sb-alert-list', 'id': 'sb-alert-list' },
alerts.map(this.renderAlertItem, this))
]);
},
renderAlertItem: function(alert) {
var severity = (alert.severity || 'info').toLowerCase();
var timestamp = alert.timestamp || alert.time || '';
return E('div', { 'class': 'sb-alert-item sb-' + severity }, [
E('div', { 'class': 'sb-alert-time' }, timestamp),
E('div', { 'class': 'sb-alert-details' }, [
E('strong', {}, alert.title || alert.message || _('Alert')),
alert.description ? E('p', {}, alert.description) : '',
alert.source ? E('span', { 'class': 'sb-alert-meta' }, alert.source) : ''
])
]);
},
updateDynamicSections: function() {
this.updateStats();
this.updateModuleGrid();
this.updateHealthMetrics();
this.updateAlerts();
},
updateStats: function() {
var modules = this.dashboardData.modules || [];
var counts = this.dashboardData.counts || {};
var alertsCount = (this.alertsData.alerts || []).length;
var healthScore = (this.healthData.overall && this.healthData.overall.score) || 0;
var stats = {
'sb-stat-total': counts.total || modules.length,
'sb-stat-running': counts.running || 0,
'sb-stat-health': healthScore + '/100',
'sb-stat-alerts': alertsCount
};
Object.keys(stats).forEach(function(id) {
var node = document.getElementById(id);
if (node) node.textContent = stats[id];
});
},
updateModuleGrid: function() {
var grid = document.getElementById('sb-module-grid');
if (!grid) return;
dom.content(grid, this.getFilteredModules().map(this.renderModuleCard, this));
},
updateHealthMetrics: function() {
var health = this.healthData || {};
var cpu = health.cpu || {};
var memory = health.memory || {};
var disk = health.disk || {};
var network = health.network || {};
var map = {
cpu: { value: cpu.percent || 0, detail: _('Load: ') + (cpu.load_1min || '0.00') },
memory: {
value: memory.percent || 0,
detail: API.formatBytes((memory.used_kb || 0) * 1024) + ' / ' +
API.formatBytes((memory.total_kb || 0) * 1024)
},
disk: {
value: disk.percent || 0,
detail: API.formatBytes((disk.used_kb || 0) * 1024) + ' / ' +
API.formatBytes((disk.total_kb || 0) * 1024)
},
network: { value: network.wan_up ? 100 : 0, detail: network.wan_up ? _('WAN online') : _('WAN offline') }
};
Object.keys(map).forEach(function(key) {
var percent = Math.min(100, Math.max(0, Math.round(map[key].value)));
var severity = percent < 70 ? 'ok' : percent < 90 ? 'warn' : 'danger';
var bar = document.getElementById('sb-health-bar-' + key);
var pct = document.getElementById('sb-health-percent-' + key);
var detail = document.getElementById('sb-health-detail-' + key);
if (bar) {
bar.style.width = percent + '%';
bar.className = 'sb-health-progress sb-' + severity;
}
if (pct) pct.textContent = percent + '%';
if (detail) detail.textContent = map[key].detail;
});
},
updateAlerts: function() {
var list = document.getElementById('sb-alert-list');
if (!list) return;
var alerts = (this.alertsData.alerts || []).slice(0, 10);
dom.content(list, alerts.map(this.renderAlertItem, this));
}
});