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 dom';
'require poll';
'require uci';
'require ui';
'require rpc';
'require client-guardian/nav as CgNav';
'require secubox-portal/header as SbHeader';
var callGetStatus = rpc.declare({
object: 'luci.client-guardian',
method: 'status'
});
'require ui';
var callGetClients = rpc.declare({
object: 'luci.client-guardian',
@ -19,12 +11,6 @@ var callGetClients = rpc.declare({
expect: { clients: [] }
});
var callGetZones = rpc.declare({
object: 'luci.client-guardian',
method: 'zones',
expect: { zones: [] }
});
var callApproveClient = rpc.declare({
object: 'luci.client-guardian',
method: 'approve_client',
@ -37,297 +23,149 @@ var callBanClient = rpc.declare({
params: ['mac', 'reason']
});
function formatBytes(bytes) {
if (!bytes || bytes === 0) return '0 B';
var units = ['B', 'KB', 'MB', 'GB', 'TB'];
var i = Math.floor(Math.log(bytes) / Math.log(1024));
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 '🔌';
}
var callUnbanClient = rpc.declare({
object: 'luci.client-guardian',
method: 'unban_client',
params: ['mac']
});
return view.extend({
load: function() {
return Promise.all([
callGetStatus(),
callGetClients(),
callGetZones(),
uci.load('client-guardian')
]);
return callGetClients();
},
render: function(data) {
var status = data[0];
var clients = Array.isArray(data[1]) ? data[1] : (data[1].clients || []);
var zones = Array.isArray(data[2]) ? data[2] : (data[2].zones || []);
var clients = Array.isArray(data) ? data : (data.clients || []);
var online = clients.filter(function(c) { return c.online; }).length;
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 approvedClients = clients.filter(function(c) { return c.status === 'approved'; });
var quarantineClients = clients.filter(function(c) { return c.status === 'unknown' || c.zone === 'quarantine'; });
var bannedClients = clients.filter(function(c) { return c.status === 'banned'; });
var view = E('div', { 'class': 'cbi-map' }, [
E('h2', {}, 'Client Guardian'),
E('div', { 'class': 'cbi-map-descr' }, 'Network client management'),
// 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('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')
// Stats
E('div', { 'style': 'display:flex;gap:20px;margin:20px 0;' }, [
E('div', { 'style': 'padding:15px;background:#22c55e22;border-radius:8px;' }, [
E('strong', { 'style': 'font-size:24px;color:#22c55e;' }, String(online)),
E('div', {}, 'Online')
]),
E('div', { 'class': 'cg-card-body' }, [
E('div', { 'class': 'cg-client-list' },
onlineClients.slice(0, 5).map(L.bind(this.renderClientItem, this, false))
)
E('div', { 'style': 'padding:15px;background:#3b82f622;border-radius:8px;' }, [
E('strong', { 'style': 'font-size:24px;color:#3b82f6;' }, String(approved)),
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
quarantineClients.length > 0 ? E('div', { 'class': 'cg-card' }, [
E('div', { 'class': 'cg-card-header' }, [
E('div', { 'class': 'cg-card-title' }, [
E('span', { 'class': 'cg-card-title-icon' }, '⏳'),
'En Attente d\'Approbation'
]),
E('span', { 'class': 'cg-card-badge' }, quarantineClients.length + ' clients')
]),
E('div', { 'class': 'cg-card-body' }, [
E('div', { 'class': 'cg-client-list' },
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))
// Client Table
E('div', { 'class': 'cbi-section' }, [
E('table', { 'class': 'table', 'id': 'client-table' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, 'Status'),
E('th', { 'class': 'th' }, 'Name'),
E('th', { 'class': 'th' }, 'MAC'),
E('th', { 'class': 'th' }, 'IP'),
E('th', { 'class': 'th' }, 'Actions')
])
].concat(clients.map(L.bind(this.renderClientRow, this))))
])
]);
if (showActions) {
var actions = E('div', { 'class': 'cg-client-actions' });
poll.add(L.bind(this.refresh, this), 10);
return view;
},
if (client.status === 'unknown') {
var approveBtn = E('div', {
'class': 'cg-client-action approve',
'title': 'Approuver',
'data-mac': client.mac
}, '✅');
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);
renderClientRow: function(client) {
var statusIcon = client.online ? '🟢' : '⚪';
var statusStyle = '';
if (client.status === 'banned') {
statusIcon = '🔴';
statusStyle = 'background:#fee2e2;';
}
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) {
var mac = ev.currentTarget.dataset.mac;
var self = this;
ui.showModal(_('Approuver le Client'), [
E('p', {}, _('Choisissez une zone pour ce client:')),
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'))
])
]);
callApproveClient(mac, '', 'lan_private', '').then(L.bind(function() {
ui.addNotification(null, E('p', 'Client approved'), 'info');
this.refresh();
}, this));
},
handleBan: function(ev) {
var mac = ev.currentTarget.dataset.mac;
ui.showModal(_('Bannir le Client'), [
E('p', {}, _('Voulez-vous vraiment bannir ce client?')),
E('p', {}, E('strong', {}, mac)),
E('div', { 'class': 'right' }, [
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'))
])
]);
if (confirm('Ban this client?\n' + mac)) {
callBanClient(mac, 'Manual ban').then(L.bind(function() {
ui.addNotification(null, E('p', 'Client banned'), 'info');
this.refresh();
}, this));
}
},
handleRefresh: function() {
return Promise.all([
callGetStatus(),
callGetClients(),
callGetZones()
]).then(L.bind(function(data) {
var container = document.querySelector('.client-guardian-dashboard');
if (container) {
var statusBadge = document.querySelector('.cg-status-badge');
if (statusBadge) {
statusBadge.classList.add('loading');
}
var newView = this.render(data);
dom.content(container.parentNode, newView);
if (statusBadge) {
statusBadge.classList.remove('loading');
}
handleUnban: function(ev) {
var mac = ev.currentTarget.dataset.mac;
callUnbanClient(mac).then(L.bind(function() {
ui.addNotification(null, E('p', 'Client unbanned'), 'info');
this.refresh();
}, this));
},
refresh: function() {
return callGetClients().then(L.bind(function(data) {
var clients = Array.isArray(data) ? data : (data.clients || []);
var table = document.getElementById('client-table');
if (table) {
while (table.rows.length > 1) table.deleteRow(1);
clients.forEach(L.bind(function(client) {
table.appendChild(this.renderClientRow(client));
}, this));
}
}, this)).catch(function(err) {
console.error('Failed to refresh Client Guardian dashboard:', err);
});
},
handleLeave: function() {
poll.stop();
}, this));
},
handleSaveApply: null,

View File

@ -1,189 +1,40 @@
'use strict';
'require view';
'require form';
'require ui';
'require uci';
'require client-guardian/api as API';
'require client-guardian/nav as CgNav';
'require secubox-portal/header as SbHeader';
return view.extend({
load: function() {
return Promise.all([
API.getPolicy(),
API.getStatus(),
L.resolveDefault(uci.load('client-guardian'), {})
]);
return uci.load('client-guardian');
},
render: function(data) {
var policy = data[0] || {};
var status = data[1] || {};
render: function() {
var m, s, o;
m = new form.Map('client-guardian', _('Client Guardian Settings'),
_('Configure default network access policy and client management.'));
m = new form.Map('client-guardian', 'Client Guardian Settings',
'Basic network client management.');
// General Settings
s = m.section(form.NamedSection, 'config', 'client-guardian', _('General Settings'));
s = m.section(form.NamedSection, 'config', 'client-guardian', 'General');
s.anonymous = true;
o = s.option(form.Flag, 'enabled', _('Enable Client Guardian'));
o.default = '0';
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.Flag, 'enabled', 'Enable');
o.default = '1';
o = s.option(form.ListValue, 'default_policy', _('Default Policy'));
o.value('open', _('Open - Allow all clients (Recommended)'));
o.value('quarantine', _('Quarantine - Require approval (Restrictive)'));
o.value('whitelist', _('Whitelist Only - Block unknown clients (Very Restrictive)'));
o = s.option(form.ListValue, 'default_policy', 'Default Policy');
o.value('open', 'Open - Auto-approve new clients');
o.value('closed', 'Closed - Require approval');
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.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 = s.option(form.Flag, 'auto_approve', 'Auto-approve known devices');
o.default = '1';
o.description = _('Use MAC addresses for client identification and blocking');
o = s.option(form.Flag, 'log_connections', _('Log Connection Events'));
o.default = '1';
o.description = _('Log client connections and authentication events');
o = s.option(form.ListValue, 'log_level', 'Log Level');
o.value('error', 'Error');
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)'));
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
return m.render();
}
});

View File

@ -9,73 +9,17 @@
"acl": ["luci-app-client-guardian"]
}
},
"admin/secubox/security/guardian/overview": {
"title": "Overview",
"admin/secubox/security/guardian/clients": {
"title": "Clients",
"order": 10,
"action": {
"type": "view",
"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": {
"title": "Settings",
"order": 90,
"order": 20,
"action": {
"type": "view",
"path": "client-guardian/settings"