secubox-openwrt/package/secubox/luci-app-client-guardian/htdocs/luci-static/resources/view/client-guardian/overview.js

175 lines
5.1 KiB
JavaScript

'use strict';
'require view';
'require dom';
'require poll';
'require rpc';
'require ui';
var callGetClients = rpc.declare({
object: 'luci.client-guardian',
method: 'clients',
expect: { clients: [] }
});
var callApproveClient = rpc.declare({
object: 'luci.client-guardian',
method: 'approve_client',
params: ['mac', 'name', 'zone', 'notes']
});
var callBanClient = rpc.declare({
object: 'luci.client-guardian',
method: 'ban_client',
params: ['mac', 'reason']
});
var callUnbanClient = rpc.declare({
object: 'luci.client-guardian',
method: 'unban_client',
params: ['mac']
});
return view.extend({
load: function() {
return callGetClients();
},
render: function(data) {
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 view = E('div', { 'class': 'cbi-map' }, [
E('h2', {}, 'Client Guardian'),
E('div', { 'class': 'cbi-map-descr' }, 'Network client management'),
// 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', { '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')
])
]),
// 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))))
])
]);
poll.add(L.bind(this.refresh, this), 10);
return view;
},
renderClientRow: function(client) {
var statusIcon = client.online ? '🟢' : '⚪';
var statusStyle = '';
if (client.status === 'banned') {
statusIcon = '🔴';
statusStyle = 'background:#fee2e2;';
}
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;
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;
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));
}
},
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));
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});