secubox-openwrt/luci-app-secubox-users/htdocs/luci-static/resources/view/secubox-users/overview.js
CyberMind-FR 58220065b5 feat(v0.23.0): Matrix homeserver, SaaS Relay CDN caching, Media Hub dashboard
Matrix Homeserver (Conduit):
- E2EE mesh messaging using Conduit v0.10.12 in LXC container
- matrixctl CLI: install/uninstall, user/room management, federation
- luci-app-matrix: status cards, user form, emancipate, mesh publish
- RPCD backend with 17 methods
- Identity (DID) integration and P2P mesh publication

SaaS Relay CDN Caching & Session Replay:
- CDN cache profiles: minimal, gandalf (default), aggressive
- Session replay modes: shared, per_user, master
- saasctl cache/session commands for management
- Enhanced mitmproxy addon (415 lines) with response caching

Media Services Hub Dashboard:
- Unified dashboard at /admin/services/media-hub
- Category-organized cards (streaming, conferencing, apps, etc.)
- Service status indicators with start/stop/restart controls
- RPCD backend querying 8 media services

Also includes:
- HexoJS static upload workflow and multi-user auth
- Jitsi config.js Promise handling fix
- Feed package updates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-20 11:44:26 +01:00

285 lines
9.5 KiB
JavaScript

