secubox-openwrt/package/secubox/luci-app-matrix/htdocs/luci-static/resources/view/matrix/overview.js
CyberMind-FR b6747c197e feat(security): Add instant ban feature and user management
- Add enhanced instant ban for critical threats (SQL injection, CVE exploits, RCE)
  - CrowdSec trigger scenario for single-hit bans on severity=critical
  - Instant ban daemon (10s polling) for rapid response
  - UCI options: instant_ban_enabled, instant_ban_duration (48h default)
  - WAF addon updated to route critical threats to instant-ban.log

- Add centralized user management (secubox-core-users, luci-app-secubox-users)
  - CLI tool: secubox-users add/del/passwd/list/sync/status
  - LuCI dashboard under System > SecuBox Users
  - Unified user provisioning across Nextcloud, PeerTube, Matrix, Jabber, Email

- Add Matrix/Conduit integration (secubox-app-matrix, luci-app-matrix)
  - LXC-based Conduit homeserver deployment
  - Full RPCD handler with user/room management
  - HAProxy integration for federation

- Add provision-users.sh script for bulk user creation
- Update secubox-feed with new IPKs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 20:17:28 +01:00

377 lines
12 KiB
JavaScript

'use strict';
'require view';
'require dom';
'require poll';
'require ui';
'require uci';
'require matrix.api as api';
return view.extend({
handleAction: function(action, args) {
var self = this;
ui.showModal(_('Processing...'), [
E('p', { 'class': 'spinning' }, _('Please wait...'))
]);
var promise;
switch(action) {
case 'start':
promise = api.start();
break;
case 'stop':
promise = api.stop();
break;
case 'install':
promise = api.install();
break;
case 'uninstall':
promise = api.uninstall();
break;
case 'update':
promise = api.update();
break;
case 'emancipate':
promise = api.emancipate(args.domain);
break;
case 'configure_haproxy':
promise = api.configureHaproxy();
break;
case 'user_add':
promise = api.userAdd(args.mxid, args.password);
break;
case 'identity_link':
promise = api.identityLink(args.mxid);
break;
case 'identity_unlink':
promise = api.identityUnlink();
break;
case 'mesh_publish':
promise = api.meshPublish();
break;
case 'mesh_unpublish':
promise = api.meshUnpublish();
break;
default:
promise = Promise.reject(new Error('Unknown action'));
}
promise.then(function(res) {
ui.hideModal();
if (res && res.success) {
var msg = res.message || 'Operation completed successfully';
if (res.password) {
msg += '\n\nGenerated password: ' + res.password;
}
ui.addNotification(null, E('p', {}, msg), 'success');
} else {
ui.addNotification(null, E('p', {}, _('Error: ') + (res.error || 'Unknown error')), 'error');
}
// Refresh status
self.load().then(function(data) {
dom.content(document.querySelector('#matrix-content'), self.renderContent(data));
});
}).catch(function(e) {
ui.hideModal();
ui.addNotification(null, E('p', {}, _('Error: ') + e.message), 'error');
});
},
load: function() {
return Promise.all([
api.getStatus(),
uci.load('matrix'),
api.getFederationStatus(),
api.getIdentityStatus(),
api.getMeshStatus()
]);
},
renderStatusBadge: function(running) {
var color = running ? '#4CAF50' : '#9e9e9e';
var text = running ? _('Running') : _('Stopped');
return E('span', {
'style': 'display:inline-block;padding:3px 10px;border-radius:3px;color:#fff;background:' + color
}, text);
},
renderFeatureBadge: function(enabled, label) {
var color = enabled ? '#2196F3' : '#9e9e9e';
return E('span', {
'style': 'display:inline-block;padding:2px 8px;border-radius:3px;color:#fff;background:' + color + ';margin-right:5px;font-size:0.85em;'
}, label);
},
renderInstallWizard: function() {
var self = this;
return E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Matrix Homeserver')),
E('p', {}, _('Matrix is a decentralized communication protocol with end-to-end encryption.')),
E('div', { 'style': 'margin:20px 0;padding:15px;background:#f5f5f5;border-radius:5px;' }, [
E('h4', {}, _('Features:')),
E('ul', {}, [
E('li', {}, _('End-to-end encrypted messaging')),
E('li', {}, _('Federated chat (connect with other Matrix servers)')),
E('li', {}, _('Group chats and spaces')),
E('li', {}, _('Voice and video calls')),
E('li', {}, _('File sharing')),
E('li', {}, _('Bridge to other platforms'))
]),
E('h4', { 'style': 'margin-top:15px;' }, _('Compatible clients:')),
E('p', {}, _('Element (Web/Desktop/Mobile), FluffyChat, Nheko, SchildiChat'))
]),
E('div', { 'class': 'cbi-page-actions' }, [
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'click': function() { self.handleAction('install'); }
}, _('Install Matrix Homeserver'))
])
]);
},
renderContent: function(data) {
var self = this;
var status = data[0] || {};
var federation = data[2] || {};
var identity = data[3] || {};
var mesh = data[4] || {};
// Show install wizard if not installed
if (status.container_state === 'not_installed') {
return this.renderInstallWizard();
}
var running = status.running === 'true' || status.running === true;
var hostname = status.hostname || 'matrix.local';
var httpPort = status.http_port || '8008';
var domain = status.domain || '';
var lanIp = '192.168.255.1';
var content = [];
// Status Section
content.push(E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Matrix Homeserver')),
E('div', { 'style': 'display:flex;flex-wrap:wrap;gap:20px;margin:15px 0;' }, [
// Status Card
E('div', { 'style': 'flex:1;min-width:200px;padding:15px;border:1px solid #ddd;border-radius:5px;' }, [
E('h4', { 'style': 'margin:0 0 10px 0;' }, _('Server Status')),
E('div', {}, this.renderStatusBadge(running)),
E('div', { 'style': 'margin-top:10px;color:#666;' }, [
E('div', {}, _('Hostname: ') + hostname),
E('div', {}, _('Client Port: ') + httpPort),
status.version ? E('div', {}, _('Version: ') + status.version) : ''
])
]),
// Features Card
E('div', { 'style': 'flex:1;min-width:200px;padding:15px;border:1px solid #ddd;border-radius:5px;' }, [
E('h4', { 'style': 'margin:0 0 10px 0;' }, _('Features')),
E('div', {}, [
this.renderFeatureBadge(federation.enabled === '1', _('Federation')),
this.renderFeatureBadge(status.haproxy === '1', _('HAProxy')),
this.renderFeatureBadge(identity.linked === '1', _('DID Linked')),
this.renderFeatureBadge(mesh.published === '1', _('Mesh'))
])
]),
// Connection Card
E('div', { 'style': 'flex:1;min-width:200px;padding:15px;border:1px solid #ddd;border-radius:5px;' }, [
E('h4', { 'style': 'margin:0 0 10px 0;' }, _('Connection Info')),
E('div', { 'style': 'font-family:monospace;font-size:0.9em;' }, [
E('div', {}, _('Homeserver URL:')),
domain ? E('div', { 'style': 'color:#2196F3;' }, 'https://' + domain) :
E('div', { 'style': 'color:#666;' }, 'http://' + lanIp + ':' + httpPort),
E('div', { 'style': 'margin-top:5px;' }, _('Admin Room:')),
E('div', { 'style': 'color:#666;' }, '#admins:' + hostname)
])
])
])
]));
// Service Controls
content.push(E('div', { 'class': 'cbi-section' }, [
E('h4', {}, _('Service Controls')),
E('div', { 'class': 'cbi-page-actions', 'style': 'margin:0;' }, [
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'click': function() { self.handleAction('start'); },
'disabled': running
}, _('Start')),
E('button', {
'class': 'btn cbi-button cbi-button-negative',
'click': function() { self.handleAction('stop'); },
'disabled': !running,
'style': 'margin-left:5px;'
}, _('Stop')),
E('button', {
'class': 'btn cbi-button',
'click': function() { self.handleAction('update'); },
'style': 'margin-left:5px;'
}, _('Update')),
E('button', {
'class': 'btn cbi-button cbi-button-remove',
'click': function() {
if (confirm(_('Are you sure you want to uninstall? Data will be preserved.'))) {
self.handleAction('uninstall');
}
},
'style': 'margin-left:5px;'
}, _('Uninstall'))
])
]));
// User Management
content.push(E('div', { 'class': 'cbi-section' }, [
E('h4', {}, _('User Management')),
E('p', { 'style': 'color:#666;' }, _('Create Matrix users for this homeserver.')),
E('div', { 'style': 'display:flex;gap:10px;align-items:center;flex-wrap:wrap;' }, [
E('input', {
'type': 'text',
'id': 'new-user-mxid',
'placeholder': '@username:' + hostname,
'style': 'width:200px;padding:5px;'
}),
E('input', {
'type': 'password',
'id': 'new-user-password',
'placeholder': _('Password (optional)'),
'style': 'width:150px;padding:5px;'
}),
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'click': function() {
var mxid = document.getElementById('new-user-mxid').value;
var password = document.getElementById('new-user-password').value;
if (mxid) {
self.handleAction('user_add', { mxid: mxid, password: password });
}
}
}, _('Add User'))
]),
E('p', { 'style': 'margin-top:10px;color:#888;font-size:0.9em;' },
_('For user listing and deletion, use the admin room: #admins:') + hostname)
]));
// Emancipate (Public Exposure)
content.push(E('div', { 'class': 'cbi-section' }, [
E('h4', {}, _('Public Exposure')),
E('p', { 'style': 'color:#666;' }, _('Expose your Matrix server to the internet with SSL.')),
E('div', { 'style': 'display:flex;gap:10px;align-items:center;flex-wrap:wrap;' }, [
E('input', {
'type': 'text',
'id': 'emancipate-domain',
'placeholder': 'matrix.example.com',
'value': domain,
'style': 'width:250px;padding:5px;'
}),
E('button', {
'class': 'btn cbi-button cbi-button-action',
'click': function() {
var domain = document.getElementById('emancipate-domain').value;
if (domain) {
self.handleAction('emancipate', { domain: domain });
}
}
}, _('Emancipate'))
]),
domain ? E('div', { 'style': 'margin-top:10px;padding:10px;background:#e8f5e9;border-radius:5px;' }, [
E('strong', {}, _('Currently exposed at: ')),
E('a', { 'href': 'https://' + domain, 'target': '_blank' }, 'https://' + domain)
]) : ''
]));
// Identity Integration
content.push(E('div', { 'class': 'cbi-section' }, [
E('h4', {}, _('Identity Integration')),
E('p', { 'style': 'color:#666;' }, _('Link your Matrix identity to the node DID.')),
identity.linked === '1' ? E('div', { 'style': 'padding:10px;background:#e3f2fd;border-radius:5px;' }, [
E('div', {}, _('Matrix ID: ') + identity.mxid),
E('div', {}, _('Node DID: ') + (identity.did || 'N/A')),
E('button', {
'class': 'btn cbi-button',
'click': function() { self.handleAction('identity_unlink'); },
'style': 'margin-top:10px;'
}, _('Unlink'))
]) : E('div', { 'style': 'display:flex;gap:10px;align-items:center;' }, [
E('input', {
'type': 'text',
'id': 'identity-mxid',
'placeholder': '@user:' + hostname,
'style': 'width:200px;padding:5px;'
}),
E('button', {
'class': 'btn cbi-button cbi-button-action',
'click': function() {
var mxid = document.getElementById('identity-mxid').value;
if (mxid) {
self.handleAction('identity_link', { mxid: mxid });
}
}
}, _('Link Identity'))
])
]));
// Mesh Publication
content.push(E('div', { 'class': 'cbi-section' }, [
E('h4', {}, _('P2P Mesh Publication')),
E('p', { 'style': 'color:#666;' }, _('Publish this Matrix server to the SecuBox P2P mesh.')),
mesh.published === '1' ?
E('div', {}, [
E('span', { 'style': 'color:#4CAF50;' }, _('Published to mesh as: ') + mesh.service_name),
E('button', {
'class': 'btn cbi-button',
'click': function() { self.handleAction('mesh_unpublish'); },
'style': 'margin-left:15px;'
}, _('Unpublish'))
]) :
E('button', {
'class': 'btn cbi-button cbi-button-action',
'click': function() { self.handleAction('mesh_publish'); }
}, _('Publish to Mesh'))
]));
// Logs Section
content.push(E('div', { 'class': 'cbi-section' }, [
E('h4', {}, _('Server Logs')),
E('div', { 'style': 'display:flex;gap:10px;margin-bottom:10px;' }, [
E('button', {
'class': 'btn cbi-button',
'click': function() {
api.getLogs(100).then(function(res) {
var pre = document.getElementById('matrix-logs');
if (pre) pre.textContent = res.logs || 'No logs available';
});
}
}, _('Refresh Logs'))
]),
E('pre', {
'id': 'matrix-logs',
'style': 'background:#1e1e1e;color:#d4d4d4;padding:15px;border-radius:5px;max-height:300px;overflow:auto;font-size:0.85em;'
}, _('Click "Refresh Logs" to load...'))
]));
return E('div', {}, content);
},
render: function(data) {
var content = E('div', { 'id': 'matrix-content' }, this.renderContent(data));
// Poll for status updates
poll.add(L.bind(function() {
return api.getStatus().then(L.bind(function(status) {
// Update status badges without full reload
var statusBadge = document.querySelector('#matrix-content .cbi-section span');
if (statusBadge && status) {
var running = status.running === 'true' || status.running === true;
statusBadge.style.background = running ? '#4CAF50' : '#9e9e9e';
statusBadge.textContent = running ? _('Running') : _('Stopped');
}
}, this));
}, this), 10);
return content;
}
});