feat(security-threats): Add Firewall & Network stats to dashboard
- Add get_security_stats RPC to API - Add renderFirewallStats section with 6 stat cards: - WAN Dropped packets - Firewall Rejects - CrowdSec Bans - CrowdSec Alerts 24h - Invalid Connections - HAProxy Connections - Visual gradient cards with formatted numbers (K/M suffixes) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a1bad31807
commit
1a4096fd2e
@ -64,6 +64,12 @@ var callRemoveWhitelist = rpc.declare({
|
||||
expect: { }
|
||||
});
|
||||
|
||||
var callGetSecurityStats = rpc.declare({
|
||||
object: 'luci.secubox-security-threats',
|
||||
method: 'get_security_stats',
|
||||
expect: { }
|
||||
});
|
||||
|
||||
// ==============================================================================
|
||||
// Utility Functions
|
||||
// ==============================================================================
|
||||
@ -212,13 +218,15 @@ function getDashboardData() {
|
||||
callStatus(),
|
||||
callGetActiveThreats(),
|
||||
callGetStatsByType(),
|
||||
callGetBlockedIPs()
|
||||
callGetBlockedIPs(),
|
||||
callGetSecurityStats()
|
||||
]).then(function(results) {
|
||||
return {
|
||||
status: results[0] || {},
|
||||
threats: results[1].threats || [],
|
||||
stats: results[2] || {},
|
||||
blocked: results[3].blocked || []
|
||||
blocked: results[3].blocked || [],
|
||||
securityStats: results[4] || {}
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -235,6 +243,7 @@ return baseclass.extend({
|
||||
getStatsByType: callGetStatsByType,
|
||||
getStatsByHost: callGetStatsByHost,
|
||||
getBlockedIPs: callGetBlockedIPs,
|
||||
getSecurityStats: callGetSecurityStats,
|
||||
blockThreat: callBlockThreat,
|
||||
whitelistHost: callWhitelistHost,
|
||||
removeWhitelist: callRemoveWhitelist,
|
||||
|
||||
@ -16,6 +16,7 @@ return L.view.extend({
|
||||
var status = data.status || {};
|
||||
var stats = data.stats || {};
|
||||
var blocked = data.blocked || [];
|
||||
var securityStats = data.securityStats || {};
|
||||
|
||||
// Calculate statistics
|
||||
var threatStats = {
|
||||
@ -30,6 +31,7 @@ return L.view.extend({
|
||||
|
||||
// Build view elements
|
||||
var statusBanner = this.renderStatusBanner(status);
|
||||
var fwStatsGrid = this.renderFirewallStats(securityStats);
|
||||
var statsGrid = this.renderStatsGrid(threatStats, blocked.length);
|
||||
var threatDist = this.renderThreatDistribution(stats);
|
||||
var riskGauge = this.renderRiskGauge(threatStats.avg_score);
|
||||
@ -46,7 +48,11 @@ return L.view.extend({
|
||||
E('div', { 'class': 'cbi-map-descr' }, _('Real-time threat detection integrating netifyd DPI and CrowdSec intelligence')),
|
||||
statusBanner,
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, _('Overview')),
|
||||
E('h3', {}, _('Firewall & Network Protection')),
|
||||
fwStatsGrid
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, _('Threat Overview')),
|
||||
statsGrid
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section', 'style': 'display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;' }, [
|
||||
@ -60,6 +66,61 @@ return L.view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
renderFirewallStats: function(stats) {
|
||||
var formatNumber = function(n) {
|
||||
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
||||
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
|
||||
return n.toString();
|
||||
};
|
||||
|
||||
return E('div', {
|
||||
'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin-bottom: 1rem;'
|
||||
}, [
|
||||
E('div', {
|
||||
'style': 'background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%); padding: 1.2rem; border-radius: 12px; color: white; text-align: center;'
|
||||
}, [
|
||||
E('div', { 'style': 'font-size: 2.5rem; font-weight: bold;' }, formatNumber(stats.wan_dropped || 0)),
|
||||
E('div', { 'style': 'font-size: 0.9rem; opacity: 0.9; margin-top: 0.3rem;' }, _('WAN Dropped')),
|
||||
E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7; margin-top: 0.2rem;' }, _('Packets blocked at interface'))
|
||||
]),
|
||||
E('div', {
|
||||
'style': 'background: linear-gradient(135deg, #c62828 0%, #e53935 100%); padding: 1.2rem; border-radius: 12px; color: white; text-align: center;'
|
||||
}, [
|
||||
E('div', { 'style': 'font-size: 2.5rem; font-weight: bold;' }, formatNumber(stats.firewall_rejects || 0)),
|
||||
E('div', { 'style': 'font-size: 0.9rem; opacity: 0.9; margin-top: 0.3rem;' }, _('FW Rejects')),
|
||||
E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7; margin-top: 0.2rem;' }, _('Firewall rule blocks'))
|
||||
]),
|
||||
E('div', {
|
||||
'style': 'background: linear-gradient(135deg, #6a1b9a 0%, #8e24aa 100%); padding: 1.2rem; border-radius: 12px; color: white; text-align: center;'
|
||||
}, [
|
||||
E('div', { 'style': 'font-size: 2.5rem; font-weight: bold;' }, formatNumber(stats.crowdsec_bans || 0)),
|
||||
E('div', { 'style': 'font-size: 0.9rem; opacity: 0.9; margin-top: 0.3rem;' }, _('CrowdSec Bans')),
|
||||
E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7; margin-top: 0.2rem;' }, _('Active IP bans'))
|
||||
]),
|
||||
E('div', {
|
||||
'style': 'background: linear-gradient(135deg, #ef6c00 0%, #ff9800 100%); padding: 1.2rem; border-radius: 12px; color: white; text-align: center;'
|
||||
}, [
|
||||
E('div', { 'style': 'font-size: 2.5rem; font-weight: bold;' }, formatNumber(stats.crowdsec_alerts_24h || 0)),
|
||||
E('div', { 'style': 'font-size: 0.9rem; opacity: 0.9; margin-top: 0.3rem;' }, _('Alerts 24h')),
|
||||
E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7; margin-top: 0.2rem;' }, _('CrowdSec detections'))
|
||||
]),
|
||||
E('div', {
|
||||
'style': 'background: linear-gradient(135deg, #455a64 0%, #607d8b 100%); padding: 1.2rem; border-radius: 12px; color: white; text-align: center;'
|
||||
}, [
|
||||
E('div', { 'style': 'font-size: 2.5rem; font-weight: bold;' }, formatNumber(stats.invalid_connections || 0)),
|
||||
E('div', { 'style': 'font-size: 0.9rem; opacity: 0.9; margin-top: 0.3rem;' }, _('Invalid Conns')),
|
||||
E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7; margin-top: 0.2rem;' }, _('Conntrack anomalies'))
|
||||
]),
|
||||
E('div', {
|
||||
'style': 'background: linear-gradient(135deg, #00695c 0%, #00897b 100%); padding: 1.2rem; border-radius: 12px; color: white; text-align: center;'
|
||||
}, [
|
||||
E('div', { 'style': 'font-size: 2.5rem; font-weight: bold;' }, formatNumber(stats.haproxy_connections || 0)),
|
||||
E('div', { 'style': 'font-size: 0.9rem; opacity: 0.9; margin-top: 0.3rem;' }, _('HAProxy Conns')),
|
||||
E('div', { 'style': 'font-size: 0.75rem; opacity: 0.7; margin-top: 0.2rem;' }, _('Reverse proxy sessions'))
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderStatusBanner: function(status) {
|
||||
var services = [];
|
||||
var hasIssue = false;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user