'use strict';
'require view';
'require dom';
'require ui';
'require rpc';
var callStatus = rpc.declare({
object: 'luci.secubox-users',
method: 'status',
expect: { }
});
var callUsers = rpc.declare({
object: 'luci.secubox-users',
method: 'users',
expect: { }
});
var callAddUser = rpc.declare({
object: 'luci.secubox-users',
method: 'add',
params: ['username', 'password', 'services'],
expect: { }
});
var callDeleteUser = rpc.declare({
object: 'luci.secubox-users',
method: 'delete',
params: ['username'],
expect: { }
});
var callPasswd = rpc.declare({
object: 'luci.secubox-users',
method: 'passwd',
params: ['username', 'password'],
expect: { }
});
return view.extend({
load: function() {
return Promise.all([
callStatus(),
callUsers()
]);
},
renderServiceBadge: function(name, running) {
var color = running ? '#4CAF50' : '#9e9e9e';
return E('span', {
'style': 'display:inline-block;padding:2px 8px;margin:2px;border-radius:3px;color:#fff;background:' + color + ';font-size:0.85em;'
}, name);
},
renderUserRow: function(user) {
var self = this;
var services = (user.services || []).map(function(s) {
return E('span', {
'style': 'display:inline-block;padding:1px 6px;margin:1px;border-radius:3px;background:#e3f2fd;font-size:0.8em;'
}, s);
});
return E('tr', {}, [
E('td', {}, user.username),
E('td', {}, user.email),
E('td', {}, services),
E('td', {}, user.enabled === '1' ? 'Yes' : 'No'),
E('td', {}, [
E('button', {
'class': 'btn cbi-button',
'click': function() { self.handlePasswd(user.username); },
'style': 'margin-right:5px;'
}, _('Password')),
E('button', {
'class': 'btn cbi-button cbi-button-remove',
'click': function() { self.handleDelete(user.username); }
}, _('Delete'))
])
]);
},
handleAdd: function() {
var self = this;
ui.showModal(_('Add User'), [
E('div', { 'class': 'cbi-section' }, [
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Username')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'text', 'id': 'new-username', 'style': 'width:200px;' })
])
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Password')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'password', 'id': 'new-password', 'placeholder': _('Leave empty to generate'), 'style': 'width:200px;' })
])
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('Services')),
E('div', { 'class': 'cbi-value-field' }, [
E('label', {}, [E('input', { 'type': 'checkbox', 'id': 'svc-nextcloud', 'checked': true }), ' Nextcloud']),
E('label', { 'style': 'margin-left:10px;' }, [E('input', { 'type': 'checkbox', 'id': 'svc-peertube', 'checked': true }), ' PeerTube']),
E('label', { 'style': 'margin-left:10px;' }, [E('input', { 'type': 'checkbox', 'id': 'svc-jabber', 'checked': true }), ' Jabber']),
E('label', { 'style': 'margin-left:10px;' }, [E('input', { 'type': 'checkbox', 'id': 'svc-matrix', 'checked': true }), ' Matrix']),
E('label', { 'style': 'margin-left:10px;' }, [E('input', { 'type': 'checkbox', 'id': 'svc-email', 'checked': true }), ' Email'])
])
])
]),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn',
'click': ui.hideModal
}, _('Cancel')),
E('button', {
'class': 'btn cbi-button-positive',
'click': function() {
var username = document.getElementById('new-username').value;
var password = document.getElementById('new-password').value;
var services = [];
if (document.getElementById('svc-nextcloud').checked) services.push('nextcloud');
if (document.getElementById('svc-peertube').checked) services.push('peertube');
if (document.getElementById('svc-jabber').checked) services.push('jabber');
if (document.getElementById('svc-matrix').checked) services.push('matrix');
if (document.getElementById('svc-email').checked) services.push('email');
if (!username) {
ui.addNotification(null, E('p', {}, _('Username required')), 'error');
return;
}
ui.hideModal();
ui.showModal(_('Creating User...'), [
E('p', { 'class': 'spinning' }, _('Please wait...'))
]);
callAddUser(username, password, services.join(',')).then(function(res) {
ui.hideModal();
if (res && res.success) {
ui.showModal(_('User Created'), [
E('div', { 'style': 'padding:20px;' }, [
E('p', {}, _('User created successfully!')),
E('table', { 'style': 'margin:15px 0;' }, [
E('tr', {}, [E('td', { 'style': 'padding:5px;font-weight:bold;' }, _('Username:')), E('td', { 'style': 'padding:5px;' }, res.username)]),
E('tr', {}, [E('td', { 'style': 'padding:5px;font-weight:bold;' }, _('Password:')), E('td', { 'style': 'padding:5px;font-family:monospace;background:#f5f5f5;' }, res.password)]),
E('tr', {}, [E('td', { 'style': 'padding:5px;font-weight:bold;' }, _('Email:')), E('td', { 'style': 'padding:5px;' }, res.email)])
])
]),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'btn cbi-button-positive', 'click': function() { ui.hideModal(); location.reload(); } }, _('OK'))
])
]);
} else {
ui.addNotification(null, E('p', {}, _('Error: ') + (res.error || 'Unknown error')), 'error');
}
});
},
'style': 'margin-left:10px;'
}, _('Create User'))
])
]);
},
handleDelete: function(username) {
var self = this;
if (!confirm(_('Delete user "%s" from all services?').format(username))) {
return;
}
ui.showModal(_('Deleting...'), [
E('p', { 'class': 'spinning' }, _('Please wait...'))
]);
callDeleteUser(username).then(function(res) {
ui.hideModal();
if (res && res.success) {
ui.addNotification(null, E('p', {}, _('User deleted')), 'success');
location.reload();
} else {
ui.addNotification(null, E('p', {}, _('Error: ') + (res.error || 'Unknown error')), 'error');
}
});
},
handlePasswd: function(username) {
var self = this;
ui.showModal(_('Change Password'), [
E('div', { 'class': 'cbi-section' }, [
E('p', {}, _('Change password for: %s').format(username)),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title' }, _('New Password')),
E('div', { 'class': 'cbi-value-field' }, [
E('input', { 'type': 'password', 'id': 'new-passwd', 'placeholder': _('Leave empty to generate'), 'style': 'width:200px;' })
])
])
]),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'btn', 'click': ui.hideModal }, _('Cancel')),
E('button', {
'class': 'btn cbi-button-positive',
'click': function() {
var password = document.getElementById('new-passwd').value;
ui.hideModal();
ui.showModal(_('Updating...'), [
E('p', { 'class': 'spinning' }, _('Please wait...'))
]);
callPasswd(username, password).then(function(res) {
ui.hideModal();
if (res && res.success) {
ui.showModal(_('Password Updated'), [
E('div', { 'style': 'padding:20px;' }, [
E('p', {}, _('Password updated for all services!')),
E('p', { 'style': 'font-family:monospace;background:#f5f5f5;padding:10px;margin:10px 0;' }, res.password)
]),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'btn cbi-button-positive', 'click': ui.hideModal }, _('OK'))
])
]);
} else {
ui.addNotification(null, E('p', {}, _('Error updating password')), 'error');
}
});
},
'style': 'margin-left:10px;'
}, _('Update'))
])
]);
},
render: function(data) {
var self = this;
var status = data[0] || {};
var usersData = data[1] || {};
var users = usersData.users || [];
var services = status.services || {};
var content = [];
// Header
content.push(E('h2', {}, _('SecuBox User Management')));
// Status Section
content.push(E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Services')),
E('div', { 'style': 'margin:10px 0;' }, [
this.renderServiceBadge('Nextcloud', services.nextcloud),
this.renderServiceBadge('PeerTube', services.peertube),
this.renderServiceBadge('Matrix', services.matrix),
this.renderServiceBadge('Jabber', services.jabber),
this.renderServiceBadge('Email', services.email)
]),
E('p', { 'style': 'color:#666;' }, _('Domain: %s | Users: %d').format(status.domain, status.user_count))
]));
// Users Table
var userRows = users.map(function(u) { return self.renderUserRow(u); });
content.push(E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Users')),
E('div', { 'class': 'cbi-page-actions', 'style': 'margin-bottom:15px;' }, [
E('button', {
'class': 'btn cbi-button-positive',
'click': function() { self.handleAdd(); }
}, _('Add User'))
]),
users.length > 0 ?
E('table', { 'class': 'table cbi-section-table' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, _('Username')),
E('th', { 'class': 'th' }, _('Email')),
E('th', { 'class': 'th' }, _('Services')),
E('th', { 'class': 'th' }, _('Enabled')),
E('th', { 'class': 'th' }, _('Actions'))
])
].concat(userRows)) :
E('p', { 'style': 'color:#666;' }, _('No users configured. Click "Add User" to create one.'))
]));
return E('div', { 'class': 'cbi-map' }, content);
}
});