fix(client-guardian): Restore original menu path

This commit is contained in:
CyberMind-FR 2026-02-10 16:54:31 +01:00
parent e3d7873d7b
commit 0e5965dd6c
3 changed files with 138 additions and 505 deletions

View File

@ -2,16 +2,8 @@
'require view'; 'require view';
'require dom'; 'require dom';
'require poll'; 'require poll';
'require uci';
'require ui';
'require rpc'; 'require rpc';
'require client-guardian/nav as CgNav'; 'require ui';
'require secubox-portal/header as SbHeader';
var callGetStatus = rpc.declare({
object: 'luci.client-guardian',
method: 'status'
});
var callGetClients = rpc.declare({ var callGetClients = rpc.declare({
object: 'luci.client-guardian', object: 'luci.client-guardian',
@ -19,12 +11,6 @@ var callGetClients = rpc.declare({
expect: { clients: [] } expect: { clients: [] }
}); });
var callGetZones = rpc.declare({
object: 'luci.client-guardian',
method: 'zones',
expect: { zones: [] }
});
var callApproveClient = rpc.declare({ var callApproveClient = rpc.declare({
object: 'luci.client-guardian', object: 'luci.client-guardian',
method: 'approve_client', method: 'approve_client',
@ -37,297 +23,149 @@ var callBanClient = rpc.declare({
params: ['mac', 'reason'] params: ['mac', 'reason']
}); });
function formatBytes(bytes) { var callUnbanClient = rpc.declare({
if (!bytes || bytes === 0) return '0 B'; object: 'luci.client-guardian',
var units = ['B', 'KB', 'MB', 'GB', 'TB']; method: 'unban_client',
var i = Math.floor(Math.log(bytes) / Math.log(1024)); params: ['mac']
i = Math.min(i, units.length - 1); });
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + units[i];
}
function getDeviceIcon(hostname, mac) {
hostname = (hostname || '').toLowerCase();
mac = (mac || '').toLowerCase();
if (hostname.match(/android|iphone|ipad|mobile|phone|samsung|xiaomi|huawei/)) return '📱';
if (hostname.match(/pc|laptop|desktop|macbook|imac|windows|linux|ubuntu/)) return '💻';
if (hostname.match(/camera|bulb|switch|sensor|thermostat|doorbell|lock/)) return '📷';
if (hostname.match(/tv|roku|chromecast|firestick|appletv|media/)) return '📺';
if (hostname.match(/playstation|xbox|nintendo|switch|steam/)) return '🎮';
if (hostname.match(/router|switch|ap|access[-_]?point|bridge/)) return '🌐';
if (hostname.match(/printer|print|hp-|canon-|epson-/)) return '🖨️';
return '🔌';
}
return view.extend({ return view.extend({
load: function() { load: function() {
return Promise.all([ return callGetClients();
callGetStatus(),
callGetClients(),
callGetZones(),
uci.load('client-guardian')
]);
}, },
render: function(data) { render: function(data) {
var status = data[0]; var clients = Array.isArray(data) ? data : (data.clients || []);
var clients = Array.isArray(data[1]) ? data[1] : (data[1].clients || []); var online = clients.filter(function(c) { return c.online; }).length;
var zones = Array.isArray(data[2]) ? data[2] : (data[2].zones || []); var approved = clients.filter(function(c) { return c.status === 'approved'; }).length;
var banned = clients.filter(function(c) { return c.status === 'banned'; }).length;
var onlineClients = clients.filter(function(c) { return c.online; }); var view = E('div', { 'class': 'cbi-map' }, [
var approvedClients = clients.filter(function(c) { return c.status === 'approved'; }); E('h2', {}, 'Client Guardian'),
var quarantineClients = clients.filter(function(c) { return c.status === 'unknown' || c.zone === 'quarantine'; }); E('div', { 'class': 'cbi-map-descr' }, 'Network client management'),
var bannedClients = clients.filter(function(c) { return c.status === 'banned'; });
// Main wrapper with SecuBox header // Stats
var wrapper = E('div', { 'class': 'secubox-page-wrapper' }); E('div', { 'style': 'display:flex;gap:20px;margin:20px 0;' }, [
wrapper.appendChild(SbHeader.render()); E('div', { 'style': 'padding:15px;background:#22c55e22;border-radius:8px;' }, [
E('strong', { 'style': 'font-size:24px;color:#22c55e;' }, String(online)),
var view = E('div', { 'class': 'client-guardian-dashboard' }, [ E('div', {}, 'Online')
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('client-guardian/dashboard.css') }),
CgNav.renderTabs('overview'),
// Chip Header
this.renderChipHeader(onlineClients.length, approvedClients.length, quarantineClients.length,
bannedClients.length, clients.filter(function(c) { return c.has_threats; }).length, zones.length),
// Recent Clients Card
E('div', { 'class': 'cg-card' }, [
E('div', { 'class': 'cg-card-header' }, [
E('div', { 'class': 'cg-card-title' }, [
E('span', { 'class': 'cg-card-title-icon' }, '⚡'),
'Clients Récents'
]),
E('span', { 'class': 'cg-card-badge' }, 'Temps réel')
]), ]),
E('div', { 'class': 'cg-card-body' }, [ E('div', { 'style': 'padding:15px;background:#3b82f622;border-radius:8px;' }, [
E('div', { 'class': 'cg-client-list' }, E('strong', { 'style': 'font-size:24px;color:#3b82f6;' }, String(approved)),
onlineClients.slice(0, 5).map(L.bind(this.renderClientItem, this, false)) E('div', {}, 'Approved')
) ]),
E('div', { 'style': 'padding:15px;background:#ef444422;border-radius:8px;' }, [
E('strong', { 'style': 'font-size:24px;color:#ef4444;' }, String(banned)),
E('div', {}, 'Banned')
]) ])
]), ]),
// Pending Approval Card // Client Table
quarantineClients.length > 0 ? E('div', { 'class': 'cg-card' }, [ E('div', { 'class': 'cbi-section' }, [
E('div', { 'class': 'cg-card-header' }, [ E('table', { 'class': 'table', 'id': 'client-table' }, [
E('div', { 'class': 'cg-card-title' }, [ E('tr', { 'class': 'tr table-titles' }, [
E('span', { 'class': 'cg-card-title-icon' }, '⏳'), E('th', { 'class': 'th' }, 'Status'),
'En Attente d\'Approbation' E('th', { 'class': 'th' }, 'Name'),
]), E('th', { 'class': 'th' }, 'MAC'),
E('span', { 'class': 'cg-card-badge' }, quarantineClients.length + ' clients') E('th', { 'class': 'th' }, 'IP'),
]), E('th', { 'class': 'th' }, 'Actions')
E('div', { 'class': 'cg-card-body' }, [ ])
E('div', { 'class': 'cg-client-list' }, ].concat(clients.map(L.bind(this.renderClientRow, this))))
quarantineClients.map(L.bind(this.renderClientItem, this, true))
)
])
]) : E('div')
]);
// Setup auto-refresh polling based on UCI settings
var autoRefresh = uci.get('client-guardian', 'config', 'auto_refresh');
var refreshInterval = parseInt(uci.get('client-guardian', 'config', 'refresh_interval') || '10');
if (autoRefresh === '1') {
poll.add(L.bind(function() {
return this.handleRefresh();
}, this), refreshInterval);
}
wrapper.appendChild(view);
return wrapper;
},
renderHeaderChip: function(stat) {
return E('div', { 'class': 'sh-header-chip' + (stat.tone ? ' ' + stat.tone : '') }, [
E('span', { 'class': 'sh-chip-icon' }, stat.icon || '•'),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, stat.label),
E('strong', {}, String(stat.value))
])
]);
},
renderChipHeader: function(online, approved, quarantine, banned, threats, zones) {
var stats = [
{ icon: '📱', label: _('Online'), value: online, tone: online > 0 ? 'success' : '' },
{ icon: '✅', label: _('Approved'), value: approved },
{ icon: '⏳', label: _('Quarantine'), value: quarantine, tone: quarantine > 0 ? 'warn' : '' },
{ icon: '🚫', label: _('Banned'), value: banned, tone: banned > 0 ? 'error' : '' },
{ icon: '⚠️', label: _('Threats'), value: threats, tone: threats > 0 ? 'error' : '' },
{ icon: '🌐', label: _('Zones'), value: zones }
];
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🛡️'),
_('Client Guardian')
]),
E('p', { 'class': 'sh-page-subtitle' },
_('Device protection · Access control · Threat monitoring'))
]),
E('div', { 'class': 'sh-header-meta' }, stats.map(L.bind(this.renderHeaderChip, this)))
]);
},
renderStatCard: function(icon, value, label) {
return E('div', { 'class': 'cg-stat-card' }, [
E('div', { 'class': 'cg-stat-icon' }, icon),
E('div', { 'class': 'cg-stat-value' }, String(value)),
E('div', { 'class': 'cg-stat-label' }, label)
]);
},
renderClientItem: function(showActions, client) {
var statusClass = client.online ? 'online' : 'offline';
if (client.status === 'unknown' || client.zone === 'quarantine')
statusClass += ' quarantine';
if (client.status === 'banned')
statusClass += ' banned';
var deviceIcon = getDeviceIcon(client.hostname || client.name, client.mac);
var zoneClass = (client.zone || 'unknown').replace('lan_', '');
var item = E('div', { 'class': 'cg-client-item ' + statusClass }, [
E('div', { 'class': 'cg-client-avatar' }, deviceIcon),
E('div', { 'class': 'cg-client-info' }, [
E('div', { 'class': 'cg-client-name' }, [
client.online ? E('span', { 'class': 'online-indicator' }) : E('span'),
client.name || client.hostname || 'Unknown',
client.has_threats ? E('span', {
'class': 'cg-threat-badge',
'title': (client.threat_count || 0) + ' menace(s) active(s), score de risque: ' + (client.risk_score || 0),
'style': 'margin-left: 8px; color: #ef4444; font-size: 16px; cursor: help;'
}, '⚠️') : E('span')
]),
E('div', { 'class': 'cg-client-meta' }, [
E('span', {}, client.mac),
E('span', {}, client.ip || 'N/A'),
client.has_threats ? E('span', {
'style': 'color: #ef4444; font-weight: 500; margin-left: 8px;'
}, 'Risque: ' + (client.risk_score || 0) + '%') : E('span')
])
]),
E('span', { 'class': 'cg-client-zone ' + zoneClass }, client.zone || 'unknown'),
E('div', { 'class': 'cg-client-traffic' }, [
E('div', { 'class': 'cg-client-traffic-value' }, '↓ ' + formatBytes(client.rx_bytes || 0)),
E('div', { 'class': 'cg-client-traffic-label' }, '↑ ' + formatBytes(client.tx_bytes || 0))
]) ])
]); ]);
if (showActions) { poll.add(L.bind(this.refresh, this), 10);
var actions = E('div', { 'class': 'cg-client-actions' }); return view;
},
if (client.status === 'unknown') { renderClientRow: function(client) {
var approveBtn = E('div', { var statusIcon = client.online ? '🟢' : '⚪';
'class': 'cg-client-action approve', var statusStyle = '';
'title': 'Approuver', if (client.status === 'banned') {
'data-mac': client.mac statusIcon = '🔴';
}, '✅'); statusStyle = 'background:#fee2e2;';
approveBtn.addEventListener('click', L.bind(this.handleApprove, this));
actions.appendChild(approveBtn);
}
if (client.status !== 'banned') {
var banBtn = E('div', {
'class': 'cg-client-action ban',
'title': 'Bannir',
'data-mac': client.mac
}, '🚫');
banBtn.addEventListener('click', L.bind(this.handleBan, this));
actions.appendChild(banBtn);
}
item.appendChild(actions);
} }
return item; return E('tr', { 'class': 'tr', 'style': statusStyle, 'data-mac': client.mac }, [
E('td', { 'class': 'td' }, statusIcon),
E('td', { 'class': 'td' }, client.name || client.hostname || '-'),
E('td', { 'class': 'td', 'style': 'font-family:monospace;' }, client.mac),
E('td', { 'class': 'td' }, client.ip || '-'),
E('td', { 'class': 'td' }, this.renderActions(client))
]);
},
renderActions: function(client) {
var actions = E('div', { 'style': 'display:flex;gap:8px;' });
if (client.status !== 'approved') {
var approveBtn = E('button', {
'class': 'cbi-button cbi-button-positive',
'style': 'padding:4px 12px;',
'data-mac': client.mac
}, '✓ Approve');
approveBtn.addEventListener('click', L.bind(this.handleApprove, this));
actions.appendChild(approveBtn);
}
if (client.status === 'banned') {
var unbanBtn = E('button', {
'class': 'cbi-button cbi-button-action',
'style': 'padding:4px 12px;',
'data-mac': client.mac
}, 'Unban');
unbanBtn.addEventListener('click', L.bind(this.handleUnban, this));
actions.appendChild(unbanBtn);
} else {
var banBtn = E('button', {
'class': 'cbi-button cbi-button-negative',
'style': 'padding:4px 12px;',
'data-mac': client.mac
}, 'Ban');
banBtn.addEventListener('click', L.bind(this.handleBan, this));
actions.appendChild(banBtn);
}
return actions;
}, },
handleApprove: function(ev) { handleApprove: function(ev) {
var mac = ev.currentTarget.dataset.mac; var mac = ev.currentTarget.dataset.mac;
var self = this; callApproveClient(mac, '', 'lan_private', '').then(L.bind(function() {
ui.addNotification(null, E('p', 'Client approved'), 'info');
ui.showModal(_('Approuver le Client'), [ this.refresh();
E('p', {}, _('Choisissez une zone pour ce client:')), }, this));
E('select', { 'id': 'approve-zone', 'class': 'cg-select' }, [
E('option', { 'value': 'lan_private' }, 'LAN Privé'),
E('option', { 'value': 'iot' }, 'IoT'),
E('option', { 'value': 'kids' }, 'Enfants'),
E('option', { 'value': 'guest' }, 'Invités')
]),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'cg-btn',
'click': ui.hideModal
}, _('Annuler')),
E('button', {
'class': 'cg-btn cg-btn-success',
'click': L.bind(function() {
var zone = document.getElementById('approve-zone').value;
callApproveClient(mac, '', zone, '').then(L.bind(function() {
ui.hideModal();
ui.addNotification(null, E('p', _('Client approved successfully')), 'success');
this.handleRefresh();
}, this));
}, this)
}, _('Approuver'))
])
]);
}, },
handleBan: function(ev) { handleBan: function(ev) {
var mac = ev.currentTarget.dataset.mac; var mac = ev.currentTarget.dataset.mac;
if (confirm('Ban this client?\n' + mac)) {
ui.showModal(_('Bannir le Client'), [ callBanClient(mac, 'Manual ban').then(L.bind(function() {
E('p', {}, _('Voulez-vous vraiment bannir ce client?')), ui.addNotification(null, E('p', 'Client banned'), 'info');
E('p', {}, E('strong', {}, mac)), this.refresh();
E('div', { 'class': 'right' }, [ }, this));
E('button', { }
'class': 'cg-btn',
'click': ui.hideModal
}, _('Annuler')),
E('button', {
'class': 'cg-btn cg-btn-danger',
'click': L.bind(function() {
callBanClient(mac, 'Manual ban').then(L.bind(function() {
ui.hideModal();
ui.addNotification(null, E('p', _('Client banned successfully')), 'info');
this.handleRefresh();
}, this));
}, this)
}, _('Bannir'))
])
]);
}, },
handleRefresh: function() { handleUnban: function(ev) {
return Promise.all([ var mac = ev.currentTarget.dataset.mac;
callGetStatus(), callUnbanClient(mac).then(L.bind(function() {
callGetClients(), ui.addNotification(null, E('p', 'Client unbanned'), 'info');
callGetZones() this.refresh();
]).then(L.bind(function(data) { }, this));
var container = document.querySelector('.client-guardian-dashboard'); },
if (container) {
var statusBadge = document.querySelector('.cg-status-badge'); refresh: function() {
if (statusBadge) { return callGetClients().then(L.bind(function(data) {
statusBadge.classList.add('loading'); var clients = Array.isArray(data) ? data : (data.clients || []);
} var table = document.getElementById('client-table');
var newView = this.render(data); if (table) {
dom.content(container.parentNode, newView); while (table.rows.length > 1) table.deleteRow(1);
if (statusBadge) { clients.forEach(L.bind(function(client) {
statusBadge.classList.remove('loading'); table.appendChild(this.renderClientRow(client));
} }, this));
} }
}, this)).catch(function(err) { }, this));
console.error('Failed to refresh Client Guardian dashboard:', err);
});
},
handleLeave: function() {
poll.stop();
}, },
handleSaveApply: null, handleSaveApply: null,

