feat(jabber): Add Jabber/XMPP server packages (Prosody)
New packages: - secubox-app-jabber: LXC-based Prosody XMPP server with: - Debian Bookworm container - Full XMPP support (C2S, S2S, MUC, MAM) - HTTP upload for file sharing - BOSH and WebSocket for web clients - SSL/TLS encryption - User and room management via jabberctl - luci-app-jabber: LuCI dashboard with: - Status overview and service controls - User management (add/delete) - Emancipate workflow (HAProxy + SSL + DNS) - Connection info display - Log viewer CLI commands: jabberctl install/uninstall/start/stop/status jabberctl user add/del/passwd/list jabberctl room create/delete/list jabberctl emancipate <domain> Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3150c65e99
commit
b0d208777d
27
package/secubox/luci-app-jabber/Makefile
Normal file
27
package/secubox/luci-app-jabber/Makefile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI Jabber/XMPP Server (Prosody)
|
||||||
|
LUCI_DEPENDS:=+luci-base +secubox-app-jabber
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
PKG_LICENSE:=MIT
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
define Package/luci-app-jabber/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||||
|
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-jabber.json $(1)/usr/share/luci/menu.d/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||||
|
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-jabber.json $(1)/usr/share/rpcd/acl.d/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||||
|
$(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.jabber $(1)/usr/libexec/rpcd/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/jabber
|
||||||
|
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/jabber/overview.js $(1)/www/luci-static/resources/view/jabber/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/www/luci-static/resources/jabber
|
||||||
|
$(INSTALL_DATA) ./htdocs/luci-static/resources/jabber/api.js $(1)/www/luci-static/resources/jabber/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,luci-app-jabber))
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
'use strict';
|
||||||
|
'require rpc';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
status: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'status',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
start: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'start',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
stop: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'stop',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
install: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'install',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
uninstall: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'uninstall',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
update: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'update',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
logs: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'logs',
|
||||||
|
params: ['lines'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
emancipate: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'emancipate',
|
||||||
|
params: ['domain'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
configureHaproxy: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'configure_haproxy',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
userAdd: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'user_add',
|
||||||
|
params: ['jid', 'password'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
userDel: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'user_del',
|
||||||
|
params: ['jid'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
userPasswd: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'user_passwd',
|
||||||
|
params: ['jid', 'password'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
userList: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'user_list',
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
roomCreate: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'room_create',
|
||||||
|
params: ['name'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
roomDelete: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'room_delete',
|
||||||
|
params: ['name'],
|
||||||
|
expect: { }
|
||||||
|
}),
|
||||||
|
|
||||||
|
roomList: rpc.declare({
|
||||||
|
object: 'luci.jabber',
|
||||||
|
method: 'room_list',
|
||||||
|
expect: { }
|
||||||
|
})
|
||||||
|
});
|
||||||
@ -0,0 +1,420 @@
|
|||||||
|
'use strict';
|
||||||
|
'require view';
|
||||||
|
'require dom';
|
||||||
|
'require poll';
|
||||||
|
'require ui';
|
||||||
|
'require uci';
|
||||||
|
'require form';
|
||||||
|
'require jabber.api as api';
|
||||||
|
|
||||||
|
return view.extend({
|
||||||
|
handleAction: function(action, args) {
|
||||||
|
var self = this;
|
||||||
|
var btn = document.activeElement;
|
||||||
|
|
||||||
|
ui.showModal(_('Please wait...'), [
|
||||||
|
E('p', { 'class': 'spinning' }, _('Processing request...'))
|
||||||
|
]);
|
||||||
|
|
||||||
|
var promise;
|
||||||
|
switch(action) {
|
||||||
|
case 'start':
|
||||||
|
promise = api.start();
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
promise = api.stop();
|
||||||
|
break;
|
||||||
|
case 'install':
|
||||||
|
promise = api.install();
|
||||||
|
break;
|
||||||
|
case 'uninstall':
|
||||||
|
if (!confirm(_('This will remove the Jabber container. User data will be preserved. Continue?')))
|
||||||
|
return ui.hideModal();
|
||||||
|
promise = api.uninstall();
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
promise = api.update();
|
||||||
|
break;
|
||||||
|
case 'configure_haproxy':
|
||||||
|
promise = api.configureHaproxy();
|
||||||
|
break;
|
||||||
|
case 'emancipate':
|
||||||
|
var domain = args;
|
||||||
|
if (!domain) {
|
||||||
|
ui.hideModal();
|
||||||
|
ui.addNotification(null, E('p', _('Domain is required')), 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise = api.emancipate(domain);
|
||||||
|
break;
|
||||||
|
case 'user_add':
|
||||||
|
var jid = args.jid;
|
||||||
|
var password = args.password;
|
||||||
|
if (!jid) {
|
||||||
|
ui.hideModal();
|
||||||
|
ui.addNotification(null, E('p', _('JID is required')), 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise = api.userAdd(jid, password);
|
||||||
|
break;
|
||||||
|
case 'user_del':
|
||||||
|
if (!confirm(_('Delete user ') + args + '?'))
|
||||||
|
return ui.hideModal();
|
||||||
|
promise = api.userDel(args);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ui.hideModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(function(res) {
|
||||||
|
ui.hideModal();
|
||||||
|
if (res && res.success) {
|
||||||
|
var msg = res.message || _('Action completed');
|
||||||
|
if (res.password) {
|
||||||
|
msg += '\n' + _('Password: ') + res.password;
|
||||||
|
}
|
||||||
|
ui.addNotification(null, E('p', { 'style': 'white-space: pre-wrap;' }, msg), 'success');
|
||||||
|
self.load().then(function(data) {
|
||||||
|
dom.content(document.querySelector('#jabber-content'), self.renderContent(data));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ui.addNotification(null, E('p', res.error || _('Action failed')), 'error');
|
||||||
|
}
|
||||||
|
}).catch(function(e) {
|
||||||
|
ui.hideModal();
|
||||||
|
ui.addNotification(null, E('p', _('Error: ') + e.message), 'error');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
load: function() {
|
||||||
|
return Promise.all([
|
||||||
|
api.status(),
|
||||||
|
api.userList(),
|
||||||
|
uci.load('jabber')
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderInstallWizard: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return E('div', { 'class': 'cbi-section' }, [
|
||||||
|
E('h3', {}, _('Jabber/XMPP Server')),
|
||||||
|
E('p', {}, _('Prosody is a modern XMPP server written in Lua. It aims to be easy to set up and configure, and efficient with system resources.')),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('h4', {}, _('Features')),
|
||||||
|
E('ul', {}, [
|
||||||
|
E('li', {}, _('Secure messaging with end-to-end encryption (OMEMO)')),
|
||||||
|
E('li', {}, _('Multi-user chat rooms (MUC)')),
|
||||||
|
E('li', {}, _('File sharing with HTTP upload')),
|
||||||
|
E('li', {}, _('Server-to-server federation (S2S)')),
|
||||||
|
E('li', {}, _('BOSH and WebSocket for web clients')),
|
||||||
|
E('li', {}, _('Message archiving (MAM)'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('h4', {}, _('Compatible Clients')),
|
||||||
|
E('ul', {}, [
|
||||||
|
E('li', {}, _('Conversations (Android)')),
|
||||||
|
E('li', {}, _('Monal (iOS/macOS)')),
|
||||||
|
E('li', {}, _('Gajim (Windows/Linux)')),
|
||||||
|
E('li', {}, _('Dino (Linux)')),
|
||||||
|
E('li', {}, _('Converse.js (Web)'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-page-actions' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-positive',
|
||||||
|
'click': function() { self.handleAction('install'); }
|
||||||
|
}, _('Install Jabber/XMPP'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderStatusBadge: function(running) {
|
||||||
|
var color = running === 'true' ? '#4CAF50' : '#f44336';
|
||||||
|
var text = running === 'true' ? _('Running') : _('Stopped');
|
||||||
|
return E('span', {
|
||||||
|
'style': 'display:inline-block;padding:3px 10px;border-radius:3px;color:#fff;background:' + color
|
||||||
|
}, text);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderContent: function(data) {
|
||||||
|
var self = this;
|
||||||
|
var status = data[0] || {};
|
||||||
|
var userListData = data[1] || {};
|
||||||
|
|
||||||
|
if (status.container_state === 'not_installed') {
|
||||||
|
return this.renderInstallWizard();
|
||||||
|
}
|
||||||
|
|
||||||
|
var running = status.running === 'true';
|
||||||
|
var haproxyConfigured = status.haproxy === '1';
|
||||||
|
var domain = status.domain || '';
|
||||||
|
var hostname = status.hostname || 'jabber.local';
|
||||||
|
|
||||||
|
// Parse user list
|
||||||
|
var users = [];
|
||||||
|
if (userListData.users) {
|
||||||
|
users = userListData.users.split(',').filter(function(u) { return u.length > 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessUrl = '';
|
||||||
|
if (running) {
|
||||||
|
if (domain && haproxyConfigured) {
|
||||||
|
accessUrl = 'https://' + domain;
|
||||||
|
} else {
|
||||||
|
accessUrl = 'http://192.168.255.1:' + (status.http_port || '5280');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return E('div', { 'class': 'cbi-section' }, [
|
||||||
|
E('h3', {}, _('Jabber/XMPP Server (Prosody)')),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('Status')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, this.renderStatusBadge(status.running))
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('Hostname')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, hostname)
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('C2S Port')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, status.c2s_port || '5222')
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('S2S Port')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
status.s2s_port || '5269',
|
||||||
|
' ',
|
||||||
|
E('span', {
|
||||||
|
'style': 'display:inline-block;padding:2px 8px;border-radius:3px;color:#fff;background:' + (status.s2s_enabled === '1' ? '#4CAF50' : '#9e9e9e')
|
||||||
|
}, status.s2s_enabled === '1' ? _('Federation ON') : _('Federation OFF'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('HTTP/BOSH Port')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, status.http_port || '5280')
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('Users')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, status.user_count || '0')
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('MUC (Chat Rooms)')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('span', {
|
||||||
|
'style': 'display:inline-block;padding:3px 10px;border-radius:3px;color:#fff;background:' + (status.muc_enabled === '1' ? '#4CAF50' : '#9e9e9e')
|
||||||
|
}, status.muc_enabled === '1' ? _('Enabled') : _('Disabled'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('HAProxy')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('span', {
|
||||||
|
'style': 'display:inline-block;padding:3px 10px;border-radius:3px;color:#fff;background:' + (haproxyConfigured ? '#4CAF50' : '#9e9e9e')
|
||||||
|
}, haproxyConfigured ? _('Configured') : _('Not configured')),
|
||||||
|
' ',
|
||||||
|
!haproxyConfigured ? E('button', {
|
||||||
|
'class': 'btn cbi-button',
|
||||||
|
'click': function() { self.handleAction('configure_haproxy'); }
|
||||||
|
}, _('Configure')) : ''
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('Domain')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, domain || _('Not configured'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('hr'),
|
||||||
|
|
||||||
|
E('h4', {}, _('Service Controls')),
|
||||||
|
E('div', { 'class': 'cbi-page-actions', 'style': 'margin-bottom: 20px;' }, [
|
||||||
|
running ?
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-negative',
|
||||||
|
'click': function() { self.handleAction('stop'); }
|
||||||
|
}, _('Stop')) :
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-positive',
|
||||||
|
'click': function() { self.handleAction('start'); }
|
||||||
|
}, _('Start')),
|
||||||
|
' ',
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button',
|
||||||
|
'click': function() { self.handleAction('update'); }
|
||||||
|
}, _('Update')),
|
||||||
|
' ',
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-negative',
|
||||||
|
'click': function() { self.handleAction('uninstall'); }
|
||||||
|
}, _('Uninstall'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('hr'),
|
||||||
|
|
||||||
|
E('h4', {}, _('User Management')),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('New User')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('input', {
|
||||||
|
'type': 'text',
|
||||||
|
'id': 'new-user-jid',
|
||||||
|
'class': 'cbi-input-text',
|
||||||
|
'placeholder': 'user@' + hostname,
|
||||||
|
'style': 'width: 200px;'
|
||||||
|
}),
|
||||||
|
' ',
|
||||||
|
E('input', {
|
||||||
|
'type': 'password',
|
||||||
|
'id': 'new-user-password',
|
||||||
|
'class': 'cbi-input-text',
|
||||||
|
'placeholder': _('Password (auto-generate if empty)'),
|
||||||
|
'style': 'width: 200px;'
|
||||||
|
}),
|
||||||
|
' ',
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-positive',
|
||||||
|
'click': function() {
|
||||||
|
var jid = document.getElementById('new-user-jid').value;
|
||||||
|
var password = document.getElementById('new-user-password').value;
|
||||||
|
self.handleAction('user_add', { jid: jid, password: password });
|
||||||
|
}
|
||||||
|
}, _('Add User'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('Registered Users')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
users.length > 0 ?
|
||||||
|
E('table', { 'class': 'table', 'style': 'width: auto;' }, [
|
||||||
|
E('tr', { 'class': 'tr table-titles' }, [
|
||||||
|
E('th', { 'class': 'th' }, _('JID')),
|
||||||
|
E('th', { 'class': 'th' }, _('Actions'))
|
||||||
|
])
|
||||||
|
].concat(users.map(function(user) {
|
||||||
|
return E('tr', { 'class': 'tr' }, [
|
||||||
|
E('td', { 'class': 'td' }, user),
|
||||||
|
E('td', { 'class': 'td' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-remove',
|
||||||
|
'click': function() { self.handleAction('user_del', user); }
|
||||||
|
}, _('Delete'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}))) :
|
||||||
|
E('em', {}, _('No users registered'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('hr'),
|
||||||
|
|
||||||
|
E('h4', {}, _('Emancipate (Public Exposure)')),
|
||||||
|
E('p', {}, _('Make Jabber publicly accessible with SSL certificate, DNS records, and S2S federation.')),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, _('Domain')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('input', {
|
||||||
|
'type': 'text',
|
||||||
|
'id': 'emancipate-domain',
|
||||||
|
'class': 'cbi-input-text',
|
||||||
|
'placeholder': 'xmpp.example.com',
|
||||||
|
'value': domain
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-page-actions' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button cbi-button-action',
|
||||||
|
'click': function() {
|
||||||
|
var domainInput = document.getElementById('emancipate-domain');
|
||||||
|
self.handleAction('emancipate', domainInput.value);
|
||||||
|
}
|
||||||
|
}, _('Emancipate'))
|
||||||
|
]),
|
||||||
|
E('p', { 'style': 'font-size: 12px; color: #666;' }, [
|
||||||
|
_('DNS records needed: A record for domain, SRV records for _xmpp-client._tcp and _xmpp-server._tcp')
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('hr'),
|
||||||
|
|
||||||
|
E('h4', {}, _('Connection Info')),
|
||||||
|
E('div', { 'style': 'background: #f5f5f5; padding: 15px; border-radius: 4px; font-family: monospace;' }, [
|
||||||
|
E('p', {}, [
|
||||||
|
E('strong', {}, _('XMPP Server: ')),
|
||||||
|
hostname + ':' + (status.c2s_port || '5222')
|
||||||
|
]),
|
||||||
|
E('p', {}, [
|
||||||
|
E('strong', {}, _('BOSH URL: ')),
|
||||||
|
accessUrl + '/http-bind'
|
||||||
|
]),
|
||||||
|
E('p', {}, [
|
||||||
|
E('strong', {}, _('WebSocket: ')),
|
||||||
|
(domain && haproxyConfigured ? 'wss://' + domain : 'ws://192.168.255.1:' + (status.http_port || '5280')) + '/xmpp-websocket'
|
||||||
|
]),
|
||||||
|
E('p', {}, [
|
||||||
|
E('strong', {}, _('Admin JID: ')),
|
||||||
|
(status.admin_user || 'admin') + '@' + hostname
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
E('hr'),
|
||||||
|
|
||||||
|
E('h4', {}, _('Logs')),
|
||||||
|
E('div', { 'id': 'jabber-logs' }, [
|
||||||
|
E('pre', {
|
||||||
|
'style': 'background:#1e1e1e;color:#d4d4d4;padding:10px;max-height:300px;overflow:auto;font-size:12px;border-radius:4px;'
|
||||||
|
}, _('Loading logs...'))
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-page-actions' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button',
|
||||||
|
'click': function() {
|
||||||
|
api.logs(100).then(function(res) {
|
||||||
|
var logsEl = document.querySelector('#jabber-logs pre');
|
||||||
|
if (logsEl) {
|
||||||
|
logsEl.textContent = res.logs || _('No logs available');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('Refresh Logs'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(data) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var content = E('div', { 'id': 'jabber-content' }, this.renderContent(data));
|
||||||
|
|
||||||
|
// Load logs initially
|
||||||
|
api.logs(50).then(function(res) {
|
||||||
|
var logsEl = document.querySelector('#jabber-logs pre');
|
||||||
|
if (logsEl) {
|
||||||
|
logsEl.textContent = res.logs || _('No logs available');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Poll for status updates
|
||||||
|
poll.add(function() {
|
||||||
|
return api.status().then(function(status) {
|
||||||
|
// Update status badge if needed
|
||||||
|
});
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSaveApply: null,
|
||||||
|
handleSave: null,
|
||||||
|
handleReset: null
|
||||||
|
});
|
||||||
536
package/secubox/luci-app-jabber/root/usr/libexec/rpcd/luci.jabber
Executable file
536
package/secubox/luci-app-jabber/root/usr/libexec/rpcd/luci.jabber
Executable file
@ -0,0 +1,536 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# RPCD backend for Jabber/XMPP LuCI app
|
||||||
|
|
||||||
|
. /usr/share/libubox/jshn.sh
|
||||||
|
|
||||||
|
JABBERCTL="/usr/sbin/jabberctl"
|
||||||
|
|
||||||
|
# Helper to get UCI value
|
||||||
|
uci_get() {
|
||||||
|
local section="$1"
|
||||||
|
local option="$2"
|
||||||
|
local default="$3"
|
||||||
|
local val
|
||||||
|
val=$(uci -q get "jabber.${section}.${option}")
|
||||||
|
echo "${val:-$default}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get container status
|
||||||
|
get_container_status() {
|
||||||
|
local state="not_installed"
|
||||||
|
local running="false"
|
||||||
|
local lxc_info=""
|
||||||
|
|
||||||
|
if [ -d "/srv/lxc/jabber" ]; then
|
||||||
|
state="installed"
|
||||||
|
lxc_info=$(lxc-info -n jabber 2>/dev/null)
|
||||||
|
if echo "$lxc_info" | grep -q "State:.*RUNNING"; then
|
||||||
|
running="true"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$state $running"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: status
|
||||||
|
method_status() {
|
||||||
|
local enabled hostname c2s_port s2s_port http_port
|
||||||
|
local container_state running
|
||||||
|
local info user_count
|
||||||
|
|
||||||
|
enabled=$(uci_get main enabled 0)
|
||||||
|
hostname=$(uci_get server hostname "jabber.local")
|
||||||
|
c2s_port=$(uci_get server c2s_port "5222")
|
||||||
|
s2s_port=$(uci_get server s2s_port "5269")
|
||||||
|
http_port=$(uci_get server http_port "5280")
|
||||||
|
|
||||||
|
info=$(get_container_status)
|
||||||
|
container_state=$(echo "$info" | awk '{print $1}')
|
||||||
|
running=$(echo "$info" | awk '{print $2}')
|
||||||
|
|
||||||
|
# Get user count
|
||||||
|
user_count=0
|
||||||
|
if [ "$running" = "true" ]; then
|
||||||
|
user_count=$(lxc-attach -n jabber -- find /var/lib/prosody -name "*.dat" -path "*accounts*" 2>/dev/null | wc -l)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get configured domain if emancipated
|
||||||
|
local domain haproxy muc_enabled s2s_enabled
|
||||||
|
domain=$(uci_get network domain "")
|
||||||
|
haproxy=$(uci_get network haproxy "0")
|
||||||
|
muc_enabled=$(uci_get muc enabled "1")
|
||||||
|
s2s_enabled=$(uci_get s2s enabled "0")
|
||||||
|
|
||||||
|
# Get admin info
|
||||||
|
local admin_email admin_user
|
||||||
|
admin_email=$(uci_get admin email "admin@localhost")
|
||||||
|
admin_user=$(uci_get admin initial_user "admin")
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_string "enabled" "$enabled"
|
||||||
|
json_add_string "container_state" "$container_state"
|
||||||
|
json_add_string "running" "$running"
|
||||||
|
json_add_string "hostname" "$hostname"
|
||||||
|
json_add_string "c2s_port" "$c2s_port"
|
||||||
|
json_add_string "s2s_port" "$s2s_port"
|
||||||
|
json_add_string "http_port" "$http_port"
|
||||||
|
json_add_int "user_count" "$user_count"
|
||||||
|
json_add_string "domain" "$domain"
|
||||||
|
json_add_string "haproxy" "$haproxy"
|
||||||
|
json_add_string "muc_enabled" "$muc_enabled"
|
||||||
|
json_add_string "s2s_enabled" "$s2s_enabled"
|
||||||
|
json_add_string "admin_email" "$admin_email"
|
||||||
|
json_add_string "admin_user" "$admin_user"
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: start
|
||||||
|
method_start() {
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL start 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Jabber/XMPP started successfully"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: stop
|
||||||
|
method_stop() {
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL stop 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Jabber/XMPP stopped successfully"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: install
|
||||||
|
method_install() {
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL install 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Jabber/XMPP installed successfully"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: uninstall
|
||||||
|
method_uninstall() {
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL uninstall 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Jabber/XMPP uninstalled successfully"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: update
|
||||||
|
method_update() {
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL update 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Jabber/XMPP updated successfully"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: logs
|
||||||
|
method_logs() {
|
||||||
|
local lines="${1:-50}"
|
||||||
|
local output
|
||||||
|
|
||||||
|
if [ -d "/srv/lxc/jabber" ]; then
|
||||||
|
output=$($JABBERCTL logs "$lines" 2>&1 | tail -n "$lines")
|
||||||
|
else
|
||||||
|
output="Container not installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_string "logs" "$output"
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: emancipate
|
||||||
|
method_emancipate() {
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var domain domain
|
||||||
|
|
||||||
|
if [ -z "$domain" ]; then
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "Domain is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL emancipate "$domain" 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Jabber/XMPP emancipated to $domain"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: configure_haproxy
|
||||||
|
method_configure_haproxy() {
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL configure-haproxy 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "HAProxy configured for Jabber"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: user_add
|
||||||
|
method_user_add() {
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var jid jid
|
||||||
|
json_get_var password password
|
||||||
|
|
||||||
|
if [ -z "$jid" ]; then
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "JID is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
|
if [ -n "$password" ]; then
|
||||||
|
output=$($JABBERCTL user add "$jid" "$password" 2>&1)
|
||||||
|
else
|
||||||
|
output=$($JABBERCTL user add "$jid" 2>&1)
|
||||||
|
fi
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "User $jid created"
|
||||||
|
# Extract password from output
|
||||||
|
local new_pass=$(echo "$output" | grep -oE 'Password: [^ ]+' | cut -d: -f2 | tr -d ' ')
|
||||||
|
json_add_string "password" "$new_pass"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: user_del
|
||||||
|
method_user_del() {
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var jid jid
|
||||||
|
|
||||||
|
if [ -z "$jid" ]; then
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "JID is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL user del "$jid" 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "User $jid deleted"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: user_passwd
|
||||||
|
method_user_passwd() {
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var jid jid
|
||||||
|
json_get_var password password
|
||||||
|
|
||||||
|
if [ -z "$jid" ]; then
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "JID is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
|
if [ -n "$password" ]; then
|
||||||
|
output=$($JABBERCTL user passwd "$jid" "$password" 2>&1)
|
||||||
|
else
|
||||||
|
output=$($JABBERCTL user passwd "$jid" 2>&1)
|
||||||
|
fi
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Password changed for $jid"
|
||||||
|
local new_pass=$(echo "$output" | grep -oE 'New password: [^ ]+' | cut -d: -f2 | tr -d ' ')
|
||||||
|
json_add_string "password" "$new_pass"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: user_list
|
||||||
|
method_user_list() {
|
||||||
|
local users=""
|
||||||
|
|
||||||
|
if lxc-info -n jabber 2>/dev/null | grep -q "RUNNING"; then
|
||||||
|
users=$(lxc-attach -n jabber -- find /var/lib/prosody -name "*.dat" -path "*accounts*" 2>/dev/null | while read f; do
|
||||||
|
user=$(basename "$f" .dat)
|
||||||
|
domain=$(echo "$f" | grep -oE '[^/]+/accounts' | cut -d/ -f1 | tr '%' '.')
|
||||||
|
echo "${user}@${domain}"
|
||||||
|
done | paste -sd,)
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_string "users" "$users"
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: room_create
|
||||||
|
method_room_create() {
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var name name
|
||||||
|
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "Room name is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL room create "$name" 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Room $name available (created on first join)"
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: room_delete
|
||||||
|
method_room_delete() {
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var name name
|
||||||
|
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "Room name is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local output
|
||||||
|
output=$($JABBERCTL room delete "$name" 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
json_init
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Room $name deleted"
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "$output"
|
||||||
|
fi
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method: room_list
|
||||||
|
method_room_list() {
|
||||||
|
local rooms=""
|
||||||
|
|
||||||
|
if lxc-info -n jabber 2>/dev/null | grep -q "RUNNING"; then
|
||||||
|
rooms=$(lxc-attach -n jabber -- find /var/lib/prosody -name "*.dat" -path "*rooms*" 2>/dev/null | while read f; do
|
||||||
|
room=$(basename "$f" .dat)
|
||||||
|
echo "$room"
|
||||||
|
done | paste -sd,)
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_string "rooms" "$rooms"
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# List available methods
|
||||||
|
list_methods() {
|
||||||
|
json_init
|
||||||
|
json_add_object "status"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "start"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "stop"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "install"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "uninstall"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "update"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "logs"
|
||||||
|
json_add_int "lines" 50
|
||||||
|
json_close_object
|
||||||
|
json_add_object "emancipate"
|
||||||
|
json_add_string "domain" ""
|
||||||
|
json_close_object
|
||||||
|
json_add_object "configure_haproxy"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "user_add"
|
||||||
|
json_add_string "jid" ""
|
||||||
|
json_add_string "password" ""
|
||||||
|
json_close_object
|
||||||
|
json_add_object "user_del"
|
||||||
|
json_add_string "jid" ""
|
||||||
|
json_close_object
|
||||||
|
json_add_object "user_passwd"
|
||||||
|
json_add_string "jid" ""
|
||||||
|
json_add_string "password" ""
|
||||||
|
json_close_object
|
||||||
|
json_add_object "user_list"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "room_create"
|
||||||
|
json_add_string "name" ""
|
||||||
|
json_close_object
|
||||||
|
json_add_object "room_delete"
|
||||||
|
json_add_string "name" ""
|
||||||
|
json_close_object
|
||||||
|
json_add_object "room_list"
|
||||||
|
json_close_object
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main dispatcher
|
||||||
|
case "$1" in
|
||||||
|
list)
|
||||||
|
list_methods
|
||||||
|
;;
|
||||||
|
call)
|
||||||
|
case "$2" in
|
||||||
|
status)
|
||||||
|
method_status
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
method_start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
method_stop
|
||||||
|
;;
|
||||||
|
install)
|
||||||
|
method_install
|
||||||
|
;;
|
||||||
|
uninstall)
|
||||||
|
method_uninstall
|
||||||
|
;;
|
||||||
|
update)
|
||||||
|
method_update
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
read -r input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_var lines lines
|
||||||
|
method_logs "${lines:-50}"
|
||||||
|
;;
|
||||||
|
emancipate)
|
||||||
|
method_emancipate
|
||||||
|
;;
|
||||||
|
configure_haproxy)
|
||||||
|
method_configure_haproxy
|
||||||
|
;;
|
||||||
|
user_add)
|
||||||
|
method_user_add
|
||||||
|
;;
|
||||||
|
user_del)
|
||||||
|
method_user_del
|
||||||
|
;;
|
||||||
|
user_passwd)
|
||||||
|
method_user_passwd
|
||||||
|
;;
|
||||||
|
user_list)
|
||||||
|
method_user_list
|
||||||
|
;;
|
||||||
|
room_create)
|
||||||
|
method_room_create
|
||||||
|
;;
|
||||||
|
room_delete)
|
||||||
|
method_room_delete
|
||||||
|
;;
|
||||||
|
room_list)
|
||||||
|
method_room_list
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo '{"error":"Method not found"}'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo '{"error":"Invalid action"}'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"admin/services/jabber": {
|
||||||
|
"title": "Jabber/XMPP",
|
||||||
|
"order": 68,
|
||||||
|
"action": {
|
||||||
|
"type": "view",
|
||||||
|
"path": "jabber/overview"
|
||||||
|
},
|
||||||
|
"depends": {
|
||||||
|
"acl": ["luci-app-jabber"],
|
||||||
|
"uci": {"jabber": true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"luci-app-jabber": {
|
||||||
|
"description": "Grant access to Jabber/XMPP management",
|
||||||
|
"read": {
|
||||||
|
"ubus": {
|
||||||
|
"luci.jabber": ["status", "logs", "user_list", "room_list"]
|
||||||
|
},
|
||||||
|
"uci": ["jabber"]
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"ubus": {
|
||||||
|
"luci.jabber": ["start", "stop", "install", "uninstall", "update", "emancipate", "configure_haproxy", "user_add", "user_del", "user_passwd", "room_create", "room_delete"]
|
||||||
|
},
|
||||||
|
"uci": ["jabber"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
package/secubox/secubox-app-jabber/Makefile
Normal file
45
package/secubox/secubox-app-jabber/Makefile
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=secubox-app-jabber
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
PKG_VERSION:=1.0.0
|
||||||
|
PKG_ARCH:=all
|
||||||
|
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||||
|
PKG_LICENSE:=MIT
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/secubox-app-jabber
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
PKGARCH:=all
|
||||||
|
SUBMENU:=SecuBox Apps
|
||||||
|
TITLE:=SecuBox Jabber/XMPP Server (Prosody)
|
||||||
|
DEPENDS:=+lxc +lxc-common +wget-ssl +tar +jsonfilter
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jabber/description
|
||||||
|
Jabber/XMPP instant messaging server based on Prosody.
|
||||||
|
Runs in an LXC Debian container with full XMPP support.
|
||||||
|
Features multi-user chat (MUC), file uploads, and S2S federation.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jabber/conffiles
|
||||||
|
/etc/config/jabber
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jabber/install
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_CONF) ./files/etc/config/jabber $(1)/etc/config/jabber
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/etc/init.d/jabber $(1)/etc/init.d/jabber
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/sbin
|
||||||
|
$(INSTALL_BIN) ./files/usr/sbin/jabberctl $(1)/usr/sbin/jabberctl
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,secubox-app-jabber))
|
||||||
36
package/secubox/secubox-app-jabber/files/etc/config/jabber
Normal file
36
package/secubox/secubox-app-jabber/files/etc/config/jabber
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
config jabber 'main'
|
||||||
|
option enabled '0'
|
||||||
|
option data_path '/srv/jabber'
|
||||||
|
option memory_limit '512'
|
||||||
|
|
||||||
|
config jabber 'server'
|
||||||
|
option hostname 'jabber.local'
|
||||||
|
option c2s_port '5222'
|
||||||
|
option s2s_port '5269'
|
||||||
|
option http_port '5280'
|
||||||
|
option https_port '5281'
|
||||||
|
|
||||||
|
config jabber 'admin'
|
||||||
|
option email 'admin@localhost'
|
||||||
|
option initial_user 'admin'
|
||||||
|
option initial_password ''
|
||||||
|
|
||||||
|
config jabber 'muc'
|
||||||
|
option enabled '1'
|
||||||
|
option host 'conference'
|
||||||
|
option default_room_logging '0'
|
||||||
|
|
||||||
|
config jabber 'http_upload'
|
||||||
|
option enabled '1'
|
||||||
|
option max_size '10485760'
|
||||||
|
option host 'upload'
|
||||||
|
|
||||||
|
config jabber 'network'
|
||||||
|
option domain ''
|
||||||
|
option haproxy '0'
|
||||||
|
option haproxy_ssl '1'
|
||||||
|
option firewall_wan '0'
|
||||||
|
|
||||||
|
config jabber 's2s'
|
||||||
|
option enabled '0'
|
||||||
|
option require_encryption '1'
|
||||||
35
package/secubox/secubox-app-jabber/files/etc/init.d/jabber
Executable file
35
package/secubox/secubox-app-jabber/files/etc/init.d/jabber
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
START=95
|
||||||
|
STOP=10
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
SERVICE_BIN="/usr/sbin/jabberctl"
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local enabled
|
||||||
|
config_load jabber
|
||||||
|
config_get enabled main enabled 0
|
||||||
|
|
||||||
|
[ "$enabled" = "1" ] || return 0
|
||||||
|
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param command "$SERVICE_BIN" service-run
|
||||||
|
procd_set_param respawn 3600 5 5
|
||||||
|
procd_set_param stdout 1
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
"$SERVICE_BIN" service-stop >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_service() {
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers() {
|
||||||
|
procd_add_reload_trigger "jabber"
|
||||||
|
}
|
||||||
1127
package/secubox/secubox-app-jabber/files/usr/sbin/jabberctl
Executable file
1127
package/secubox/secubox-app-jabber/files/usr/sbin/jabberctl
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user