fix: restore original v0.0.1-beta dashboard design with dark theme
- Reverted to original secubox.css with dark theme aesthetic - Restored circular gauges with SVG in dark mode - Added stats overview cards (Total, Installed, Running, Alerts) - Kept auto-refresh functionality (30s interval) - Kept module dashboard links - Maintained dynamic updates - Fixed module paths mapping to correct dashboards This restores the beautiful dark UI that was present in v0.0.1-beta while keeping the improvements from v0.0.2-beta and v0.0.3-beta. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0d98ca759b
commit
0df9ebbcb4
@ -21,6 +21,55 @@
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
|
||||
/* Stats Overview */
|
||||
.secubox-stats-overview {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.secubox-stat-box {
|
||||
background: var(--sb-bg-card);
|
||||
border: 1px solid var(--sb-border);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.secubox-stat-box:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.secubox-stat-icon {
|
||||
font-size: 36px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.secubox-stat-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.secubox-stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--sb-text);
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.secubox-stat-label {
|
||||
font-size: 12px;
|
||||
color: var(--sb-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* SecuBox brand colors for each module */
|
||||
.sb-color-crowdsec { --module-color: #22c55e; }
|
||||
.sb-color-netdata { --module-color: #00ab44; }
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
document.head.appendChild(E('link', {
|
||||
'rel': 'stylesheet',
|
||||
'type': 'text/css',
|
||||
'href': L.resource('secubox/dashboard.css')
|
||||
'href': L.resource('secubox/secubox.css')
|
||||
}));
|
||||
|
||||
return view.extend({
|
||||
@ -37,30 +37,38 @@ return view.extend({
|
||||
|
||||
render: function(data) {
|
||||
var self = this;
|
||||
var container = E('div', { 'class': 'secubox-dashboard' });
|
||||
|
||||
// Header with version
|
||||
var status = this.dashboardData.status || {};
|
||||
container.appendChild(this.renderHeader(status));
|
||||
var modules = this.dashboardData.modules || [];
|
||||
var counts = this.dashboardData.counts || {};
|
||||
var alerts = this.alertsData.alerts || [];
|
||||
|
||||
// Stats Overview Cards
|
||||
container.appendChild(this.renderStatsOverview());
|
||||
var container = E('div', { 'class': 'cbi-map secubox-dashboard' });
|
||||
|
||||
// Main Content Grid
|
||||
var mainGrid = E('div', { 'class': 'secubox-main-grid' }, [
|
||||
// Left column
|
||||
E('div', { 'class': 'secubox-column-left' }, [
|
||||
this.renderSystemHealth(this.healthData),
|
||||
this.renderAlerts(this.alertsData.alerts || [])
|
||||
]),
|
||||
// Right column
|
||||
E('div', { 'class': 'secubox-column-right' }, [
|
||||
this.renderActiveModules(),
|
||||
this.renderQuickActions()
|
||||
])
|
||||
]);
|
||||
// Header
|
||||
container.appendChild(E('h2', {}, '🛡️ SecuBox Central Hub'));
|
||||
container.appendChild(E('p', {},
|
||||
'Hostname: ' + (status.hostname || 'Unknown') + ' | ' +
|
||||
'Uptime: ' + API.formatUptime(status.uptime) + ' | ' +
|
||||
'Load: ' + (status.load || '0.00') + ' | ' +
|
||||
'Version: ' + (status.version || 'v0.0.1-beta')
|
||||
));
|
||||
|
||||
container.appendChild(mainGrid);
|
||||
// Stats Overview
|
||||
container.appendChild(this.renderStatsOverview(counts, alerts));
|
||||
|
||||
// System Health Section
|
||||
container.appendChild(this.renderSystemHealth(this.healthData));
|
||||
|
||||
// Quick Actions Section
|
||||
container.appendChild(this.renderQuickActions());
|
||||
|
||||
// Alerts Section
|
||||
if (alerts.length > 0) {
|
||||
container.appendChild(this.renderAlerts(alerts));
|
||||
}
|
||||
|
||||
// Modules Grid
|
||||
container.appendChild(this.renderModulesGrid(modules));
|
||||
|
||||
// Start auto-refresh poll
|
||||
poll.add(function() {
|
||||
@ -72,237 +80,113 @@ return view.extend({
|
||||
return container;
|
||||
},
|
||||
|
||||
renderHeader: function(status) {
|
||||
return E('div', { 'class': 'secubox-header' }, [
|
||||
E('div', { 'class': 'secubox-header-content' }, [
|
||||
E('div', {}, [
|
||||
E('h2', {}, '🛡️ SecuBox Central Hub'),
|
||||
E('p', { 'class': 'secubox-subtitle' }, 'Security & Network Management Suite')
|
||||
]),
|
||||
E('div', { 'class': 'secubox-header-info' }, [
|
||||
E('span', { 'class': 'secubox-badge secubox-badge-version' },
|
||||
'v' + (status.version || '0.0.1-beta')),
|
||||
E('span', { 'class': 'secubox-badge' },
|
||||
'⏱️ ' + API.formatUptime(status.uptime)),
|
||||
E('span', { 'class': 'secubox-badge', 'id': 'secubox-hostname' },
|
||||
'🖥️ ' + (status.hostname || 'SecuBox'))
|
||||
renderStatsOverview: function(counts, alerts) {
|
||||
return E('div', { 'class': 'secubox-stats-overview' }, [
|
||||
E('div', { 'class': 'secubox-stat-box' }, [
|
||||
E('div', { 'class': 'secubox-stat-icon' }, '📦'),
|
||||
E('div', { 'class': 'secubox-stat-content' }, [
|
||||
E('div', { 'class': 'secubox-stat-value', 'id': 'stat-total' }, counts.total || 0),
|
||||
E('div', { 'class': 'secubox-stat-label' }, 'Total Modules')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'secubox-stat-box' }, [
|
||||
E('div', { 'class': 'secubox-stat-icon' }, '✅'),
|
||||
E('div', { 'class': 'secubox-stat-content' }, [
|
||||
E('div', { 'class': 'secubox-stat-value', 'id': 'stat-installed', 'style': 'color: #22c55e' }, counts.installed || 0),
|
||||
E('div', { 'class': 'secubox-stat-label' }, 'Installed')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'secubox-stat-box' }, [
|
||||
E('div', { 'class': 'secubox-stat-icon' }, '▶️'),
|
||||
E('div', { 'class': 'secubox-stat-content' }, [
|
||||
E('div', { 'class': 'secubox-stat-value', 'id': 'stat-running', 'style': 'color: #00ab44' }, counts.running || 0),
|
||||
E('div', { 'class': 'secubox-stat-label' }, 'Running')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'secubox-stat-box' }, [
|
||||
E('div', { 'class': 'secubox-stat-icon' }, '⚠️'),
|
||||
E('div', { 'class': 'secubox-stat-content' }, [
|
||||
E('div', { 'class': 'secubox-stat-value', 'id': 'stat-alerts', 'style': 'color: #f59e0b' }, alerts.length || 0),
|
||||
E('div', { 'class': 'secubox-stat-label' }, 'Alerts')
|
||||
])
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderStatsOverview: function() {
|
||||
var modules = this.dashboardData.modules || [];
|
||||
var counts = this.dashboardData.counts || {};
|
||||
|
||||
var stats = [
|
||||
{
|
||||
label: 'Total Modules',
|
||||
value: counts.total || modules.length,
|
||||
icon: '📦',
|
||||
color: '#6366f1',
|
||||
id: 'stat-total'
|
||||
},
|
||||
{
|
||||
label: 'Installed',
|
||||
value: counts.installed || 0,
|
||||
icon: '✓',
|
||||
color: '#22c55e',
|
||||
id: 'stat-installed'
|
||||
},
|
||||
{
|
||||
label: 'Running',
|
||||
value: counts.running || 0,
|
||||
icon: '▶',
|
||||
color: '#00ab44',
|
||||
id: 'stat-running'
|
||||
},
|
||||
{
|
||||
label: 'Alerts',
|
||||
value: (this.alertsData.alerts || []).length,
|
||||
icon: '⚠️',
|
||||
color: '#f59e0b',
|
||||
id: 'stat-alerts'
|
||||
}
|
||||
];
|
||||
|
||||
return E('div', { 'class': 'secubox-stats-grid' },
|
||||
stats.map(function(stat) {
|
||||
return E('div', {
|
||||
'class': 'secubox-stat-card',
|
||||
'style': 'border-top: 3px solid ' + stat.color
|
||||
}, [
|
||||
E('div', { 'class': 'secubox-stat-icon' }, stat.icon),
|
||||
E('div', { 'class': 'secubox-stat-content' }, [
|
||||
E('div', {
|
||||
'class': 'secubox-stat-value',
|
||||
'id': stat.id,
|
||||
'style': 'color: ' + stat.color
|
||||
}, stat.value),
|
||||
E('div', { 'class': 'secubox-stat-label' }, stat.label)
|
||||
])
|
||||
]);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
renderSystemHealth: function(health) {
|
||||
var cpu = health.cpu || {};
|
||||
var memory = health.memory || {};
|
||||
var disk = health.disk || {};
|
||||
var network = health.network || {};
|
||||
|
||||
return E('div', { 'class': 'secubox-card' }, [
|
||||
E('h3', { 'class': 'secubox-card-title' }, '📊 System Health'),
|
||||
var section = E('div', { 'class': 'secubox-health-section' }, [
|
||||
E('h3', {}, '📊 System Health'),
|
||||
E('div', { 'class': 'secubox-health-grid' }, [
|
||||
this.renderCircularGauge('CPU', cpu.percent || 0,
|
||||
'Load: ' + (cpu.load_1min || '0.00'), 'cpu', '#6366f1'),
|
||||
this.renderCircularGauge('Memory', memory.percent || 0,
|
||||
API.formatBytes((memory.used_kb || 0) * 1024) + ' / ' +
|
||||
API.formatBytes((memory.total_kb || 0) * 1024), 'memory', '#22c55e'),
|
||||
this.renderCircularGauge('Disk', disk.percent || 0,
|
||||
API.formatBytes((disk.used_kb || 0) * 1024) + ' / ' +
|
||||
API.formatBytes((disk.total_kb || 0) * 1024), 'disk', '#f59e0b')
|
||||
this.renderGauge('CPU', cpu.percent || 0, '%', 'Load: ' + (cpu.load_1min || '0.00'), 'cpu'),
|
||||
this.renderGauge('Memory', memory.percent || 0, '%',
|
||||
API.formatBytes((memory.used_kb || 0) * 1024) + ' / ' + API.formatBytes((memory.total_kb || 0) * 1024), 'memory'),
|
||||
this.renderGauge('Disk', disk.percent || 0, '%',
|
||||
API.formatBytes((disk.used_kb || 0) * 1024) + ' / ' + API.formatBytes((disk.total_kb || 0) * 1024), 'disk'),
|
||||
this.renderGauge('Network', 0, '',
|
||||
'RX: ' + API.formatBytes(network.rx_bytes || 0) + ' | TX: ' + API.formatBytes(network.tx_bytes || 0), 'network')
|
||||
])
|
||||
]);
|
||||
|
||||
return section;
|
||||
},
|
||||
|
||||
renderCircularGauge: function(label, percent, details, id, baseColor) {
|
||||
renderGauge: function(label, percent, unit, details, id) {
|
||||
var color = percent < 70 ? '#22c55e' : percent < 85 ? '#f59e0b' : '#ef4444';
|
||||
var radius = 45;
|
||||
var circumference = 2 * Math.PI * radius;
|
||||
var offset = circumference - (percent / 100) * circumference;
|
||||
|
||||
return E('div', { 'class': 'secubox-gauge-container' }, [
|
||||
E('div', { 'class': 'secubox-gauge' }, [
|
||||
E('svg', { 'viewBox': '0 0 120 120', 'class': 'secubox-gauge-svg' }, [
|
||||
// Background circle
|
||||
return E('div', { 'class': 'secubox-gauge' }, [
|
||||
E('div', { 'class': 'secubox-gauge-label' }, label),
|
||||
E('div', { 'class': 'secubox-gauge-chart' }, [
|
||||
E('svg', { 'width': '120', 'height': '120', 'viewBox': '0 0 120 120' }, [
|
||||
E('circle', {
|
||||
'cx': '60',
|
||||
'cy': '60',
|
||||
'r': radius,
|
||||
'fill': 'none',
|
||||
'stroke': '#f1f5f9',
|
||||
'stroke-width': '10'
|
||||
'cx': '60', 'cy': '60', 'r': '54',
|
||||
'fill': 'none', 'stroke': '#1e293b', 'stroke-width': '12'
|
||||
}),
|
||||
// Progress circle
|
||||
E('circle', {
|
||||
'id': 'gauge-' + id,
|
||||
'cx': '60',
|
||||
'cy': '60',
|
||||
'r': radius,
|
||||
'fill': 'none',
|
||||
'stroke': color,
|
||||
'stroke-width': '10',
|
||||
'id': 'gauge-circle-' + id,
|
||||
'cx': '60', 'cy': '60', 'r': '54',
|
||||
'fill': 'none', 'stroke': color, 'stroke-width': '12',
|
||||
'stroke-dasharray': (339.292 * percent / 100) + ' 339.292',
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-dasharray': circumference,
|
||||
'stroke-dashoffset': offset,
|
||||
'transform': 'rotate(-90 60 60)',
|
||||
'class': 'secubox-gauge-progress'
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'secubox-gauge-content' }, [
|
||||
E('div', {
|
||||
'class': 'secubox-gauge-percent',
|
||||
'id': 'gauge-' + id + '-percent',
|
||||
'style': 'color: ' + color
|
||||
}, Math.round(percent) + '%'),
|
||||
E('div', { 'class': 'secubox-gauge-label' }, label)
|
||||
'transform': 'rotate(-90 60 60)'
|
||||
}),
|
||||
E('text', {
|
||||
'id': 'gauge-text-' + id,
|
||||
'x': '60', 'y': '65', 'text-anchor': 'middle',
|
||||
'font-size': '24', 'font-weight': 'bold', 'fill': color
|
||||
}, Math.round(percent) + unit)
|
||||
])
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'secubox-gauge-details',
|
||||
'id': 'gauge-' + id + '-details'
|
||||
}, details)
|
||||
]);
|
||||
},
|
||||
|
||||
renderActiveModules: function() {
|
||||
var modules = this.dashboardData.modules || [];
|
||||
var activeModules = modules.filter(function(m) { return m.installed; });
|
||||
|
||||
// Map module IDs to their dashboard paths
|
||||
var modulePaths = {
|
||||
'crowdsec': 'admin/secubox/security/crowdsec',
|
||||
'netdata': 'admin/secubox/monitoring/netdata',
|
||||
'netifyd': 'admin/secubox/security/netifyd',
|
||||
'wireguard': 'admin/secubox/network/wireguard',
|
||||
'network_modes': 'admin/secubox/network/modes',
|
||||
'client_guardian': 'admin/secubox/security/guardian',
|
||||
'system_hub': 'admin/secubox/system/hub',
|
||||
'bandwidth_manager': 'admin/secubox/network/bandwidth',
|
||||
'auth_guardian': 'admin/secubox/security/auth',
|
||||
'media_flow': 'admin/secubox/network/media',
|
||||
'vhost_manager': 'admin/secubox/system/vhost',
|
||||
'traffic_shaper': 'admin/secubox/network/shaper',
|
||||
'cdn_cache': 'admin/secubox/network/cdn',
|
||||
'ksm_manager': 'admin/secubox/security/ksm'
|
||||
};
|
||||
|
||||
var moduleCards = activeModules.map(function(module) {
|
||||
var isRunning = module.running;
|
||||
var statusClass = isRunning ? 'running' : 'stopped';
|
||||
var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id);
|
||||
|
||||
return E('a', {
|
||||
'href': L.url(dashboardPath),
|
||||
'class': 'secubox-module-link secubox-module-' + statusClass
|
||||
}, [
|
||||
E('div', {
|
||||
'class': 'secubox-module-mini-card',
|
||||
'style': 'border-left: 4px solid ' + (module.color || '#64748b')
|
||||
}, [
|
||||
E('div', { 'class': 'secubox-module-mini-header' }, [
|
||||
E('span', { 'class': 'secubox-module-mini-icon' }, module.icon || '📦'),
|
||||
E('span', {
|
||||
'class': 'secubox-status-dot secubox-status-' + statusClass,
|
||||
'title': isRunning ? 'Running' : 'Stopped'
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'secubox-module-mini-body' }, [
|
||||
E('div', { 'class': 'secubox-module-mini-name' }, module.name || module.id),
|
||||
E('div', { 'class': 'secubox-module-mini-status' },
|
||||
isRunning ? '● Running' : '○ Stopped')
|
||||
])
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
return E('div', { 'class': 'secubox-card' }, [
|
||||
E('h3', { 'class': 'secubox-card-title' }, '🎯 Active Modules (' + activeModules.length + ')'),
|
||||
E('div', { 'class': 'secubox-modules-mini-grid' },
|
||||
moduleCards.length > 0 ? moduleCards : [
|
||||
E('p', { 'class': 'secubox-empty-state' }, 'No modules installed')
|
||||
]
|
||||
)
|
||||
E('div', { 'class': 'secubox-gauge-details', 'id': 'gauge-details-' + id }, details || '')
|
||||
]);
|
||||
},
|
||||
|
||||
renderQuickActions: function() {
|
||||
var self = this;
|
||||
var actions = [
|
||||
{ name: 'restart_rpcd', label: 'RPCD', icon: '🔄', color: '#6366f1' },
|
||||
{ name: 'restart_uhttpd', label: 'Web Server', icon: '🌐', color: '#00ab44' },
|
||||
{ name: 'restart_network', label: 'Network', icon: '📡', color: '#06b6d4' },
|
||||
{ name: 'restart_firewall', label: 'Firewall', icon: '🛡️', color: '#ef4444' },
|
||||
{ name: 'clear_cache', label: 'Clear Cache', icon: '🧹', color: '#f59e0b' },
|
||||
{ name: 'backup_config', label: 'Backup', icon: '💾', color: '#8b5cf6' }
|
||||
{ name: 'restart_rpcd', label: 'Restart RPCD', icon: '🔄' },
|
||||
{ name: 'restart_uhttpd', label: 'Restart uHTTPd', icon: '🌐' },
|
||||
{ name: 'clear_cache', label: 'Clear Cache', icon: '🧹' },
|
||||
{ name: 'backup_config', label: 'Backup Config', icon: '💾' },
|
||||
{ name: 'restart_network', label: 'Restart Network', icon: '📡' },
|
||||
{ name: 'restart_firewall', label: 'Restart Firewall', icon: '🛡️' }
|
||||
];
|
||||
|
||||
var buttons = actions.map(function(action) {
|
||||
return E('button', {
|
||||
'class': 'secubox-action-btn',
|
||||
'style': 'border-color: ' + action.color,
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'click': function() {
|
||||
self.executeQuickAction(action.name, action.label);
|
||||
}
|
||||
}, [
|
||||
E('span', { 'class': 'secubox-action-icon' }, action.icon),
|
||||
E('span', { 'class': 'secubox-action-label' }, action.label)
|
||||
]);
|
||||
}, action.icon + ' ' + action.label);
|
||||
});
|
||||
|
||||
return E('div', { 'class': 'secubox-card' }, [
|
||||
E('h3', { 'class': 'secubox-card-title' }, '⚡ Quick Actions'),
|
||||
return E('div', { 'class': 'secubox-quick-actions' }, [
|
||||
E('h3', {}, '⚡ Quick Actions'),
|
||||
E('div', { 'class': 'secubox-actions-grid' }, buttons)
|
||||
]);
|
||||
},
|
||||
@ -333,46 +217,89 @@ return view.extend({
|
||||
},
|
||||
|
||||
renderAlerts: function(alerts) {
|
||||
if (!alerts || alerts.length === 0) {
|
||||
return E('div', { 'class': 'secubox-card' }, [
|
||||
E('h3', { 'class': 'secubox-card-title' }, '✓ System Status'),
|
||||
E('div', { 'class': 'secubox-alert secubox-alert-success' }, [
|
||||
E('span', {}, '✓ All systems operational')
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
var alertItems = alerts.slice(0, 5).map(function(alert) {
|
||||
var severityClass = 'secubox-alert-' + (alert.severity || 'info');
|
||||
var severityIcon = alert.severity === 'error' ? '✗' :
|
||||
alert.severity === 'warning' ? '⚠️' : 'ℹ️';
|
||||
return E('div', { 'class': 'secubox-alert ' + severityClass }, [
|
||||
E('span', { 'class': 'secubox-alert-icon' }, severityIcon),
|
||||
E('div', { 'class': 'secubox-alert-content' }, [
|
||||
E('strong', {}, alert.module || 'System'),
|
||||
E('span', {}, ': ' + alert.message)
|
||||
E('strong', {}, (alert.module || 'System') + ': '),
|
||||
E('span', {}, alert.message || '')
|
||||
]);
|
||||
});
|
||||
|
||||
return E('div', { 'class': 'secubox-alerts-section' }, [
|
||||
E('h3', {}, '⚠️ Recent Alerts (' + alerts.length + ')'),
|
||||
E('div', { 'class': 'secubox-alerts-list' }, alertItems),
|
||||
alerts.length > 5 ? E('a', {
|
||||
'href': L.url('admin/secubox/alerts'),
|
||||
'class': 'cbi-button cbi-button-link',
|
||||
'style': 'margin-top: 12px; display: inline-block;'
|
||||
}, 'View All Alerts →') : null
|
||||
]);
|
||||
},
|
||||
|
||||
renderModulesGrid: function(modules) {
|
||||
// Map module IDs to their dashboard paths
|
||||
var modulePaths = {
|
||||
'crowdsec': 'admin/secubox/security/crowdsec',
|
||||
'netdata': 'admin/secubox/monitoring/netdata',
|
||||
'netifyd': 'admin/secubox/security/netifyd',
|
||||
'wireguard': 'admin/secubox/network/wireguard',
|
||||
'network_modes': 'admin/secubox/network/modes',
|
||||
'client_guardian': 'admin/secubox/security/guardian',
|
||||
'system_hub': 'admin/secubox/system/hub',
|
||||
'bandwidth_manager': 'admin/secubox/network/bandwidth',
|
||||
'auth_guardian': 'admin/secubox/security/auth',
|
||||
'media_flow': 'admin/secubox/network/media',
|
||||
'vhost_manager': 'admin/secubox/services/vhost',
|
||||
'traffic_shaper': 'admin/secubox/network/shaper',
|
||||
'cdn_cache': 'admin/secubox/services/cdn',
|
||||
'ksm_manager': 'admin/secubox/security/ksm'
|
||||
};
|
||||
|
||||
var moduleCards = modules.map(function(module) {
|
||||
var statusClass = module.installed ? (module.running ? 'running' : 'stopped') : 'not-installed';
|
||||
var statusIcon = module.installed ? (module.running ? '✓' : '✗') : '○';
|
||||
var statusColor = module.installed ? (module.running ? '#22c55e' : '#ef4444') : '#64748b';
|
||||
var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id);
|
||||
|
||||
return E('div', {
|
||||
'class': 'secubox-module-card',
|
||||
'data-status': statusClass
|
||||
}, [
|
||||
E('div', { 'class': 'secubox-module-header' }, [
|
||||
E('div', {
|
||||
'class': 'secubox-module-icon',
|
||||
'style': 'background-color: ' + (module.color || '#64748b')
|
||||
}, module.icon || '📦'),
|
||||
E('div', {
|
||||
'class': 'secubox-module-status',
|
||||
'style': 'color: ' + statusColor
|
||||
}, statusIcon)
|
||||
]),
|
||||
E('div', { 'class': 'secubox-module-body' }, [
|
||||
E('div', { 'class': 'secubox-module-name' }, module.name || module.id),
|
||||
E('div', { 'class': 'secubox-module-description' }, module.description || ''),
|
||||
E('div', { 'class': 'secubox-module-category' }, module.category || 'other')
|
||||
]),
|
||||
E('div', { 'class': 'secubox-module-footer' }, [
|
||||
module.installed ? E('a', {
|
||||
'href': L.url(dashboardPath),
|
||||
'class': 'cbi-button cbi-button-link'
|
||||
}, 'Open Dashboard') : E('span', { 'class': 'secubox-not-installed' }, 'Not Installed')
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
return E('div', { 'class': 'secubox-card' }, [
|
||||
E('h3', { 'class': 'secubox-card-title' },
|
||||
'⚠️ Alerts (' + alerts.length + ')'),
|
||||
E('div', { 'class': 'secubox-alerts-list' }, alertItems),
|
||||
alerts.length > 5 ? E('a', {
|
||||
'href': '#',
|
||||
'class': 'secubox-view-all',
|
||||
'click': function(ev) {
|
||||
ev.preventDefault();
|
||||
window.location.hash = '#admin/secubox/alerts';
|
||||
}
|
||||
}, 'View all alerts →') : null
|
||||
return E('div', { 'class': 'secubox-modules-section' }, [
|
||||
E('h3', {}, '🎯 Active Modules (' + modules.filter(function(m) { return m.installed; }).length + ')'),
|
||||
E('div', { 'class': 'secubox-modules-grid' }, moduleCards)
|
||||
]);
|
||||
},
|
||||
|
||||
updateDynamicElements: function() {
|
||||
// Update stats
|
||||
var counts = this.dashboardData.counts || {};
|
||||
var alerts = this.alertsData.alerts || [];
|
||||
|
||||
var totalEl = document.getElementById('stat-total');
|
||||
var installedEl = document.getElementById('stat-installed');
|
||||
var runningEl = document.getElementById('stat-running');
|
||||
@ -381,44 +308,39 @@ return view.extend({
|
||||
if (totalEl) totalEl.textContent = counts.total || 0;
|
||||
if (installedEl) installedEl.textContent = counts.installed || 0;
|
||||
if (runningEl) runningEl.textContent = counts.running || 0;
|
||||
if (alertsEl) alertsEl.textContent = (this.alertsData.alerts || []).length;
|
||||
if (alertsEl) alertsEl.textContent = alerts.length || 0;
|
||||
|
||||
// Update health bars
|
||||
// Update health gauges
|
||||
var health = this.healthData;
|
||||
this.updateHealthBar('cpu', health.cpu);
|
||||
this.updateHealthBar('memory', health.memory);
|
||||
this.updateHealthBar('disk', health.disk);
|
||||
this.updateGauge('cpu', health.cpu);
|
||||
this.updateGauge('memory', health.memory);
|
||||
this.updateGauge('disk', health.disk);
|
||||
},
|
||||
|
||||
updateHealthBar: function(type, data) {
|
||||
updateGauge: function(id, data) {
|
||||
if (!data) return;
|
||||
|
||||
var percent = data.percent || 0;
|
||||
var color = percent < 70 ? '#22c55e' : percent < 85 ? '#f59e0b' : '#ef4444';
|
||||
|
||||
var percentEl = document.getElementById('gauge-' + type + '-percent');
|
||||
var gaugeEl = document.getElementById('gauge-' + type);
|
||||
var circleEl = document.getElementById('gauge-circle-' + id);
|
||||
var textEl = document.getElementById('gauge-text-' + id);
|
||||
var detailsEl = document.getElementById('gauge-details-' + id);
|
||||
|
||||
if (percentEl) {
|
||||
percentEl.textContent = Math.round(percent) + '%';
|
||||
percentEl.style.color = color;
|
||||
if (circleEl) {
|
||||
circleEl.setAttribute('stroke', color);
|
||||
circleEl.setAttribute('stroke-dasharray', (339.292 * percent / 100) + ' 339.292');
|
||||
}
|
||||
if (gaugeEl) {
|
||||
var radius = 45;
|
||||
var circumference = 2 * Math.PI * radius;
|
||||
var offset = circumference - (percent / 100) * circumference;
|
||||
gaugeEl.setAttribute('stroke-dashoffset', offset);
|
||||
gaugeEl.setAttribute('stroke', color);
|
||||
if (textEl) {
|
||||
textEl.setAttribute('fill', color);
|
||||
textEl.textContent = Math.round(percent) + (id === 'network' ? '' : '%');
|
||||
}
|
||||
|
||||
// Update details
|
||||
var detailsEl = document.getElementById('gauge-' + type + '-details');
|
||||
if (detailsEl && type === 'cpu') {
|
||||
if (detailsEl && id === 'cpu') {
|
||||
detailsEl.textContent = 'Load: ' + (data.load_1min || '0.00');
|
||||
} else if (detailsEl && type === 'memory') {
|
||||
} else if (detailsEl && id === 'memory') {
|
||||
detailsEl.textContent = API.formatBytes((data.used_kb || 0) * 1024) + ' / ' +
|
||||
API.formatBytes((data.total_kb || 0) * 1024);
|
||||
} else if (detailsEl && type === 'disk') {
|
||||
} else if (detailsEl && id === 'disk') {
|
||||
detailsEl.textContent = API.formatBytes((data.used_kb || 0) * 1024) + ' / ' +
|
||||
API.formatBytes((data.total_kb || 0) * 1024);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user