View File

@ -1,189 +1,40 @@
'use strict'; 'use strict';
'require view'; 'require view';
'require form'; 'require form';
'require ui';
'require uci'; 'require uci';
'require client-guardian/api as API';
'require client-guardian/nav as CgNav';
'require secubox-portal/header as SbHeader';
return view.extend({ return view.extend({
load: function() { load: function() {
return Promise.all([ return uci.load('client-guardian');
API.getPolicy(),
API.getStatus(),
L.resolveDefault(uci.load('client-guardian'), {})
]);
}, },
render: function(data) { render: function() {
var policy = data[0] || {};
var status = data[1] || {};
var m, s, o; var m, s, o;
m = new form.Map('client-guardian', _('Client Guardian Settings'), m = new form.Map('client-guardian', 'Client Guardian Settings',
_('Configure default network access policy and client management.')); 'Basic network client management.');
// General Settings s = m.section(form.NamedSection, 'config', 'client-guardian', 'General');
s = m.section(form.NamedSection, 'config', 'client-guardian', _('General Settings')); s.anonymous = true;
o = s.option(form.Flag, 'enabled', _('Enable Client Guardian')); o = s.option(form.Flag, 'enabled', 'Enable');
o.default = '0'; o.default = '1';
o.rmempty = false;
o.description = _('Enable or disable the Client Guardian access control system. WARNING: Enabling with restrictive policies may block network access!');
o = s.option(form.ListValue, 'default_policy', _('Default Policy')); o = s.option(form.ListValue, 'default_policy', 'Default Policy');
o.value('open', _('Open - Allow all clients (Recommended)')); o.value('open', 'Open - Auto-approve new clients');
o.value('quarantine', _('Quarantine - Require approval (Restrictive)')); o.value('closed', 'Closed - Require approval');
o.value('whitelist', _('Whitelist Only - Block unknown clients (Very Restrictive)'));
o.default = 'open'; o.default = 'open';
o.rmempty = false;
o.description = _('Default behavior for new/unknown clients. WARNING: Quarantine and Whitelist modes will block new devices from accessing the network!');
o = s.option(form.Flag, 'auto_approve', _('Auto-Approve Known Devices')); o = s.option(form.Flag, 'auto_approve', 'Auto-approve known devices');
o.default = '0';
o.depends('default_policy', 'whitelist');
o.description = _('Automatically approve devices that have connected before');
o = s.option(form.Value, 'session_timeout', _('Session Timeout'));
o.datatype = 'uinteger';
o.default = '86400';
o.placeholder = '86400';
o.description = _('Maximum session duration in seconds (default: 86400 = 24 hours)');
// Advanced Settings
s = m.section(form.NamedSection, 'config', 'client-guardian', _('Advanced Settings'));
o = s.option(form.Flag, 'mac_filtering', _('Enable MAC Filtering'));
o.default = '1'; o.default = '1';
o.description = _('Use MAC addresses for client identification and blocking');
o = s.option(form.Flag, 'log_connections', _('Log Connection Events')); o = s.option(form.ListValue, 'log_level', 'Log Level');
o.default = '1'; o.value('error', 'Error');
o.description = _('Log client connections and authentication events'); o.value('warn', 'Warning');
o.value('info', 'Info');
o.value('debug', 'Debug');
o.default = 'info';
o = s.option(form.Value, 'log_retention', _('Log Retention (days)')); return m.render();
o.datatype = 'uinteger'; }
o.default = '30';
o.depends('log_connections', '1');
o.description = _('Number of days to keep connection logs');
o = s.option(form.Flag, 'block_tor', _('Block Tor Exit Nodes'));
o.default = '0';
o.description = _('Automatically block known Tor exit nodes');
o = s.option(form.Flag, 'block_vpn', _('Block VPN Detection'));
o.default = '0';
o.description = _('Attempt to detect and block VPN connections');
// Dashboard Reactiveness
s = m.section(form.NamedSection, 'config', 'client-guardian', _('Dashboard Reactiveness'));
o = s.option(form.Flag, 'auto_refresh', _('Enable Auto-Refresh'),
_('Automatically refresh dashboard every few seconds'));
o.default = o.enabled;
o.rmempty = false;
o = s.option(form.ListValue, 'refresh_interval', _('Refresh Interval'),
_('How often to poll for updates'));
o.value('5', _('Every 5 seconds'));
o.value('10', _('Every 10 seconds (recommended)'));
o.value('30', _('Every 30 seconds'));
o.value('60', _('Every 60 seconds'));
o.default = '10';
o.depends('auto_refresh', '1');
// Threat Intelligence Integration
s = m.section(form.NamedSection, 'threat_policy', 'threat_policy', _('Threat Intelligence Integration'));
o = s.option(form.Flag, 'enabled', _('Enable Threat Intelligence'),
_('Correlate clients with Security Threats Dashboard data'));
o.default = o.enabled;
o.rmempty = false;
o = s.option(form.Value, 'auto_ban_threshold', _('Auto-Ban Threshold'),
_('Automatically ban clients with threat score above this value (0-100)'));
o.datatype = 'range(1,100)';
o.placeholder = '80';
o.default = '80';
o.depends('enabled', '1');
o = s.option(form.Value, 'auto_quarantine_threshold', _('Auto-Quarantine Threshold'),
_('Automatically quarantine clients with threat score above this value (0-100)'));
o.datatype = 'range(1,100)';
o.placeholder = '60';
o.default = '60';
o.depends('enabled', '1');
o = s.option(form.Value, 'threat_check_interval', _('Threat Check Interval'),
_('How often to check for threats (seconds)'));
o.datatype = 'uinteger';
o.placeholder = '60';
o.default = '60';
o.depends('enabled', '1');
return m.render().then(function(rendered) {
// Policy display names
var policyNames = {
'open': _('Open'),
'quarantine': _('Quarantine'),
'whitelist': _('Whitelist Only')
};
var currentPolicy = policy.default_policy || 'quarantine';
var policyDisplay = policyNames[currentPolicy] || currentPolicy;
// Add policy info box at the top
var infoBox = E('div', {
'class': 'cbi-section',
'style': 'background: var(--cg-bg-secondary, #151b23); border-left: 4px solid var(--cg-accent, #6366f1); padding: 1em; margin-bottom: 1em; border-radius: 8px;'
}, [
E('h3', { 'style': 'margin-top: 0; color: var(--cg-text-primary, #e6edf3);' }, [
_('Current Policy: '),
E('span', { 'style': 'color: var(--cg-accent, #6366f1); font-weight: 600;' }, policyDisplay)
]),
E('div', { 'style': 'margin-top: 0.5em; color: var(--cg-text-secondary, #8b949e);' }, [
E('strong', {}, _('Session Timeout: ')),
E('span', {}, (policy.session_timeout || 86400) + ' ' + _('seconds'))
]),
E('div', { 'style': 'margin-top: 1em; padding: 0.75em; background: var(--cg-bg-tertiary, #1e2632); border-radius: 4px;' }, [
E('strong', { 'style': 'color: var(--cg-text-primary, #e6edf3);' }, _('Policy Descriptions:')),
E('ul', { 'style': 'margin: 0.5em 0; color: var(--cg-text-secondary, #8b949e);' }, [
E('li', {}, [
E('strong', { 'style': 'color: #22c55e;' }, _('Open: ')),
_('All clients can access the network without authentication. Not recommended for public networks.')
]),
E('li', {}, [
E('strong', { 'style': 'color: #f59e0b;' }, _('Quarantine: ')),
_('New clients are placed in quarantine and require manual approval. Recommended for secure networks.')
]),
E('li', {}, [
E('strong', { 'style': 'color: #ef4444;' }, _('Whitelist Only: ')),
_('Only explicitly approved clients can access the network. Highest security.')
])
])
])
]);
rendered.insertBefore(infoBox, rendered.firstChild);
// Main wrapper with SecuBox header
var wrapper = E('div', { 'class': 'secubox-page-wrapper' });
wrapper.appendChild(SbHeader.render());
var view = E('div', { 'class': 'client-guardian-dashboard' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('client-guardian/dashboard.css') }),
CgNav.renderTabs('settings'),
rendered
]);
wrapper.appendChild(view);
return wrapper;
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
}); });

View File

@ -9,73 +9,17 @@
"acl": ["luci-app-client-guardian"] "acl": ["luci-app-client-guardian"]
} }
}, },
"admin/secubox/security/guardian/overview": { "admin/secubox/security/guardian/clients": {
"title": "Overview", "title": "Clients",
"order": 10, "order": 10,
"action": { "action": {
"type": "view", "type": "view",
"path": "client-guardian/overview" "path": "client-guardian/overview"
} }
}, },
"admin/secubox/security/guardian/wizard": {
"title": "Setup Wizard",
"order": 15,
"action": {
"type": "view",
"path": "client-guardian/wizard"
}
},
"admin/secubox/security/guardian/clients": {
"title": "Clients",
"order": 20,
"action": {
"type": "view",
"path": "client-guardian/clients"
}
},
"admin/secubox/security/guardian/zones": {
"title": "Network Zones",
"order": 25,
"action": {
"type": "view",
"path": "client-guardian/zones"
}
},
"admin/secubox/security/guardian/autozoning": {
"title": "Auto-Zoning Rules",
"order": 30,
"action": {
"type": "view",
"path": "client-guardian/autozoning"
}
},
"admin/secubox/security/guardian/logs": {
"title": "Event Logs",
"order": 40,
"action": {
"type": "view",
"path": "client-guardian/logs"
}
},
"admin/secubox/security/guardian/alerts": {
"title": "Alerts & Notifications",
"order": 50,
"action": {
"type": "view",
"path": "client-guardian/alerts"
}
},
"admin/secubox/security/guardian/parental": {
"title": "Parental Controls",
"order": 60,
"action": {
"type": "view",
"path": "client-guardian/parental"
}
},
"admin/secubox/security/guardian/settings": { "admin/secubox/security/guardian/settings": {
"title": "Settings", "title": "Settings",
"order": 90, "order": 20,
"action": { "action": {
"type": "view", "type": "view",
"path": "client-guardian/settings" "path": "client-guardian/settings"