secubox-openwrt/package/secubox/luci-app-iot-guard/htdocs/luci-static/resources/view/iot-guard/overview.js
CyberMind-FR edca533f07 feat(ui): Apply KISS theme with C3BOX sidebar to SecuBox views
- InterceptoR: Refactor to use shared KissTheme.wrap() module
  - Remove duplicate inline CSS (~200 lines)
  - Use shared theme for sidebar navigation

- IoT Guard: Update to KISS dark theme styling
  - Use KissTheme.wrap() with sidebar
  - Update stat cards to use KISS classes
  - Update device chips and anomaly table styling

- mitmproxy: Add KISS theme wrapper
  - Add KissTheme.wrap() for sidebar navigation
  - Update info card styling to match theme

- System Hub: Update to KISS theme
  - Add KissTheme.wrap() for sidebar navigation
  - Update quick actions to use kiss-btn class
  - Inject KISS-compatible extra styles for cards

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-11 12:09:42 +01:00

267 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
'require view';
'require rpc';
'require poll';
'require secubox/kiss-theme';
var callStatus = rpc.declare({
object: 'luci.iot-guard',
method: 'status',
expect: {}
});
var callGetDevices = rpc.declare({
object: 'luci.iot-guard',
method: 'get_devices',
expect: {}
});
var callGetAnomalies = rpc.declare({
object: 'luci.iot-guard',
method: 'get_anomalies',
params: ['limit'],
expect: {}
});
var callScan = rpc.declare({
object: 'luci.iot-guard',
method: 'scan',
expect: {}
});
// Device class icons
var CLASS_ICONS = {
camera: '📷',
thermostat: '🌡️',
lighting: '💡',
plug: '🔌',
assistant: '🎤',
media: '📺',
lock: '🔒',
sensor: '📡',
diy: '🛠️',
mixed: '🔗',
unknown: '❓'
};
return view.extend({
handleScan: function() {
return callScan().then(function() {
window.location.reload();
});
},
load: function() {
return Promise.all([
callStatus(),
callGetDevices(),
callGetAnomalies(5)
]);
},
render: function(data) {
var status = data[0] || {};
var devices = (data[1] && data[1].devices) || [];
var anomalies = (data[2] && data[2].anomalies) || [];
var self = this;
// Poll for updates
poll.add(L.bind(function() {
return callStatus().then(L.bind(function(status) {
this.updateStats(status);
}, this));
}, this), 10);
var content = [
// Header
E('div', { 'style': 'margin-bottom: 24px;' }, [
E('h2', { 'style': 'font-size: 24px; font-weight: 700; margin: 0 0 8px 0;' }, '📡 IoT Guard'),
E('p', { 'style': 'color: var(--kiss-muted); margin: 0;' }, 'Device Isolation & Security Monitoring')
]),
// Stats Grid
E('div', { 'class': 'kiss-grid kiss-grid-auto', 'style': 'margin-bottom: 24px;' }, [
this.renderStatCard('Devices', status.total_devices || 0, 'var(--kiss-blue)'),
this.renderStatCard('Isolated', status.isolated || 0, 'var(--kiss-yellow)'),
this.renderStatCard('Blocked', status.blocked || 0, 'var(--kiss-red)'),
this.renderStatCard('High Risk', status.high_risk || 0, 'var(--kiss-red)'),
this.renderStatCard('Anomalies', status.anomalies || 0, 'var(--kiss-orange)'),
this.renderScoreCard('Security Score', status.security_score || 0)
]),
// Action buttons
E('div', { 'class': 'kiss-card', 'style': 'display: flex; gap: 12px; flex-wrap: wrap;' }, [
E('button', {
'class': 'kiss-btn kiss-btn-green',
'click': L.bind(this.handleScan, this)
}, '🔍 Scan Network'),
E('a', {
'href': L.url('admin/secubox/services/iot-guard/devices'),
'class': 'kiss-btn',
'style': 'text-decoration: none;'
}, '📋 View All Devices'),
E('a', {
'href': L.url('admin/secubox/services/iot-guard/settings'),
'class': 'kiss-btn',
'style': 'text-decoration: none;'
}, ' Settings')
]),
// Device Grid by Risk
E('div', { 'class': 'kiss-card' }, [
E('div', { 'class': 'kiss-card-title' }, '🎯 Devices by Risk Level'),
this.renderDeviceGrid(devices)
]),
// Recent Anomalies
E('div', { 'class': 'kiss-card' }, [
E('div', { 'class': 'kiss-card-title' }, ' Recent Anomalies'),
this.renderAnomaliesTable(anomalies)
])
];
return KissTheme.wrap(content, 'admin/secubox/services/iot-guard');
},
renderStatCard: function(label, value, color) {
return E('div', { 'class': 'kiss-stat', 'style': 'border-left: 3px solid ' + color + ';' }, [
E('div', {
'class': 'kiss-stat-value',
'style': 'color: ' + color + ';',
'data-stat': label.toLowerCase().replace(' ', '-')
}, String(value)),
E('div', { 'class': 'kiss-stat-label' }, label)
]);
},
renderScoreCard: function(label, score) {
var color = score >= 70 ? 'var(--kiss-green)' : (score >= 40 ? 'var(--kiss-yellow)' : 'var(--kiss-red)');
return E('div', { 'class': 'kiss-stat', 'style': 'border-left: 3px solid ' + color + ';' }, [
E('div', {
'class': 'kiss-stat-value',
'style': 'color: ' + color + ';',
'data-stat': 'security-score'
}, score + '%'),
E('div', { 'class': 'kiss-stat-label' }, label)
]);
},
renderDeviceGrid: function(devices) {
var self = this;
var RISK_COLORS = {
high: 'var(--kiss-red)',
medium: 'var(--kiss-yellow)',
low: 'var(--kiss-green)',
unknown: 'var(--kiss-muted)'
};
var groups = { high: [], medium: [], low: [], unknown: [] };
devices.forEach(function(d) {
var risk = d.risk_level || 'unknown';
if (groups[risk]) {
groups[risk].push(d);
} else {
groups.unknown.push(d);
}
});
var rows = [];
['high', 'medium', 'low'].forEach(function(risk) {
if (groups[risk].length === 0) return;
rows.push(E('div', { 'style': 'margin-bottom: 16px;' }, [
E('div', {
'style': 'color: ' + RISK_COLORS[risk] + '; font-weight: 600; margin-bottom: 10px; font-size: 12px; text-transform: uppercase; letter-spacing: 1px;'
}, risk + ' Risk (' + groups[risk].length + ')'),
E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 10px;' },
groups[risk].slice(0, 12).map(function(d) {
return self.renderDeviceChip(d, RISK_COLORS);
})
)
]));
});
if (rows.length === 0) {
return E('div', { 'style': 'color: var(--kiss-muted); padding: 30px; text-align: center;' }, [
E('div', { 'style': 'font-size: 48px; margin-bottom: 12px;' }, '📡'),
E('p', {}, 'No IoT devices detected.'),
E('p', { 'style': 'font-size: 12px;' }, 'Click "Scan Network" to discover devices.')
]);
}
return E('div', {}, rows);
},
renderDeviceChip: function(device, RISK_COLORS) {
var icon = CLASS_ICONS[device.device_class] || CLASS_ICONS.unknown;
var color = RISK_COLORS[device.risk_level] || RISK_COLORS.unknown;
var statusBadge = '';
if (device.isolated) statusBadge = ' 🔸';
else if (device.blocked) statusBadge = ' 🚫';
else if (device.trusted) statusBadge = ' ';
var label = device.hostname || device.ip || device.mac.substring(9);
return E('a', {
'href': L.url('admin/secubox/services/iot-guard/devices') + '?mac=' + encodeURIComponent(device.mac),
'class': 'kiss-btn',
'style': 'text-decoration: none; border-color: ' + color + '66;',
'title': device.vendor + ' - ' + device.mac
}, [
E('span', {}, icon),
E('span', {}, label.length > 15 ? label.substring(0, 12) + '...' : label),
statusBadge ? E('span', { 'style': 'font-size: 10px;' }, statusBadge) : ''
]);
},
renderAnomaliesTable: function(anomalies) {
if (!anomalies || anomalies.length === 0) {
return E('div', { 'style': 'text-align: center; padding: 30px; color: var(--kiss-green);' }, [
E('div', { 'style': 'font-size: 48px; margin-bottom: 12px;' }, ''),
E('p', {}, 'No recent anomalies detected')
]);
}
var rows = anomalies.map(function(a) {
var sevColor = a.severity === 'high' ? 'var(--kiss-red)' : (a.severity === 'medium' ? 'var(--kiss-yellow)' : 'var(--kiss-muted)');
return E('tr', {}, [
E('td', { 'class': 'td' }, a.timestamp ? a.timestamp.substring(11, 16) : '-'),
E('td', { 'class': 'td' }, a.hostname || a.mac),
E('td', { 'class': 'td' }, a.type),
E('td', { 'class': 'td', 'style': 'color: ' + sevColor + '; font-weight: 600;' }, a.severity),
E('td', { 'class': 'td', 'style': 'color: var(--kiss-muted);' }, a.description)
]);
});
return E('table', { 'class': 'kiss-table' }, [
E('thead', {}, E('tr', {}, [
E('th', { 'class': 'th' }, 'Time'),
E('th', { 'class': 'th' }, 'Device'),
E('th', { 'class': 'th' }, 'Type'),
E('th', { 'class': 'th' }, 'Severity'),
E('th', { 'class': 'th' }, 'Description')
])),
E('tbody', {}, rows)
]);
},
updateStats: function(status) {
var updates = {
'devices': status.total_devices,
'isolated': status.isolated,
'blocked': status.blocked,
'high-risk': status.high_risk,
'anomalies': status.anomalies,
'security-score': status.security_score + '%'
};
Object.keys(updates).forEach(function(key) {
var el = document.querySelector('[data-stat="' + key + '"]');
if (el) el.textContent = String(updates[key]);
});
}
});