feat(simplex): Add SimpleX Chat self-hosted messaging servers
Integrate SimpleX Chat SMP and XFTP servers for privacy-focused messaging: - secubox-app-simplex: Backend with LXC container management - SMP server for message relay (port 5223) - XFTP server for encrypted file sharing (port 443) - Auto-download of SimpleX binaries for aarch64/x86_64 - TLS certificate generation (self-signed or Let's Encrypt) - Firewall and HAProxy integration - luci-app-simplex: LuCI dashboard with: - Service status monitoring - Server address display with copy-to-clipboard - Full configuration forms for SMP, XFTP, and TLS - Install/certificate management actions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ec31cdba12
commit
db847ba1cd
29
package/secubox/luci-app-simplex/Makefile
Normal file
29
package/secubox/luci-app-simplex/Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI SimpleX Chat Server Configuration
|
||||
LUCI_DEPENDS:=+secubox-app-simplex
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_NAME:=luci-app-simplex
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
define Package/luci-app-simplex/install
|
||||
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-simplex.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-simplex.json $(1)/usr/share/rpcd/acl.d/
|
||||
|
||||
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/simplex
|
||||
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/simplex/*.js $(1)/www/luci-static/resources/view/simplex/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||
$(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.simplex $(1)/usr/libexec/rpcd/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,luci-app-simplex))
|
||||
@ -0,0 +1,458 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require uci';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
'require ui';
|
||||
|
||||
var callSimplexStatus = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'status',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
var callSimplexStart = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'start'
|
||||
});
|
||||
|
||||
var callSimplexStop = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'stop'
|
||||
});
|
||||
|
||||
var callSimplexRestart = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'restart'
|
||||
});
|
||||
|
||||
var callSimplexInstall = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'install'
|
||||
});
|
||||
|
||||
var callSimplexGetAddresses = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'get_addresses',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
var callSimplexGetStats = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'get_stats',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
var callSimplexInitCerts = rpc.declare({
|
||||
object: 'luci.simplex',
|
||||
method: 'init_certs',
|
||||
params: ['hostname']
|
||||
});
|
||||
|
||||
function formatBytes(bytes, decimals) {
|
||||
if (bytes === 0) return '0 B';
|
||||
var k = 1024;
|
||||
var dm = decimals || 2;
|
||||
var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
ui.addNotification(null, E('p', _('Address copied to clipboard')), 'success');
|
||||
});
|
||||
} else {
|
||||
var textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
ui.addNotification(null, E('p', _('Address copied to clipboard')), 'success');
|
||||
}
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
uci.load('simplex'),
|
||||
callSimplexStatus(),
|
||||
callSimplexGetAddresses(),
|
||||
callSimplexGetStats()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var status = data[1];
|
||||
var addresses = data[2];
|
||||
var stats = data[3];
|
||||
var m, s, o;
|
||||
|
||||
m = new form.Map('simplex', _('SimpleX Chat Server'),
|
||||
_('Privacy-focused self-hosted messaging infrastructure with SMP (message relay) and XFTP (file transfer) servers.'));
|
||||
|
||||
// ==========================================
|
||||
// Status Section
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'main', 'simplex', _('Service Status'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_status', _('Status'));
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function() {
|
||||
var html = '<div class="simplex-status" style="display:flex;gap:30px;flex-wrap:wrap;align-items:flex-start;">';
|
||||
|
||||
// LXC/Container status
|
||||
html += '<div class="status-card" style="min-width:200px;">';
|
||||
html += '<h4 style="margin:0 0 10px 0;border-bottom:1px solid #ddd;padding-bottom:5px;">Container</h4>';
|
||||
|
||||
if (!status.lxc_available) {
|
||||
html += '<p style="color:#c00;"><b>LXC not installed</b></p>';
|
||||
html += '<p><small>Install: opkg install lxc lxc-common</small></p>';
|
||||
} else if (!status.container_exists) {
|
||||
html += '<p style="color:#888;"><b>Not installed</b></p>';
|
||||
html += '<p><small>Click "Install" below</small></p>';
|
||||
} else {
|
||||
var containerColor = status.container_status === 'running' ? '#080' : '#c60';
|
||||
html += '<p><b>Status:</b> <span style="color:' + containerColor + '">' + status.container_status + '</span></p>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// SMP Server status
|
||||
if (status.smp_enabled) {
|
||||
html += '<div class="status-card" style="min-width:200px;">';
|
||||
html += '<h4 style="margin:0 0 10px 0;border-bottom:1px solid #ddd;padding-bottom:5px;">SMP Server</h4>';
|
||||
var smpColor = status.smp_status === 'running' ? '#080' : '#c00';
|
||||
html += '<p><b>Status:</b> <span style="color:' + smpColor + '">' + status.smp_status + '</span></p>';
|
||||
html += '<p><b>Port:</b> ' + status.smp_port + '/tcp</p>';
|
||||
if (status.smp_hostname) {
|
||||
html += '<p><b>Host:</b> ' + status.smp_hostname + '</p>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// XFTP Server status
|
||||
if (status.xftp_enabled) {
|
||||
html += '<div class="status-card" style="min-width:200px;">';
|
||||
html += '<h4 style="margin:0 0 10px 0;border-bottom:1px solid #ddd;padding-bottom:5px;">XFTP Server</h4>';
|
||||
var xftpColor = status.xftp_status === 'running' ? '#080' : '#c00';
|
||||
html += '<p><b>Status:</b> <span style="color:' + xftpColor + '">' + status.xftp_status + '</span></p>';
|
||||
html += '<p><b>Port:</b> ' + status.xftp_port + '/tcp</p>';
|
||||
if (status.xftp_hostname) {
|
||||
html += '<p><b>Host:</b> ' + status.xftp_hostname + '</p>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Storage stats
|
||||
if (stats) {
|
||||
html += '<div class="status-card" style="min-width:200px;">';
|
||||
html += '<h4 style="margin:0 0 10px 0;border-bottom:1px solid #ddd;padding-bottom:5px;">Storage</h4>';
|
||||
html += '<p><b>Used:</b> ' + formatBytes(stats.storage_used || 0) + '</p>';
|
||||
html += '<p><b>Quota:</b> ' + formatBytes(stats.storage_quota || 0) + '</p>';
|
||||
html += '<p><b>Files:</b> ' + (stats.file_count || 0) + '</p>';
|
||||
if (stats.storage_quota > 0) {
|
||||
var pct = Math.round((stats.storage_used / stats.storage_quota) * 100);
|
||||
var barColor = pct > 80 ? '#c00' : (pct > 60 ? '#c60' : '#080');
|
||||
html += '<div style="background:#ddd;border-radius:3px;height:8px;margin-top:5px;">';
|
||||
html += '<div style="background:' + barColor + ';width:' + Math.min(pct, 100) + '%;height:100%;border-radius:3px;"></div>';
|
||||
html += '</div>';
|
||||
html += '<small>' + pct + '% used</small>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
return html;
|
||||
};
|
||||
|
||||
// Control buttons
|
||||
o = s.option(form.Button, '_start', _('Start'));
|
||||
o.inputtitle = _('Start');
|
||||
o.inputstyle = 'apply';
|
||||
o.onclick = function() {
|
||||
return callSimplexStart().then(function() {
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
o = s.option(form.Button, '_stop', _('Stop'));
|
||||
o.inputtitle = _('Stop');
|
||||
o.inputstyle = 'remove';
|
||||
o.onclick = function() {
|
||||
return callSimplexStop().then(function() {
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
o = s.option(form.Button, '_restart', _('Restart'));
|
||||
o.inputtitle = _('Restart');
|
||||
o.inputstyle = 'reload';
|
||||
o.onclick = function() {
|
||||
return callSimplexRestart().then(function() {
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// ==========================================
|
||||
// Server Addresses Section
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'main', 'simplex', _('Server Addresses'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_addresses', _('Addresses'));
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function() {
|
||||
var html = '<div class="simplex-addresses" style="background:#f8f8f8;padding:15px;border-radius:5px;">';
|
||||
|
||||
if (!addresses.smp_address && !addresses.xftp_address) {
|
||||
html += '<p style="color:#888;">Server addresses will appear here after installation and configuration.</p>';
|
||||
html += '<p><small>Set hostnames in the configuration below, then restart the service.</small></p>';
|
||||
} else {
|
||||
html += '<p style="margin-bottom:10px;"><small>Add these addresses to your SimpleX Chat app under Settings > Network & Servers</small></p>';
|
||||
|
||||
if (addresses.smp_address) {
|
||||
html += '<div style="margin-bottom:15px;">';
|
||||
html += '<label style="font-weight:bold;display:block;margin-bottom:5px;">SMP Server (messaging):</label>';
|
||||
html += '<div style="display:flex;gap:10px;align-items:center;">';
|
||||
html += '<code style="flex:1;background:#fff;padding:8px;border:1px solid #ddd;border-radius:3px;word-break:break-all;font-size:12px;">' + addresses.smp_address + '</code>';
|
||||
html += '<button class="cbi-button" onclick="copyToClipboard(\'' + addresses.smp_address + '\')">Copy</button>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
if (addresses.xftp_address) {
|
||||
html += '<div>';
|
||||
html += '<label style="font-weight:bold;display:block;margin-bottom:5px;">XFTP Server (file transfer):</label>';
|
||||
html += '<div style="display:flex;gap:10px;align-items:center;">';
|
||||
html += '<code style="flex:1;background:#fff;padding:8px;border:1px solid #ddd;border-radius:3px;word-break:break-all;font-size:12px;">' + addresses.xftp_address + '</code>';
|
||||
html += '<button class="cbi-button" onclick="copyToClipboard(\'' + addresses.xftp_address + '\')">Copy</button>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
// Add copyToClipboard function to window
|
||||
html += '<script>window.copyToClipboard = ' + copyToClipboard.toString() + '</script>';
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
// ==========================================
|
||||
// General Configuration
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'main', 'simplex', _('General Configuration'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enabled'),
|
||||
_('Enable SimpleX Chat servers'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'data_path', _('Data Path'),
|
||||
_('Path for server data and configuration'));
|
||||
o.default = '/srv/simplex';
|
||||
o.placeholder = '/srv/simplex';
|
||||
|
||||
o = s.option(form.Value, 'memory_limit', _('Memory Limit'),
|
||||
_('Container memory limit'));
|
||||
o.default = '256M';
|
||||
o.placeholder = '256M';
|
||||
|
||||
// ==========================================
|
||||
// SMP Server Configuration
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'smp', 'smp', _('SMP Server (Message Relay)'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enabled'),
|
||||
_('Enable SMP server for message relay'));
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.Value, 'hostname', _('Hostname'),
|
||||
_('Public hostname or IP address for clients to connect'));
|
||||
o.placeholder = 'smp.example.com';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'port', _('Port'),
|
||||
_('TCP port for SMP connections'));
|
||||
o.datatype = 'port';
|
||||
o.default = '5223';
|
||||
|
||||
o = s.option(form.Value, 'control_port', _('Control Port'),
|
||||
_('Local control port (admin API)'));
|
||||
o.datatype = 'port';
|
||||
o.default = '5224';
|
||||
|
||||
o = s.option(form.Flag, 'store_log', _('Store Message Log'),
|
||||
_('Enable message store for offline delivery'));
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.Flag, 'daily_stats', _('Daily Statistics'),
|
||||
_('Collect daily usage statistics'));
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.Value, 'queue_password', _('Queue Password'),
|
||||
_('Optional: require password to create new message queues'));
|
||||
o.password = true;
|
||||
o.optional = true;
|
||||
|
||||
// ==========================================
|
||||
// XFTP Server Configuration
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'xftp', 'xftp', _('XFTP Server (File Transfer)'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enabled'),
|
||||
_('Enable XFTP server for encrypted file sharing'));
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.Value, 'hostname', _('Hostname'),
|
||||
_('Public hostname or IP address for clients to connect'));
|
||||
o.placeholder = 'xftp.example.com';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'port', _('Port'),
|
||||
_('TCP port for XFTP connections'));
|
||||
o.datatype = 'port';
|
||||
o.default = '443';
|
||||
|
||||
o = s.option(form.Value, 'control_port', _('Control Port'),
|
||||
_('Local control port (admin API)'));
|
||||
o.datatype = 'port';
|
||||
o.default = '5225';
|
||||
|
||||
o = s.option(form.Value, 'storage_quota', _('Storage Quota'),
|
||||
_('Maximum storage for files (e.g., 10G, 500M)'));
|
||||
o.default = '10G';
|
||||
o.placeholder = '10G';
|
||||
|
||||
o = s.option(form.Value, 'file_expiry', _('File Expiry'),
|
||||
_('Time before files are deleted (e.g., 48h, 7d)'));
|
||||
o.default = '48h';
|
||||
o.placeholder = '48h';
|
||||
|
||||
o = s.option(form.Value, 'create_password', _('Upload Password'),
|
||||
_('Optional: require password to upload files'));
|
||||
o.password = true;
|
||||
o.optional = true;
|
||||
|
||||
// ==========================================
|
||||
// TLS Configuration
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'tls', 'tls', _('TLS Certificates'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'use_letsencrypt', _('Use Let\'s Encrypt'),
|
||||
_('Automatically obtain certificates from Let\'s Encrypt'));
|
||||
o.default = '0';
|
||||
|
||||
o = s.option(form.Value, 'domain', _('Domain'),
|
||||
_('Domain for TLS certificates'));
|
||||
o.placeholder = 'simplex.example.com';
|
||||
o.depends('use_letsencrypt', '1');
|
||||
|
||||
o = s.option(form.Value, 'email', _('Email'),
|
||||
_('Email for Let\'s Encrypt notifications'));
|
||||
o.placeholder = 'admin@example.com';
|
||||
o.depends('use_letsencrypt', '1');
|
||||
|
||||
o = s.option(form.Value, 'cert_path', _('Certificate Path'),
|
||||
_('Path to store TLS certificates'));
|
||||
o.default = '/srv/simplex/certs';
|
||||
|
||||
// ==========================================
|
||||
// Actions Section
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'main', 'simplex', _('Actions'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Button, '_install', _('Install Servers'));
|
||||
o.inputtitle = _('Install');
|
||||
o.inputstyle = 'apply';
|
||||
o.onclick = function() {
|
||||
if (confirm(_('This will download SimpleX binaries and create the LXC container. Continue?'))) {
|
||||
ui.showModal(_('Installing SimpleX'), [
|
||||
E('p', { 'class': 'spinning' }, _('Downloading binaries and setting up container...'))
|
||||
]);
|
||||
return callSimplexInstall().then(function(res) {
|
||||
ui.hideModal();
|
||||
if (res.success) {
|
||||
ui.addNotification(null, E('p', _('Installation completed successfully')), 'success');
|
||||
} else {
|
||||
ui.addNotification(null, E('p', _('Installation failed: ') + (res.output || 'Unknown error')), 'error');
|
||||
}
|
||||
window.location.reload();
|
||||
}).catch(function(err) {
|
||||
ui.hideModal();
|
||||
ui.addNotification(null, E('p', _('Installation failed: ') + err), 'error');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
o = s.option(form.Button, '_init_certs', _('Generate Certificates'));
|
||||
o.inputtitle = _('Generate TLS Certs');
|
||||
o.inputstyle = 'reload';
|
||||
o.onclick = function() {
|
||||
var hostname = uci.get('simplex', 'smp', 'hostname') || uci.get('simplex', 'tls', 'domain');
|
||||
if (!hostname) {
|
||||
ui.addNotification(null, E('p', _('Please set a hostname first')), 'warning');
|
||||
return;
|
||||
}
|
||||
return callSimplexInitCerts(hostname).then(function(res) {
|
||||
if (res.success) {
|
||||
ui.addNotification(null, E('p', _('Certificates generated successfully')), 'success');
|
||||
} else {
|
||||
ui.addNotification(null, E('p', _('Certificate generation failed: ') + (res.output || 'Unknown error')), 'error');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ==========================================
|
||||
// Help Section
|
||||
// ==========================================
|
||||
s = m.section(form.NamedSection, 'main', 'simplex', _('About SimpleX'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_help');
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function() {
|
||||
return '<div style="background:#f5f5f5;padding:15px;border-radius:5px;">' +
|
||||
'<h4>What is SimpleX?</h4>' +
|
||||
'<p>SimpleX Chat is a privacy-focused messaging platform with no user identifiers. ' +
|
||||
'Self-hosting your own servers ensures your messages never pass through third-party infrastructure.</p>' +
|
||||
'<h4>Server Types:</h4>' +
|
||||
'<ul>' +
|
||||
'<li><b>SMP Server</b> - Message relay using Simple Messaging Protocol</li>' +
|
||||
'<li><b>XFTP Server</b> - Encrypted file transfer storage</li>' +
|
||||
'</ul>' +
|
||||
'<h4>Setup Steps:</h4>' +
|
||||
'<ol>' +
|
||||
'<li>Set hostnames for SMP and XFTP servers above</li>' +
|
||||
'<li>Click "Install Servers" to download binaries and create container</li>' +
|
||||
'<li>Enable the service and save configuration</li>' +
|
||||
'<li>Open firewall ports (5223 for SMP, 443 for XFTP)</li>' +
|
||||
'<li>Add server addresses to your SimpleX Chat mobile app</li>' +
|
||||
'</ol>' +
|
||||
'<h4>Resources:</h4>' +
|
||||
'<ul>' +
|
||||
'<li><a href="https://simplex.chat" target="_blank">SimpleX Chat Website</a></li>' +
|
||||
'<li><a href="https://github.com/simplex-chat/simplexmq" target="_blank">SimpleX Server Documentation</a></li>' +
|
||||
'</ul>' +
|
||||
'<h4>CLI Commands:</h4>' +
|
||||
'<code style="display:block;background:#fff;padding:10px;margin-top:10px;">' +
|
||||
'simplexctl status # Show server status<br>' +
|
||||
'simplexctl get-address # Show server addresses<br>' +
|
||||
'simplexctl logs smp # View SMP logs<br>' +
|
||||
'simplexctl shell # Access container shell' +
|
||||
'</code>' +
|
||||
'</div>';
|
||||
};
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,187 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
SIMPLEX_DIR="/srv/simplex"
|
||||
SMP_DIR="$SIMPLEX_DIR/smp"
|
||||
XFTP_DIR="$SIMPLEX_DIR/xftp"
|
||||
|
||||
case "$1" in
|
||||
list)
|
||||
echo '{"status":{},"start":{},"stop":{},"restart":{},"install":{},"get_addresses":{},"init_certs":{"hostname":"str"},"logs":{"service":"str","lines":"int"},"get_stats":{}}'
|
||||
;;
|
||||
call)
|
||||
case "$2" in
|
||||
status)
|
||||
json_init
|
||||
|
||||
# Get configuration
|
||||
enabled=$(uci -q get simplex.main.enabled)
|
||||
smp_enabled=$(uci -q get simplex.smp.enabled)
|
||||
xftp_enabled=$(uci -q get simplex.xftp.enabled)
|
||||
smp_hostname=$(uci -q get simplex.smp.hostname)
|
||||
xftp_hostname=$(uci -q get simplex.xftp.hostname)
|
||||
smp_port=$(uci -q get simplex.smp.port || echo "5223")
|
||||
xftp_port=$(uci -q get simplex.xftp.port || echo "443")
|
||||
|
||||
json_add_boolean "enabled" ${enabled:-0}
|
||||
json_add_boolean "smp_enabled" ${smp_enabled:-1}
|
||||
json_add_boolean "xftp_enabled" ${xftp_enabled:-1}
|
||||
json_add_string "smp_hostname" "$smp_hostname"
|
||||
json_add_string "xftp_hostname" "$xftp_hostname"
|
||||
json_add_int "smp_port" $smp_port
|
||||
json_add_int "xftp_port" $xftp_port
|
||||
|
||||
# Check LXC
|
||||
if command -v lxc-info >/dev/null 2>&1; then
|
||||
json_add_boolean "lxc_available" 1
|
||||
|
||||
# Check container status
|
||||
if lxc-info -n simplex >/dev/null 2>&1; then
|
||||
json_add_boolean "container_exists" 1
|
||||
if lxc-info -n simplex -s 2>/dev/null | grep -q "RUNNING"; then
|
||||
json_add_string "container_status" "running"
|
||||
else
|
||||
json_add_string "container_status" "stopped"
|
||||
fi
|
||||
else
|
||||
json_add_boolean "container_exists" 0
|
||||
json_add_string "container_status" "not_installed"
|
||||
fi
|
||||
else
|
||||
json_add_boolean "lxc_available" 0
|
||||
json_add_boolean "container_exists" 0
|
||||
json_add_string "container_status" "lxc_not_installed"
|
||||
fi
|
||||
|
||||
# Check server processes
|
||||
if pgrep smp-server >/dev/null 2>&1; then
|
||||
json_add_string "smp_status" "running"
|
||||
else
|
||||
json_add_string "smp_status" "stopped"
|
||||
fi
|
||||
|
||||
if pgrep xftp-server >/dev/null 2>&1; then
|
||||
json_add_string "xftp_status" "running"
|
||||
else
|
||||
json_add_string "xftp_status" "stopped"
|
||||
fi
|
||||
|
||||
# Check binaries exist
|
||||
if [ -x "$SIMPLEX_DIR/bin/smp-server" ]; then
|
||||
json_add_boolean "smp_binary_exists" 1
|
||||
else
|
||||
json_add_boolean "smp_binary_exists" 0
|
||||
fi
|
||||
|
||||
if [ -x "$SIMPLEX_DIR/bin/xftp-server" ]; then
|
||||
json_add_boolean "xftp_binary_exists" 1
|
||||
else
|
||||
json_add_boolean "xftp_binary_exists" 0
|
||||
fi
|
||||
|
||||
json_dump
|
||||
;;
|
||||
|
||||
start)
|
||||
/etc/init.d/simplex start >/dev/null 2>&1
|
||||
echo '{"success":true}'
|
||||
;;
|
||||
|
||||
stop)
|
||||
/etc/init.d/simplex stop >/dev/null 2>&1
|
||||
echo '{"success":true}'
|
||||
;;
|
||||
|
||||
restart)
|
||||
/etc/init.d/simplex restart >/dev/null 2>&1
|
||||
echo '{"success":true}'
|
||||
;;
|
||||
|
||||
install)
|
||||
output=$(/usr/sbin/simplexctl install 2>&1)
|
||||
code=$?
|
||||
json_init
|
||||
json_add_boolean "success" $((code == 0))
|
||||
json_add_string "output" "$output"
|
||||
json_dump
|
||||
;;
|
||||
|
||||
get_addresses)
|
||||
json_init
|
||||
|
||||
# SMP address
|
||||
smp_enabled=$(uci -q get simplex.smp.enabled)
|
||||
if [ "$smp_enabled" = "1" ] && [ -f "$SMP_DIR/fingerprint" ]; then
|
||||
smp_fp=$(cat "$SMP_DIR/fingerprint" 2>/dev/null)
|
||||
smp_host=$(uci -q get simplex.smp.hostname)
|
||||
smp_port=$(uci -q get simplex.smp.port || echo "5223")
|
||||
[ -n "$smp_fp" ] && [ -n "$smp_host" ] && \
|
||||
json_add_string "smp_address" "smp://${smp_fp}@${smp_host}:${smp_port}"
|
||||
json_add_string "smp_fingerprint" "$smp_fp"
|
||||
fi
|
||||
|
||||
# XFTP address
|
||||
xftp_enabled=$(uci -q get simplex.xftp.enabled)
|
||||
if [ "$xftp_enabled" = "1" ] && [ -f "$XFTP_DIR/fingerprint" ]; then
|
||||
xftp_fp=$(cat "$XFTP_DIR/fingerprint" 2>/dev/null)
|
||||
xftp_host=$(uci -q get simplex.xftp.hostname)
|
||||
xftp_port=$(uci -q get simplex.xftp.port || echo "443")
|
||||
[ -n "$xftp_fp" ] && [ -n "$xftp_host" ] && \
|
||||
json_add_string "xftp_address" "xftp://${xftp_fp}@${xftp_host}:${xftp_port}"
|
||||
json_add_string "xftp_fingerprint" "$xftp_fp"
|
||||
fi
|
||||
|
||||
json_dump
|
||||
;;
|
||||
|
||||
init_certs)
|
||||
read -r input
|
||||
hostname=$(echo "$input" | jsonfilter -e '@.hostname' 2>/dev/null)
|
||||
output=$(/usr/sbin/simplexctl init-certs "$hostname" 2>&1)
|
||||
code=$?
|
||||
json_init
|
||||
json_add_boolean "success" $((code == 0))
|
||||
json_add_string "output" "$output"
|
||||
json_dump
|
||||
;;
|
||||
|
||||
logs)
|
||||
read -r input
|
||||
service=$(echo "$input" | jsonfilter -e '@.service' 2>/dev/null)
|
||||
lines=$(echo "$input" | jsonfilter -e '@.lines' 2>/dev/null)
|
||||
[ -z "$lines" ] && lines=50
|
||||
|
||||
logs=$(/usr/sbin/simplexctl logs "$service" "$lines" 2>&1 | tail -100)
|
||||
json_init
|
||||
json_add_string "logs" "$logs"
|
||||
json_dump
|
||||
;;
|
||||
|
||||
get_stats)
|
||||
json_init
|
||||
|
||||
# Storage stats
|
||||
if [ -d "$XFTP_DIR/files" ]; then
|
||||
storage_used=$(du -sb "$XFTP_DIR/files" 2>/dev/null | cut -f1)
|
||||
file_count=$(find "$XFTP_DIR/files" -type f 2>/dev/null | wc -l)
|
||||
json_add_int "storage_used" ${storage_used:-0}
|
||||
json_add_int "file_count" ${file_count:-0}
|
||||
else
|
||||
json_add_int "storage_used" 0
|
||||
json_add_int "file_count" 0
|
||||
fi
|
||||
|
||||
# Parse storage quota
|
||||
quota=$(uci -q get simplex.xftp.storage_quota || echo "10G")
|
||||
quota_bytes=$(echo "$quota" | sed -e 's/G/*1024*1024*1024/' -e 's/M/*1024*1024/' -e 's/K/*1024/' | bc 2>/dev/null || echo "10737418240")
|
||||
json_add_int "storage_quota" $quota_bytes
|
||||
|
||||
json_dump
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"admin/services/simplex": {
|
||||
"title": "SimpleX Chat",
|
||||
"order": 65,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "simplex/overview"
|
||||
},
|
||||
"depends": {
|
||||
"acl": ["luci-app-simplex"],
|
||||
"uci": {"simplex": true}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
{
|
||||
"luci-app-simplex": {
|
||||
"description": "Grant access to SimpleX Chat Server configuration",
|
||||
"read": {
|
||||
"file": {
|
||||
"/etc/config/simplex": ["read"],
|
||||
"/srv/simplex/smp/fingerprint": ["read"],
|
||||
"/srv/simplex/xftp/fingerprint": ["read"]
|
||||
},
|
||||
"ubus": {
|
||||
"file": ["read", "stat"],
|
||||
"luci.simplex": ["*"]
|
||||
},
|
||||
"uci": ["simplex"]
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/etc/config/simplex": ["write"]
|
||||
},
|
||||
"ubus": {
|
||||
"luci.simplex": ["*"]
|
||||
},
|
||||
"uci": ["simplex"]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,12 +1,12 @@
|
||||
{
|
||||
"feed_url": "/secubox-feed",
|
||||
"generated": "2026-02-02T11:00:39+01:00",
|
||||
"generated": "2026-02-02T12:51:55+01:00",
|
||||
"packages": [
|
||||
{
|
||||
"name": "luci-app-auth-guardian",
|
||||
"version": "0.4.0-r3",
|
||||
"filename": "luci-app-auth-guardian_0.4.0-r3_all.ipk",
|
||||
"size": 11737,
|
||||
"size": 11736,
|
||||
"category": "security",
|
||||
"icon": "key",
|
||||
"description": "Authentication management",
|
||||
@ -18,7 +18,7 @@
|
||||
"name": "luci-app-bandwidth-manager",
|
||||
"version": "0.5.0-r2",
|
||||
"filename": "luci-app-bandwidth-manager_0.5.0-r2_all.ipk",
|
||||
"size": 61540,
|
||||
"size": 61538,
|
||||
"category": "network",
|
||||
"icon": "activity",
|
||||
"description": "Bandwidth monitoring and control",
|
||||
@ -30,7 +30,7 @@
|
||||
"name": "luci-app-cdn-cache",
|
||||
"version": "0.5.0-r3",
|
||||
"filename": "luci-app-cdn-cache_0.5.0-r3_all.ipk",
|
||||
"size": 23184,
|
||||
"size": 23183,
|
||||
"category": "network",
|
||||
"icon": "globe",
|
||||
"description": "CDN caching",
|
||||
@ -42,7 +42,7 @@
|
||||
"name": "luci-app-client-guardian",
|
||||
"version": "0.4.0-r7",
|
||||
"filename": "luci-app-client-guardian_0.4.0-r7_all.ipk",
|
||||
"size": 54534,
|
||||
"size": 54537,
|
||||
"category": "network",
|
||||
"icon": "users",
|
||||
"description": "Client management and monitoring",
|
||||
@ -54,7 +54,7 @@
|
||||
"name": "luci-app-crowdsec-dashboard",
|
||||
"version": "0.7.0-r32",
|
||||
"filename": "luci-app-crowdsec-dashboard_0.7.0-r32_all.ipk",
|
||||
"size": 32630,
|
||||
"size": 32627,
|
||||
"category": "security",
|
||||
"icon": "shield",
|
||||
"description": "CrowdSec security monitoring",
|
||||
@ -66,7 +66,7 @@
|
||||
"name": "luci-app-cyberfeed",
|
||||
"version": "0.1.1-r1",
|
||||
"filename": "luci-app-cyberfeed_0.1.1-r1_all.ipk",
|
||||
"size": 12838,
|
||||
"size": 12839,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -78,7 +78,7 @@
|
||||
"name": "luci-app-dnsguard",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-dnsguard_1.0.0-r1_all.ipk",
|
||||
"size": 7546,
|
||||
"size": 7551,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -90,7 +90,7 @@
|
||||
"name": "luci-app-exposure",
|
||||
"version": "1.0.0-r3",
|
||||
"filename": "luci-app-exposure_1.0.0-r3_all.ipk",
|
||||
"size": 20536,
|
||||
"size": 20533,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -114,7 +114,7 @@
|
||||
"name": "luci-app-glances",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "luci-app-glances_1.0.0-r2_all.ipk",
|
||||
"size": 6962,
|
||||
"size": 6968,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -126,7 +126,7 @@
|
||||
"name": "luci-app-haproxy",
|
||||
"version": "1.0.0-r8",
|
||||
"filename": "luci-app-haproxy_1.0.0-r8_all.ipk",
|
||||
"size": 34557,
|
||||
"size": 34561,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -138,7 +138,7 @@
|
||||
"name": "luci-app-hexojs",
|
||||
"version": "1.0.0-r3",
|
||||
"filename": "luci-app-hexojs_1.0.0-r3_all.ipk",
|
||||
"size": 30303,
|
||||
"size": 30308,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -150,7 +150,7 @@
|
||||
"name": "luci-app-jitsi",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-jitsi_1.0.0-r1_all.ipk",
|
||||
"size": 5139,
|
||||
"size": 5141,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -162,7 +162,7 @@
|
||||
"name": "luci-app-ksm-manager",
|
||||
"version": "0.4.0-r2",
|
||||
"filename": "luci-app-ksm-manager_0.4.0-r2_all.ipk",
|
||||
"size": 18718,
|
||||
"size": 18724,
|
||||
"category": "system",
|
||||
"icon": "cpu",
|
||||
"description": "Kernel memory management",
|
||||
@ -174,7 +174,7 @@
|
||||
"name": "luci-app-localai",
|
||||
"version": "0.1.0-r15",
|
||||
"filename": "luci-app-localai_0.1.0-r15_all.ipk",
|
||||
"size": 13181,
|
||||
"size": 13183,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -198,7 +198,7 @@
|
||||
"name": "luci-app-magicmirror2",
|
||||
"version": "0.4.0-r6",
|
||||
"filename": "luci-app-magicmirror2_0.4.0-r6_all.ipk",
|
||||
"size": 12277,
|
||||
"size": 12279,
|
||||
"category": "iot",
|
||||
"icon": "monitor",
|
||||
"description": "Smart mirror display",
|
||||
@ -210,7 +210,7 @@
|
||||
"name": "luci-app-mailinabox",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-mailinabox_1.0.0-r1_all.ipk",
|
||||
"size": 5484,
|
||||
"size": 5481,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -222,7 +222,7 @@
|
||||
"name": "luci-app-media-flow",
|
||||
"version": "0.6.4-r1",
|
||||
"filename": "luci-app-media-flow_0.6.4-r1_all.ipk",
|
||||
"size": 25414,
|
||||
"size": 25420,
|
||||
"category": "media",
|
||||
"icon": "film",
|
||||
"description": "Media streaming",
|
||||
@ -234,7 +234,7 @@
|
||||
"name": "luci-app-metablogizer",
|
||||
"version": "1.0.0-r5",
|
||||
"filename": "luci-app-metablogizer_1.0.0-r5_all.ipk",
|
||||
"size": 23343,
|
||||
"size": 23348,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -246,7 +246,7 @@
|
||||
"name": "luci-app-metabolizer",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "luci-app-metabolizer_1.0.0-r2_all.ipk",
|
||||
"size": 4759,
|
||||
"size": 4757,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -258,7 +258,7 @@
|
||||
"name": "luci-app-mitmproxy",
|
||||
"version": "0.5.0-r2",
|
||||
"filename": "luci-app-mitmproxy_0.5.0-r2_all.ipk",
|
||||
"size": 10520,
|
||||
"size": 10527,
|
||||
"category": "security",
|
||||
"icon": "lock",
|
||||
"description": "HTTPS proxy and traffic inspection",
|
||||
@ -270,7 +270,7 @@
|
||||
"name": "luci-app-mmpm",
|
||||
"version": "0.2.0-r3",
|
||||
"filename": "luci-app-mmpm_0.2.0-r3_all.ipk",
|
||||
"size": 7901,
|
||||
"size": 7904,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -282,7 +282,7 @@
|
||||
"name": "luci-app-mqtt-bridge",
|
||||
"version": "0.4.0-r4",
|
||||
"filename": "luci-app-mqtt-bridge_0.4.0-r4_all.ipk",
|
||||
"size": 22775,
|
||||
"size": 22778,
|
||||
"category": "iot",
|
||||
"icon": "message-square",
|
||||
"description": "MQTT bridge",
|
||||
@ -294,7 +294,7 @@
|
||||
"name": "luci-app-ndpid",
|
||||
"version": "1.1.2-r2",
|
||||
"filename": "luci-app-ndpid_1.1.2-r2_all.ipk",
|
||||
"size": 22651,
|
||||
"size": 22652,
|
||||
"category": "security",
|
||||
"icon": "eye",
|
||||
"description": "Deep packet inspection",
|
||||
@ -306,7 +306,7 @@
|
||||
"name": "luci-app-netdata-dashboard",
|
||||
"version": "0.5.0-r2",
|
||||
"filename": "luci-app-netdata-dashboard_0.5.0-r2_all.ipk",
|
||||
"size": 20483,
|
||||
"size": 20490,
|
||||
"category": "monitoring",
|
||||
"icon": "bar-chart-2",
|
||||
"description": "System monitoring dashboard",
|
||||
@ -318,7 +318,7 @@
|
||||
"name": "luci-app-network-modes",
|
||||
"version": "0.5.0-r3",
|
||||
"filename": "luci-app-network-modes_0.5.0-r3_all.ipk",
|
||||
"size": 54147,
|
||||
"size": 54150,
|
||||
"category": "network",
|
||||
"icon": "wifi",
|
||||
"description": "Network configuration",
|
||||
@ -330,7 +330,7 @@
|
||||
"name": "luci-app-network-tweaks",
|
||||
"version": "1.0.0-r7",
|
||||
"filename": "luci-app-network-tweaks_1.0.0-r7_all.ipk",
|
||||
"size": 14955,
|
||||
"size": 14966,
|
||||
"category": "network",
|
||||
"icon": "wifi",
|
||||
"description": "Network configuration",
|
||||
@ -342,7 +342,7 @@
|
||||
"name": "luci-app-nextcloud",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-nextcloud_1.0.0-r1_all.ipk",
|
||||
"size": 6486,
|
||||
"size": 6487,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -354,7 +354,7 @@
|
||||
"name": "luci-app-ollama",
|
||||
"version": "0.1.0-r1",
|
||||
"filename": "luci-app-ollama_0.1.0-r1_all.ipk",
|
||||
"size": 12349,
|
||||
"size": 12351,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -366,7 +366,7 @@
|
||||
"name": "luci-app-picobrew",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-picobrew_1.0.0-r1_all.ipk",
|
||||
"size": 9455,
|
||||
"size": 9456,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -378,7 +378,7 @@
|
||||
"name": "luci-app-secubox",
|
||||
"version": "0.7.1-r4",
|
||||
"filename": "luci-app-secubox_0.7.1-r4_all.ipk",
|
||||
"size": 77677,
|
||||
"size": 77680,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -390,7 +390,7 @@
|
||||
"name": "luci-app-secubox-admin",
|
||||
"version": "1.0.0-r19",
|
||||
"filename": "luci-app-secubox-admin_1.0.0-r19_all.ipk",
|
||||
"size": 57246,
|
||||
"size": 57247,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -402,7 +402,7 @@
|
||||
"name": "luci-app-secubox-crowdsec",
|
||||
"version": "1.0.0-r3",
|
||||
"filename": "luci-app-secubox-crowdsec_1.0.0-r3_all.ipk",
|
||||
"size": 13914,
|
||||
"size": 13922,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -414,7 +414,7 @@
|
||||
"name": "luci-app-secubox-netdiag",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-secubox-netdiag_1.0.0-r1_all.ipk",
|
||||
"size": 15307,
|
||||
"size": 15306,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -426,7 +426,7 @@
|
||||
"name": "luci-app-secubox-netifyd",
|
||||
"version": "1.2.1-r1",
|
||||
"filename": "luci-app-secubox-netifyd_1.2.1-r1_all.ipk",
|
||||
"size": 36538,
|
||||
"size": 36545,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -450,7 +450,7 @@
|
||||
"name": "luci-app-secubox-portal",
|
||||
"version": "0.7.0-r2",
|
||||
"filename": "luci-app-secubox-portal_0.7.0-r2_all.ipk",
|
||||
"size": 24643,
|
||||
"size": 24644,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -462,7 +462,7 @@
|
||||
"name": "luci-app-secubox-security-threats",
|
||||
"version": "1.0.0-r4",
|
||||
"filename": "luci-app-secubox-security-threats_1.0.0-r4_all.ipk",
|
||||
"size": 21311,
|
||||
"size": 21314,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox system component",
|
||||
@ -474,7 +474,19 @@
|
||||
"name": "luci-app-service-registry",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-service-registry_1.0.0-r1_all.ipk",
|
||||
"size": 39824,
|
||||
"size": 39826,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
"installed": false,
|
||||
"luci_app": null
|
||||
}
|
||||
,
|
||||
{
|
||||
"name": "luci-app-simplex",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "luci-app-simplex_1.0.0-r1_all.ipk",
|
||||
"size": 6999,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -486,7 +498,7 @@
|
||||
"name": "luci-app-streamlit",
|
||||
"version": "1.0.0-r11",
|
||||
"filename": "luci-app-streamlit_1.0.0-r11_all.ipk",
|
||||
"size": 14747,
|
||||
"size": 14750,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -498,7 +510,7 @@
|
||||
"name": "luci-app-system-hub",
|
||||
"version": "0.5.1-r4",
|
||||
"filename": "luci-app-system-hub_0.5.1-r4_all.ipk",
|
||||
"size": 61102,
|
||||
"size": 61103,
|
||||
"category": "system",
|
||||
"icon": "settings",
|
||||
"description": "System management",
|
||||
@ -510,7 +522,7 @@
|
||||
"name": "luci-app-tor-shield",
|
||||
"version": "1.0.0-r10",
|
||||
"filename": "luci-app-tor-shield_1.0.0-r10_all.ipk",
|
||||
"size": 22360,
|
||||
"size": 22363,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -522,7 +534,7 @@
|
||||
"name": "luci-app-traffic-shaper",
|
||||
"version": "0.4.0-r2",
|
||||
"filename": "luci-app-traffic-shaper_0.4.0-r2_all.ipk",
|
||||
"size": 14532,
|
||||
"size": 14533,
|
||||
"category": "network",
|
||||
"icon": "filter",
|
||||
"description": "Traffic shaping and QoS",
|
||||
@ -534,7 +546,7 @@
|
||||
"name": "luci-app-vhost-manager",
|
||||
"version": "0.5.0-r5",
|
||||
"filename": "luci-app-vhost-manager_0.5.0-r5_all.ipk",
|
||||
"size": 26184,
|
||||
"size": 26186,
|
||||
"category": "network",
|
||||
"icon": "server",
|
||||
"description": "Virtual host management",
|
||||
@ -546,7 +558,7 @@
|
||||
"name": "luci-app-wireguard-dashboard",
|
||||
"version": "0.7.0-r5",
|
||||
"filename": "luci-app-wireguard-dashboard_0.7.0-r5_all.ipk",
|
||||
"size": 39604,
|
||||
"size": 39608,
|
||||
"category": "vpn",
|
||||
"icon": "shield",
|
||||
"description": "WireGuard VPN dashboard",
|
||||
@ -558,7 +570,7 @@
|
||||
"name": "luci-app-zigbee2mqtt",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "luci-app-zigbee2mqtt_1.0.0-r2_all.ipk",
|
||||
"size": 6810,
|
||||
"size": 6816,
|
||||
"category": "iot",
|
||||
"icon": "radio",
|
||||
"description": "Zigbee device management",
|
||||
@ -570,7 +582,7 @@
|
||||
"name": "luci-theme-secubox",
|
||||
"version": "0.4.7-r1",
|
||||
"filename": "luci-theme-secubox_0.4.7-r1_all.ipk",
|
||||
"size": 110239,
|
||||
"size": 110242,
|
||||
"category": "theme",
|
||||
"icon": "palette",
|
||||
"description": "LuCI theme",
|
||||
@ -582,7 +594,7 @@
|
||||
"name": "secubox-app",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "secubox-app_1.0.0-r2_all.ipk",
|
||||
"size": 11182,
|
||||
"size": 11186,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
@ -594,7 +606,7 @@
|
||||
"name": "secubox-app-adguardhome",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "secubox-app-adguardhome_1.0.0-r2_all.ipk",
|
||||
"size": 2877,
|
||||
"size": 2884,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -618,7 +630,7 @@
|
||||
"name": "secubox-app-crowdsec-custom",
|
||||
"version": "1.1.0-r1",
|
||||
"filename": "secubox-app-crowdsec-custom_1.1.0-r1_all.ipk",
|
||||
"size": 5761,
|
||||
"size": 5762,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -630,7 +642,7 @@
|
||||
"name": "secubox-app-cs-firewall-bouncer",
|
||||
"version": "0.0.31-r4_aarch64",
|
||||
"filename": "secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk",
|
||||
"size": 5049325,
|
||||
"size": 5049323,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -642,7 +654,7 @@
|
||||
"name": "secubox-app-cyberfeed",
|
||||
"version": "0.2.1-r1",
|
||||
"filename": "secubox-app-cyberfeed_0.2.1-r1_all.ipk",
|
||||
"size": 12446,
|
||||
"size": 12452,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -654,7 +666,7 @@
|
||||
"name": "secubox-app-domoticz",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "secubox-app-domoticz_1.0.0-r2_all.ipk",
|
||||
"size": 2546,
|
||||
"size": 2547,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -666,7 +678,7 @@
|
||||
"name": "secubox-app-exposure",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "secubox-app-exposure_1.0.0-r1_all.ipk",
|
||||
"size": 6933,
|
||||
"size": 6936,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -678,7 +690,7 @@
|
||||
"name": "secubox-app-gitea",
|
||||
"version": "1.0.0-r5",
|
||||
"filename": "secubox-app-gitea_1.0.0-r5_all.ipk",
|
||||
"size": 9400,
|
||||
"size": 9407,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -690,7 +702,7 @@
|
||||
"name": "secubox-app-glances",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "secubox-app-glances_1.0.0-r1_all.ipk",
|
||||
"size": 5535,
|
||||
"size": 5536,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -702,7 +714,7 @@
|
||||
"name": "secubox-app-haproxy",
|
||||
"version": "1.0.0-r23",
|
||||
"filename": "secubox-app-haproxy_1.0.0-r23_all.ipk",
|
||||
"size": 15676,
|
||||
"size": 15682,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -714,7 +726,7 @@
|
||||
"name": "secubox-app-hexojs",
|
||||
"version": "1.0.0-r8",
|
||||
"filename": "secubox-app-hexojs_1.0.0-r8_all.ipk",
|
||||
"size": 94926,
|
||||
"size": 94939,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -726,7 +738,7 @@
|
||||
"name": "secubox-app-jitsi",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "secubox-app-jitsi_1.0.0-r1_all.ipk",
|
||||
"size": 8906,
|
||||
"size": 8911,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -738,7 +750,7 @@
|
||||
"name": "secubox-app-localai",
|
||||
"version": "2.25.0-r1",
|
||||
"filename": "secubox-app-localai_2.25.0-r1_all.ipk",
|
||||
"size": 5707,
|
||||
"size": 5724,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -750,7 +762,7 @@
|
||||
"name": "secubox-app-localai-wb",
|
||||
"version": "2.25.0-r1",
|
||||
"filename": "secubox-app-localai-wb_2.25.0-r1_all.ipk",
|
||||
"size": 7946,
|
||||
"size": 7953,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -762,7 +774,7 @@
|
||||
"name": "secubox-app-lyrion",
|
||||
"version": "2.0.2-r1",
|
||||
"filename": "secubox-app-lyrion_2.0.2-r1_all.ipk",
|
||||
"size": 7283,
|
||||
"size": 7287,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -774,7 +786,7 @@
|
||||
"name": "secubox-app-magicmirror2",
|
||||
"version": "0.4.0-r8",
|
||||
"filename": "secubox-app-magicmirror2_0.4.0-r8_all.ipk",
|
||||
"size": 9245,
|
||||
"size": 9254,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -786,7 +798,7 @@
|
||||
"name": "secubox-app-mailinabox",
|
||||
"version": "2.0.0-r1",
|
||||
"filename": "secubox-app-mailinabox_2.0.0-r1_all.ipk",
|
||||
"size": 7563,
|
||||
"size": 7572,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -798,7 +810,7 @@
|
||||
"name": "secubox-app-metabolizer",
|
||||
"version": "1.0.0-r3",
|
||||
"filename": "secubox-app-metabolizer_1.0.0-r3_all.ipk",
|
||||
"size": 13971,
|
||||
"size": 13979,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -810,7 +822,7 @@
|
||||
"name": "secubox-app-mitmproxy",
|
||||
"version": "0.5.0-r19",
|
||||
"filename": "secubox-app-mitmproxy_0.5.0-r19_all.ipk",
|
||||
"size": 22950,
|
||||
"size": 22956,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -834,7 +846,7 @@
|
||||
"name": "secubox-app-nextcloud",
|
||||
"version": "1.0.0-r2",
|
||||
"filename": "secubox-app-nextcloud_1.0.0-r2_all.ipk",
|
||||
"size": 2943,
|
||||
"size": 2963,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -846,7 +858,7 @@
|
||||
"name": "secubox-app-ollama",
|
||||
"version": "0.1.0-r1",
|
||||
"filename": "secubox-app-ollama_0.1.0-r1_all.ipk",
|
||||
"size": 5731,
|
||||
"size": 5739,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -858,19 +870,31 @@
|
||||
"name": "secubox-app-picobrew",
|
||||
"version": "1.0.0-r7",
|
||||
"filename": "secubox-app-picobrew_1.0.0-r7_all.ipk",
|
||||
"size": 5537,
|
||||
"size": 5539,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
"installed": false,
|
||||
"luci_app": "luci-app-picobrew"
|
||||
}
|
||||
,
|
||||
{
|
||||
"name": "secubox-app-simplex",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "secubox-app-simplex_1.0.0-r1_all.ipk",
|
||||
"size": 9235,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
"installed": false,
|
||||
"luci_app": "luci-app-simplex"
|
||||
}
|
||||
,
|
||||
{
|
||||
"name": "secubox-app-streamlit",
|
||||
"version": "1.0.0-r5",
|
||||
"filename": "secubox-app-streamlit_1.0.0-r5_all.ipk",
|
||||
"size": 11716,
|
||||
"size": 11725,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -882,7 +906,7 @@
|
||||
"name": "secubox-app-tor",
|
||||
"version": "1.0.0-r1",
|
||||
"filename": "secubox-app-tor_1.0.0-r1_all.ipk",
|
||||
"size": 7360,
|
||||
"size": 7369,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -894,7 +918,7 @@
|
||||
"name": "secubox-app-webapp",
|
||||
"version": "1.5.0-r7",
|
||||
"filename": "secubox-app-webapp_1.5.0-r7_all.ipk",
|
||||
"size": 39169,
|
||||
"size": 39175,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -906,7 +930,7 @@
|
||||
"name": "secubox-app-zigbee2mqtt",
|
||||
"version": "1.0.0-r3",
|
||||
"filename": "secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk",
|
||||
"size": 3537,
|
||||
"size": 3542,
|
||||
"category": "secubox",
|
||||
"icon": "package",
|
||||
"description": "SecuBox backend service",
|
||||
@ -918,7 +942,7 @@
|
||||
"name": "secubox-core",
|
||||
"version": "0.10.0-r11",
|
||||
"filename": "secubox-core_0.10.0-r11_all.ipk",
|
||||
"size": 87971,
|
||||
"size": 87975,
|
||||
"category": "system",
|
||||
"icon": "box",
|
||||
"description": "SecuBox core components",
|
||||
@ -930,7 +954,7 @@
|
||||
"name": "secubox-p2p",
|
||||
"version": "0.6.0-r3",
|
||||
"filename": "secubox-p2p_0.6.0-r3_all.ipk",
|
||||
"size": 42010,
|
||||
"size": 42016,
|
||||
"category": "utility",
|
||||
"icon": "package",
|
||||
"description": "SecuBox package",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
83
package/secubox/secubox-app-simplex/Makefile
Normal file
83
package/secubox/secubox-app-simplex/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-simplex
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_ARCH:=all
|
||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/secubox-app-simplex
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
PKGARCH:=all
|
||||
SUBMENU:=SecuBox Apps
|
||||
TITLE:=SecuBox SimpleX Chat Server
|
||||
DEPENDS:=+lxc +lxc-common +wget +openssl-util +tar
|
||||
endef
|
||||
|
||||
define Package/secubox-app-simplex/description
|
||||
SimpleX Chat self-hosted messaging infrastructure for SecuBox.
|
||||
|
||||
Features:
|
||||
- SMP Server (Simple Messaging Protocol) for message relay
|
||||
- XFTP Server for encrypted file sharing
|
||||
- No user identifiers or metadata collection
|
||||
- End-to-end encryption with post-quantum algorithms
|
||||
- Runs in lightweight Alpine Linux LXC container
|
||||
- Automatic TLS certificate generation
|
||||
- HAProxy integration for SSL termination
|
||||
|
||||
Privacy-first messaging relay that you control.
|
||||
Configure in /etc/config/simplex.
|
||||
endef
|
||||
|
||||
define Package/secubox-app-simplex/conffiles
|
||||
/etc/config/simplex
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/secubox-app-simplex/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/etc/config/simplex $(1)/etc/config/simplex
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/etc/init.d/simplex $(1)/etc/init.d/simplex
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/simplexctl $(1)/usr/sbin/simplexctl
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/secubox/haproxy.d
|
||||
$(INSTALL_DATA) ./files/usr/lib/secubox/haproxy.d/simplex.cfg $(1)/usr/lib/secubox/haproxy.d/
|
||||
endef
|
||||
|
||||
define Package/secubox-app-simplex/postinst
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " SimpleX Chat Server Installed"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo "Quick Start:"
|
||||
echo " 1. Install container: simplexctl install"
|
||||
echo " 2. Set hostname: uci set simplex.smp.hostname='smp.example.com'"
|
||||
echo " 3. Commit: uci commit simplex"
|
||||
echo " 4. Start: /etc/init.d/simplex start"
|
||||
echo ""
|
||||
echo "Control commands:"
|
||||
echo " simplexctl status - Show service status"
|
||||
echo " simplexctl get-address - Get server addresses for clients"
|
||||
echo " simplexctl logs - View server logs"
|
||||
echo ""
|
||||
echo "Ports: SMP=5223, XFTP=443"
|
||||
echo ""
|
||||
}
|
||||
exit 0
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,secubox-app-simplex))
|
||||
32
package/secubox/secubox-app-simplex/files/etc/config/simplex
Normal file
32
package/secubox/secubox-app-simplex/files/etc/config/simplex
Normal file
@ -0,0 +1,32 @@
|
||||
# SimpleX Chat Server Configuration
|
||||
|
||||
config simplex 'main'
|
||||
option enabled '0'
|
||||
option data_path '/srv/simplex'
|
||||
option memory_limit '256M'
|
||||
option container_type 'lxc'
|
||||
|
||||
config smp 'smp'
|
||||
option enabled '1'
|
||||
option port '5223'
|
||||
option control_port '5224'
|
||||
option hostname ''
|
||||
option store_log '1'
|
||||
option daily_stats '1'
|
||||
option log_stats '0'
|
||||
option queue_password ''
|
||||
|
||||
config xftp 'xftp'
|
||||
option enabled '1'
|
||||
option port '443'
|
||||
option control_port '5225'
|
||||
option hostname ''
|
||||
option storage_quota '10G'
|
||||
option file_expiry '48h'
|
||||
option create_password ''
|
||||
|
||||
config tls 'tls'
|
||||
option use_letsencrypt '0'
|
||||
option cert_path '/srv/simplex/certs'
|
||||
option domain ''
|
||||
option email ''
|
||||
77
package/secubox/secubox-app-simplex/files/etc/init.d/simplex
Normal file
77
package/secubox/secubox-app-simplex/files/etc/init.d/simplex
Normal file
@ -0,0 +1,77 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# SimpleX Chat Server Service
|
||||
|
||||
START=95
|
||||
STOP=15
|
||||
USE_PROCD=1
|
||||
|
||||
SIMPLEX_DIR="/srv/simplex"
|
||||
CONTAINER_NAME="simplex"
|
||||
|
||||
start_service() {
|
||||
local enabled=$(uci -q get simplex.main.enabled)
|
||||
[ "$enabled" != "1" ] && return 0
|
||||
|
||||
# Check LXC
|
||||
if ! command -v lxc-start >/dev/null 2>&1; then
|
||||
logger -t simplex "LXC not available"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if container exists
|
||||
if ! lxc-info -n "$CONTAINER_NAME" >/dev/null 2>&1; then
|
||||
logger -t simplex "Container not installed. Run: simplexctl install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Start container if not running
|
||||
if ! lxc-info -n "$CONTAINER_NAME" -s 2>/dev/null | grep -q "RUNNING"; then
|
||||
logger -t simplex "Starting SimpleX container..."
|
||||
/usr/sbin/simplexctl start-container
|
||||
fi
|
||||
|
||||
# Start SMP server
|
||||
local smp_enabled=$(uci -q get simplex.smp.enabled)
|
||||
if [ "$smp_enabled" = "1" ]; then
|
||||
procd_open_instance smp
|
||||
procd_set_param command /usr/sbin/simplexctl service-run smp
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
fi
|
||||
|
||||
# Start XFTP server
|
||||
local xftp_enabled=$(uci -q get simplex.xftp.enabled)
|
||||
if [ "$xftp_enabled" = "1" ]; then
|
||||
procd_open_instance xftp
|
||||
procd_set_param command /usr/sbin/simplexctl service-run xftp
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
fi
|
||||
|
||||
logger -t simplex "SimpleX Chat Server started"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
# Stop services (procd handles the instances)
|
||||
/usr/sbin/simplexctl stop-services 2>/dev/null
|
||||
|
||||
# Optionally stop container
|
||||
local keep_container=$(uci -q get simplex.main.keep_container_running)
|
||||
if [ "$keep_container" != "1" ]; then
|
||||
/usr/sbin/simplexctl stop-container 2>/dev/null
|
||||
fi
|
||||
|
||||
logger -t simplex "SimpleX Chat Server stopped"
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
/usr/sbin/simplexctl reload-config
|
||||
}
|
||||
|
||||
status() {
|
||||
/usr/sbin/simplexctl status
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
# SimpleX Chat Server HAProxy Configuration
|
||||
#
|
||||
# This file provides HAProxy configuration snippets for SimpleX servers.
|
||||
# SimpleX uses TLS passthrough (TCP mode) as it handles TLS internally.
|
||||
#
|
||||
# Note: SMP and XFTP servers require direct TLS connections.
|
||||
# HAProxy can be used for:
|
||||
# - TCP load balancing (if running multiple instances)
|
||||
# - Port mapping
|
||||
# - Connection limiting
|
||||
#
|
||||
# For most setups, direct port forwarding is recommended.
|
||||
|
||||
# Example frontend for SMP (if needed):
|
||||
# frontend simplex_smp_frontend
|
||||
# bind *:5223
|
||||
# mode tcp
|
||||
# default_backend simplex_smp_backend
|
||||
#
|
||||
# backend simplex_smp_backend
|
||||
# mode tcp
|
||||
# server simplex-smp 127.0.0.1:15223 check
|
||||
|
||||
# Example frontend for XFTP:
|
||||
# frontend simplex_xftp_frontend
|
||||
# bind *:443
|
||||
# mode tcp
|
||||
# default_backend simplex_xftp_backend
|
||||
#
|
||||
# backend simplex_xftp_backend
|
||||
# mode tcp
|
||||
# server simplex-xftp 127.0.0.1:1443 check
|
||||
985
package/secubox/secubox-app-simplex/files/usr/sbin/simplexctl
Normal file
985
package/secubox/secubox-app-simplex/files/usr/sbin/simplexctl
Normal file
@ -0,0 +1,985 @@
|
||||
#!/bin/sh
|
||||
# SimpleX Chat Server Control Script for SecuBox
|
||||
# Manages SMP and XFTP servers in LXC container
|
||||
|
||||
VERSION="1.0.0"
|
||||
SIMPLEX_DIR="/srv/simplex"
|
||||
CONTAINER_NAME="simplex"
|
||||
CONTAINER_PATH="/var/lib/lxc/$CONTAINER_NAME"
|
||||
SMP_DIR="$SIMPLEX_DIR/smp"
|
||||
XFTP_DIR="$SIMPLEX_DIR/xftp"
|
||||
CERTS_DIR="$SIMPLEX_DIR/certs"
|
||||
|
||||
# SimpleX release information
|
||||
SIMPLEX_VERSION="v6.0.4"
|
||||
SIMPLEX_BASE_URL="https://github.com/simplex-chat/simplexmq/releases/download"
|
||||
|
||||
# Colors for terminal output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[SIMPLEX]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
# ============================================================================
|
||||
# UCI Configuration Helpers
|
||||
# ============================================================================
|
||||
|
||||
uci_get() { uci -q get simplex.$1; }
|
||||
uci_set() { uci -q set simplex.$1="$2" && uci commit simplex; }
|
||||
|
||||
load_config() {
|
||||
# Main settings
|
||||
ENABLED=$(uci_get main.enabled || echo "0")
|
||||
DATA_PATH=$(uci_get main.data_path || echo "$SIMPLEX_DIR")
|
||||
MEMORY_LIMIT=$(uci_get main.memory_limit || echo "256M")
|
||||
|
||||
# SMP settings
|
||||
SMP_ENABLED=$(uci_get smp.enabled || echo "1")
|
||||
SMP_PORT=$(uci_get smp.port || echo "5223")
|
||||
SMP_CONTROL_PORT=$(uci_get smp.control_port || echo "5224")
|
||||
SMP_HOSTNAME=$(uci_get smp.hostname)
|
||||
SMP_STORE_LOG=$(uci_get smp.store_log || echo "1")
|
||||
SMP_QUEUE_PASSWORD=$(uci_get smp.queue_password)
|
||||
|
||||
# XFTP settings
|
||||
XFTP_ENABLED=$(uci_get xftp.enabled || echo "1")
|
||||
XFTP_PORT=$(uci_get xftp.port || echo "443")
|
||||
XFTP_CONTROL_PORT=$(uci_get xftp.control_port || echo "5225")
|
||||
XFTP_HOSTNAME=$(uci_get xftp.hostname)
|
||||
XFTP_STORAGE_QUOTA=$(uci_get xftp.storage_quota || echo "10G")
|
||||
XFTP_FILE_EXPIRY=$(uci_get xftp.file_expiry || echo "48h")
|
||||
XFTP_CREATE_PASSWORD=$(uci_get xftp.create_password)
|
||||
|
||||
# TLS settings
|
||||
USE_LETSENCRYPT=$(uci_get tls.use_letsencrypt || echo "0")
|
||||
CERT_PATH=$(uci_get tls.cert_path || echo "$CERTS_DIR")
|
||||
TLS_DOMAIN=$(uci_get tls.domain)
|
||||
TLS_EMAIL=$(uci_get tls.email)
|
||||
|
||||
# Use hostname from TLS domain if not set specifically
|
||||
[ -z "$SMP_HOSTNAME" ] && SMP_HOSTNAME="$TLS_DOMAIN"
|
||||
[ -z "$XFTP_HOSTNAME" ] && XFTP_HOSTNAME="$TLS_DOMAIN"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Architecture Detection
|
||||
# ============================================================================
|
||||
|
||||
detect_arch() {
|
||||
local machine=$(uname -m)
|
||||
case "$machine" in
|
||||
aarch64|arm64)
|
||||
echo "aarch64"
|
||||
;;
|
||||
x86_64|amd64)
|
||||
echo "x86_64"
|
||||
;;
|
||||
*)
|
||||
error "Unsupported architecture: $machine"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# LXC Container Management
|
||||
# ============================================================================
|
||||
|
||||
container_exists() {
|
||||
lxc-info -n "$CONTAINER_NAME" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
container_running() {
|
||||
lxc-info -n "$CONTAINER_NAME" -s 2>/dev/null | grep -q "RUNNING"
|
||||
}
|
||||
|
||||
create_container() {
|
||||
log "Creating Alpine Linux LXC container..."
|
||||
|
||||
# Create container directory
|
||||
mkdir -p "$CONTAINER_PATH/rootfs"
|
||||
|
||||
# Download Alpine minirootfs
|
||||
local arch=$(detect_arch)
|
||||
local alpine_version="3.19"
|
||||
local alpine_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version}/releases/${arch}/alpine-minirootfs-${alpine_version}.0-${arch}.tar.gz"
|
||||
|
||||
log "Downloading Alpine Linux minirootfs..."
|
||||
if ! wget -q -O /tmp/alpine-rootfs.tar.gz "$alpine_url"; then
|
||||
error "Failed to download Alpine rootfs"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract rootfs
|
||||
tar -xzf /tmp/alpine-rootfs.tar.gz -C "$CONTAINER_PATH/rootfs"
|
||||
rm -f /tmp/alpine-rootfs.tar.gz
|
||||
|
||||
# Create LXC config
|
||||
cat > "$CONTAINER_PATH/config" << EOF
|
||||
# SimpleX Chat LXC Container Configuration
|
||||
|
||||
lxc.uts.name = $CONTAINER_NAME
|
||||
lxc.arch = linux64
|
||||
|
||||
# Rootfs
|
||||
lxc.rootfs.path = dir:$CONTAINER_PATH/rootfs
|
||||
|
||||
# Network - use host networking for direct port access
|
||||
lxc.net.0.type = none
|
||||
|
||||
# Resource limits
|
||||
lxc.cgroup2.memory.max = $MEMORY_LIMIT
|
||||
|
||||
# Mount points for persistent data
|
||||
lxc.mount.entry = $DATA_PATH srv/simplex none bind,create=dir 0 0
|
||||
|
||||
# Capabilities
|
||||
lxc.cap.drop = sys_admin sys_module mac_admin mac_override
|
||||
|
||||
# Console
|
||||
lxc.tty.max = 1
|
||||
lxc.pty.max = 1
|
||||
|
||||
# Auto start
|
||||
lxc.start.auto = 0
|
||||
EOF
|
||||
|
||||
# Setup container init
|
||||
cat > "$CONTAINER_PATH/rootfs/etc/inittab" << 'EOF'
|
||||
::sysinit:/sbin/openrc sysinit
|
||||
::sysinit:/sbin/openrc boot
|
||||
::wait:/sbin/openrc default
|
||||
::ctrlaltdel:/sbin/reboot
|
||||
::shutdown:/sbin/openrc shutdown
|
||||
EOF
|
||||
|
||||
# Configure networking in container
|
||||
cat > "$CONTAINER_PATH/rootfs/etc/resolv.conf" << EOF
|
||||
nameserver 127.0.0.1
|
||||
nameserver 1.1.1.1
|
||||
EOF
|
||||
|
||||
# Install required packages in container
|
||||
cat > "$CONTAINER_PATH/rootfs/tmp/setup.sh" << 'SETUP'
|
||||
#!/bin/sh
|
||||
apk update
|
||||
apk add --no-cache ca-certificates openssl curl
|
||||
mkdir -p /srv/simplex/smp /srv/simplex/xftp /srv/simplex/certs
|
||||
SETUP
|
||||
chmod +x "$CONTAINER_PATH/rootfs/tmp/setup.sh"
|
||||
|
||||
log "Container created successfully"
|
||||
}
|
||||
|
||||
start_container() {
|
||||
if ! container_exists; then
|
||||
error "Container does not exist. Run: simplexctl install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if container_running; then
|
||||
log "Container already running"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Starting container..."
|
||||
lxc-start -n "$CONTAINER_NAME" -d
|
||||
|
||||
# Wait for container to be ready
|
||||
local timeout=30
|
||||
while [ $timeout -gt 0 ]; do
|
||||
if container_running; then
|
||||
log "Container started"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
timeout=$((timeout - 1))
|
||||
done
|
||||
|
||||
error "Container failed to start"
|
||||
return 1
|
||||
}
|
||||
|
||||
stop_container() {
|
||||
if container_running; then
|
||||
log "Stopping container..."
|
||||
lxc-stop -n "$CONTAINER_NAME" -t 10
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SimpleX Binary Management
|
||||
# ============================================================================
|
||||
|
||||
download_binaries() {
|
||||
log "Downloading SimpleX binaries (version $SIMPLEX_VERSION)..."
|
||||
|
||||
local arch=$(detect_arch)
|
||||
local bin_suffix=""
|
||||
|
||||
case "$arch" in
|
||||
aarch64) bin_suffix="ubuntu-20_04-aarch64" ;;
|
||||
x86_64) bin_suffix="ubuntu-20_04-x86-64" ;;
|
||||
esac
|
||||
|
||||
local smp_url="$SIMPLEX_BASE_URL/$SIMPLEX_VERSION/smp-server-$bin_suffix"
|
||||
local xftp_url="$SIMPLEX_BASE_URL/$SIMPLEX_VERSION/xftp-server-$bin_suffix"
|
||||
|
||||
mkdir -p "$DATA_PATH/bin"
|
||||
|
||||
# Download SMP server
|
||||
log "Downloading SMP server..."
|
||||
if ! wget -q --show-progress -O "$DATA_PATH/bin/smp-server" "$smp_url"; then
|
||||
error "Failed to download SMP server"
|
||||
return 1
|
||||
fi
|
||||
chmod +x "$DATA_PATH/bin/smp-server"
|
||||
|
||||
# Download XFTP server
|
||||
log "Downloading XFTP server..."
|
||||
if ! wget -q --show-progress -O "$DATA_PATH/bin/xftp-server" "$xftp_url"; then
|
||||
error "Failed to download XFTP server"
|
||||
return 1
|
||||
fi
|
||||
chmod +x "$DATA_PATH/bin/xftp-server"
|
||||
|
||||
log "Binaries downloaded successfully"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TLS Certificate Management
|
||||
# ============================================================================
|
||||
|
||||
generate_self_signed_certs() {
|
||||
local hostname="$1"
|
||||
[ -z "$hostname" ] && hostname="simplex.local"
|
||||
|
||||
log "Generating self-signed TLS certificates for $hostname..."
|
||||
|
||||
mkdir -p "$CERT_PATH"
|
||||
|
||||
# Generate CA key and cert
|
||||
openssl genrsa -out "$CERT_PATH/ca.key" 4096 2>/dev/null
|
||||
openssl req -new -x509 -days 3650 -key "$CERT_PATH/ca.key" \
|
||||
-out "$CERT_PATH/ca.crt" -subj "/CN=SimpleX CA" 2>/dev/null
|
||||
|
||||
# Generate server key and CSR
|
||||
openssl genrsa -out "$CERT_PATH/server.key" 4096 2>/dev/null
|
||||
openssl req -new -key "$CERT_PATH/server.key" \
|
||||
-out "$CERT_PATH/server.csr" -subj "/CN=$hostname" 2>/dev/null
|
||||
|
||||
# Create server certificate
|
||||
cat > "$CERT_PATH/server.ext" << EOF
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = DNS:$hostname
|
||||
EOF
|
||||
|
||||
openssl x509 -req -in "$CERT_PATH/server.csr" -CA "$CERT_PATH/ca.crt" \
|
||||
-CAkey "$CERT_PATH/ca.key" -CAcreateserial -out "$CERT_PATH/server.crt" \
|
||||
-days 365 -extfile "$CERT_PATH/server.ext" 2>/dev/null
|
||||
|
||||
# Cleanup
|
||||
rm -f "$CERT_PATH/server.csr" "$CERT_PATH/server.ext"
|
||||
|
||||
log "Certificates generated at $CERT_PATH"
|
||||
}
|
||||
|
||||
init_letsencrypt() {
|
||||
local domain="$1"
|
||||
local email="$2"
|
||||
|
||||
if [ -z "$domain" ]; then
|
||||
error "Domain required for Let's Encrypt"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Initializing Let's Encrypt for $domain..."
|
||||
|
||||
# Use acme.sh if available, or certbot
|
||||
if command -v acme.sh >/dev/null 2>&1; then
|
||||
acme.sh --issue -d "$domain" --standalone --keylength 4096 \
|
||||
--cert-file "$CERT_PATH/server.crt" \
|
||||
--key-file "$CERT_PATH/server.key" \
|
||||
--fullchain-file "$CERT_PATH/fullchain.crt"
|
||||
else
|
||||
error "acme.sh not found. Install with: opkg install acme"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Server Configuration
|
||||
# ============================================================================
|
||||
|
||||
init_smp_server() {
|
||||
log "Initializing SMP server..."
|
||||
|
||||
mkdir -p "$SMP_DIR"
|
||||
|
||||
# Initialize server if not already done
|
||||
if [ ! -f "$SMP_DIR/smp-server.ini" ]; then
|
||||
local hostname="${SMP_HOSTNAME:-$(hostname)}"
|
||||
|
||||
# Create SMP config
|
||||
cat > "$SMP_DIR/smp-server.ini" << EOF
|
||||
[STORE_LOG]
|
||||
enable: ${SMP_STORE_LOG}
|
||||
restore_messages: on
|
||||
|
||||
[AUTH]
|
||||
new_queues: ${SMP_QUEUE_PASSWORD:+on}
|
||||
|
||||
[TRANSPORT]
|
||||
host: $hostname
|
||||
port: $SMP_PORT
|
||||
control_port: ${SMP_CONTROL_PORT}
|
||||
websockets: off
|
||||
|
||||
[SERVER]
|
||||
information: SimpleX SMP Server
|
||||
source_code: https://github.com/simplex-chat/simplexmq
|
||||
EOF
|
||||
|
||||
# Add password if set
|
||||
if [ -n "$SMP_QUEUE_PASSWORD" ]; then
|
||||
echo "create_password: $SMP_QUEUE_PASSWORD" >> "$SMP_DIR/smp-server.ini"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generate server keys if not present
|
||||
if [ ! -f "$SMP_DIR/server.key" ]; then
|
||||
log "Generating SMP server keys..."
|
||||
if [ -x "$DATA_PATH/bin/smp-server" ]; then
|
||||
cd "$SMP_DIR"
|
||||
"$DATA_PATH/bin/smp-server" init --store-log
|
||||
fi
|
||||
fi
|
||||
|
||||
log "SMP server initialized"
|
||||
}
|
||||
|
||||
init_xftp_server() {
|
||||
log "Initializing XFTP server..."
|
||||
|
||||
mkdir -p "$XFTP_DIR/files"
|
||||
|
||||
# Parse storage quota
|
||||
local quota_bytes=$(echo "$XFTP_STORAGE_QUOTA" | \
|
||||
sed -e 's/G/*1024*1024*1024/' -e 's/M/*1024*1024/' -e 's/K/*1024/' | bc)
|
||||
|
||||
# Create XFTP config
|
||||
if [ ! -f "$XFTP_DIR/xftp-server.ini" ]; then
|
||||
local hostname="${XFTP_HOSTNAME:-$(hostname)}"
|
||||
|
||||
cat > "$XFTP_DIR/xftp-server.ini" << EOF
|
||||
[STORE_LOG]
|
||||
enable: on
|
||||
file_path: $XFTP_DIR/files
|
||||
|
||||
[AUTH]
|
||||
new_files: ${XFTP_CREATE_PASSWORD:+on}
|
||||
|
||||
[TRANSPORT]
|
||||
host: $hostname
|
||||
port: $XFTP_PORT
|
||||
control_port: ${XFTP_CONTROL_PORT}
|
||||
|
||||
[SERVER]
|
||||
information: SimpleX XFTP Server
|
||||
source_code: https://github.com/simplex-chat/simplexmq
|
||||
|
||||
[QUOTA]
|
||||
storage_quota: $quota_bytes
|
||||
file_expiration: $XFTP_FILE_EXPIRY
|
||||
EOF
|
||||
|
||||
# Add password if set
|
||||
if [ -n "$XFTP_CREATE_PASSWORD" ]; then
|
||||
echo "create_password: $XFTP_CREATE_PASSWORD" >> "$XFTP_DIR/xftp-server.ini"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generate server keys if not present
|
||||
if [ ! -f "$XFTP_DIR/server.key" ]; then
|
||||
log "Generating XFTP server keys..."
|
||||
if [ -x "$DATA_PATH/bin/xftp-server" ]; then
|
||||
cd "$XFTP_DIR"
|
||||
"$DATA_PATH/bin/xftp-server" init
|
||||
fi
|
||||
fi
|
||||
|
||||
log "XFTP server initialized"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Service Control
|
||||
# ============================================================================
|
||||
|
||||
service_run() {
|
||||
local service="$1"
|
||||
load_config
|
||||
|
||||
case "$service" in
|
||||
smp)
|
||||
if [ -x "$DATA_PATH/bin/smp-server" ]; then
|
||||
cd "$SMP_DIR"
|
||||
exec "$DATA_PATH/bin/smp-server" start
|
||||
else
|
||||
error "SMP server binary not found"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
xftp)
|
||||
if [ -x "$DATA_PATH/bin/xftp-server" ]; then
|
||||
cd "$XFTP_DIR"
|
||||
exec "$DATA_PATH/bin/xftp-server" start
|
||||
else
|
||||
error "XFTP server binary not found"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
error "Unknown service: $service"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
stop_services() {
|
||||
# Send SIGTERM to any running servers
|
||||
pgrep smp-server && pkill smp-server
|
||||
pgrep xftp-server && pkill xftp-server
|
||||
}
|
||||
|
||||
reload_config() {
|
||||
load_config
|
||||
init_smp_server
|
||||
init_xftp_server
|
||||
log "Configuration reloaded"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Server Addresses
|
||||
# ============================================================================
|
||||
|
||||
get_server_fingerprint() {
|
||||
local key_file="$1"
|
||||
if [ -f "$key_file" ]; then
|
||||
# Extract base64-encoded fingerprint from key
|
||||
openssl x509 -in "$key_file" -noout -fingerprint -sha256 2>/dev/null | \
|
||||
sed 's/.*=//' | tr -d ':' | xxd -r -p | base64 | tr '+/' '-_' | tr -d '='
|
||||
fi
|
||||
}
|
||||
|
||||
show_addresses() {
|
||||
load_config
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " SimpleX Server Addresses"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# SMP address
|
||||
if [ "$SMP_ENABLED" = "1" ] && [ -f "$SMP_DIR/fingerprint" ]; then
|
||||
local smp_fp=$(cat "$SMP_DIR/fingerprint" 2>/dev/null)
|
||||
local smp_host="${SMP_HOSTNAME:-$(hostname)}"
|
||||
echo "SMP Server:"
|
||||
echo " smp://${smp_fp}@${smp_host}:${SMP_PORT}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# XFTP address
|
||||
if [ "$XFTP_ENABLED" = "1" ] && [ -f "$XFTP_DIR/fingerprint" ]; then
|
||||
local xftp_fp=$(cat "$XFTP_DIR/fingerprint" 2>/dev/null)
|
||||
local xftp_host="${XFTP_HOSTNAME:-$(hostname)}"
|
||||
echo "XFTP Server:"
|
||||
echo " xftp://${xftp_fp}@${xftp_host}:${XFTP_PORT}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Add these addresses to your SimpleX Chat app"
|
||||
echo "under Settings > Network & Servers"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Firewall Configuration
|
||||
# ============================================================================
|
||||
|
||||
configure_firewall() {
|
||||
load_config
|
||||
log "Configuring firewall..."
|
||||
|
||||
# SMP port
|
||||
if [ "$SMP_ENABLED" = "1" ]; then
|
||||
if ! uci show firewall 2>/dev/null | grep -q "SimpleX-SMP"; then
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='SimpleX-SMP'
|
||||
uci set firewall.@rule[-1].src='wan'
|
||||
uci set firewall.@rule[-1].dest_port="$SMP_PORT"
|
||||
uci set firewall.@rule[-1].proto='tcp'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
uci set firewall.@rule[-1].enabled='1'
|
||||
fi
|
||||
fi
|
||||
|
||||
# XFTP port
|
||||
if [ "$XFTP_ENABLED" = "1" ]; then
|
||||
if ! uci show firewall 2>/dev/null | grep -q "SimpleX-XFTP"; then
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='SimpleX-XFTP'
|
||||
uci set firewall.@rule[-1].src='wan'
|
||||
uci set firewall.@rule[-1].dest_port="$XFTP_PORT"
|
||||
uci set firewall.@rule[-1].proto='tcp'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
uci set firewall.@rule[-1].enabled='1'
|
||||
fi
|
||||
fi
|
||||
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall reload 2>/dev/null
|
||||
|
||||
log "Firewall configured"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# HAProxy Integration
|
||||
# ============================================================================
|
||||
|
||||
configure_haproxy() {
|
||||
load_config
|
||||
|
||||
if [ ! -x /usr/sbin/haproxyctl ]; then
|
||||
warn "HAProxy not installed, skipping integration"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Configuring HAProxy..."
|
||||
|
||||
local domain="${TLS_DOMAIN:-simplex.local}"
|
||||
|
||||
# Check if vhost already exists
|
||||
if uci show haproxy 2>/dev/null | grep -q "\.domain='$domain'"; then
|
||||
warn "HAProxy vhost for $domain already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add SMP backend (TCP mode for TLS passthrough)
|
||||
uci -q add haproxy backend
|
||||
uci -q set haproxy.@backend[-1].name='simplex_smp'
|
||||
uci -q set haproxy.@backend[-1].mode='tcp'
|
||||
uci -q add_list haproxy.@backend[-1].server="simplex-smp 127.0.0.1:$SMP_PORT check"
|
||||
|
||||
uci commit haproxy
|
||||
/etc/init.d/haproxy reload 2>/dev/null
|
||||
|
||||
log "HAProxy configured"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Status Display
|
||||
# ============================================================================
|
||||
|
||||
show_status() {
|
||||
load_config
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " SimpleX Chat Server Status v$VERSION"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
echo "Configuration:"
|
||||
echo -e " Enabled: $([ "$ENABLED" = "1" ] && echo "${GREEN}Yes${NC}" || echo "${RED}No${NC}")"
|
||||
echo " Data Path: $DATA_PATH"
|
||||
echo ""
|
||||
|
||||
# Container status
|
||||
echo "Container:"
|
||||
if container_exists; then
|
||||
if container_running; then
|
||||
echo -e " Status: ${GREEN}Running${NC}"
|
||||
else
|
||||
echo -e " Status: ${YELLOW}Stopped${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e " Status: ${RED}Not installed${NC}"
|
||||
echo ""
|
||||
echo "Run 'simplexctl install' to set up the server"
|
||||
return
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# SMP server status
|
||||
echo "SMP Server:"
|
||||
echo -e " Enabled: $([ "$SMP_ENABLED" = "1" ] && echo "${GREEN}Yes${NC}" || echo "${RED}No${NC}")"
|
||||
if [ "$SMP_ENABLED" = "1" ]; then
|
||||
echo " Port: $SMP_PORT"
|
||||
echo " Hostname: ${SMP_HOSTNAME:-<not set>}"
|
||||
if pgrep smp-server >/dev/null 2>&1; then
|
||||
echo -e " Status: ${GREEN}Running${NC}"
|
||||
else
|
||||
echo -e " Status: ${RED}Stopped${NC}"
|
||||
fi
|
||||
if [ -f "$SMP_DIR/fingerprint" ]; then
|
||||
echo " Fingerprint: $(cat "$SMP_DIR/fingerprint" | head -c 20)..."
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# XFTP server status
|
||||
echo "XFTP Server:"
|
||||
echo -e " Enabled: $([ "$XFTP_ENABLED" = "1" ] && echo "${GREEN}Yes${NC}" || echo "${RED}No${NC}")"
|
||||
if [ "$XFTP_ENABLED" = "1" ]; then
|
||||
echo " Port: $XFTP_PORT"
|
||||
echo " Hostname: ${XFTP_HOSTNAME:-<not set>}"
|
||||
echo " Quota: $XFTP_STORAGE_QUOTA"
|
||||
echo " File Expiry: $XFTP_FILE_EXPIRY"
|
||||
if pgrep xftp-server >/dev/null 2>&1; then
|
||||
echo -e " Status: ${GREEN}Running${NC}"
|
||||
else
|
||||
echo -e " Status: ${RED}Stopped${NC}"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Storage usage
|
||||
if [ -d "$XFTP_DIR/files" ]; then
|
||||
local storage_used=$(du -sh "$XFTP_DIR/files" 2>/dev/null | cut -f1)
|
||||
echo "Storage:"
|
||||
echo " Used: ${storage_used:-0}"
|
||||
echo " Quota: $XFTP_STORAGE_QUOTA"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Logs
|
||||
# ============================================================================
|
||||
|
||||
show_logs() {
|
||||
local service="${1:-all}"
|
||||
local lines="${2:-50}"
|
||||
|
||||
case "$service" in
|
||||
smp)
|
||||
if [ -f "$SMP_DIR/smp-server-store.log" ]; then
|
||||
tail -n "$lines" "$SMP_DIR/smp-server-store.log"
|
||||
else
|
||||
echo "No SMP logs found"
|
||||
fi
|
||||
;;
|
||||
xftp)
|
||||
if [ -f "$XFTP_DIR/xftp-server-store.log" ]; then
|
||||
tail -n "$lines" "$XFTP_DIR/xftp-server-store.log"
|
||||
else
|
||||
echo "No XFTP logs found"
|
||||
fi
|
||||
;;
|
||||
all)
|
||||
echo "=== SMP Logs ==="
|
||||
show_logs smp "$lines"
|
||||
echo ""
|
||||
echo "=== XFTP Logs ==="
|
||||
show_logs xftp "$lines"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: simplexctl logs [smp|xftp|all] [lines]"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Installation
|
||||
# ============================================================================
|
||||
|
||||
install_simplex() {
|
||||
log "Installing SimpleX Chat Server..."
|
||||
|
||||
# Check LXC
|
||||
if ! command -v lxc-start >/dev/null 2>&1; then
|
||||
error "LXC is required. Install with: opkg install lxc lxc-common"
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_config
|
||||
|
||||
# Create directories
|
||||
mkdir -p "$DATA_PATH" "$SMP_DIR" "$XFTP_DIR" "$CERT_PATH"
|
||||
|
||||
# Create LXC container
|
||||
if ! container_exists; then
|
||||
create_container || return 1
|
||||
fi
|
||||
|
||||
# Start container
|
||||
start_container || return 1
|
||||
|
||||
# Run setup inside container
|
||||
lxc-attach -n "$CONTAINER_NAME" -- /tmp/setup.sh
|
||||
|
||||
# Download binaries
|
||||
download_binaries || return 1
|
||||
|
||||
# Generate certificates
|
||||
local hostname="${TLS_DOMAIN:-$(hostname)}"
|
||||
if [ "$USE_LETSENCRYPT" = "1" ] && [ -n "$TLS_DOMAIN" ]; then
|
||||
init_letsencrypt "$TLS_DOMAIN" "$TLS_EMAIL"
|
||||
else
|
||||
generate_self_signed_certs "$hostname"
|
||||
fi
|
||||
|
||||
# Initialize servers
|
||||
init_smp_server
|
||||
init_xftp_server
|
||||
|
||||
# Configure firewall
|
||||
configure_firewall
|
||||
|
||||
# Configure HAProxy if available
|
||||
configure_haproxy
|
||||
|
||||
log ""
|
||||
log "SimpleX Chat Server installed successfully!"
|
||||
log ""
|
||||
log "Next steps:"
|
||||
log " 1. Set hostnames: uci set simplex.smp.hostname='smp.example.com'"
|
||||
log " 2. Commit: uci commit simplex"
|
||||
log " 3. Enable: uci set simplex.main.enabled=1 && uci commit simplex"
|
||||
log " 4. Start: /etc/init.d/simplex start"
|
||||
log ""
|
||||
log "Get server addresses: simplexctl get-address"
|
||||
log ""
|
||||
}
|
||||
|
||||
uninstall_simplex() {
|
||||
log "Uninstalling SimpleX Chat Server..."
|
||||
|
||||
# Stop services
|
||||
/etc/init.d/simplex stop 2>/dev/null
|
||||
|
||||
# Stop and destroy container
|
||||
if container_exists; then
|
||||
stop_container
|
||||
log "Removing container..."
|
||||
lxc-destroy -n "$CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
# Remove firewall rules
|
||||
local rules=$(uci show firewall 2>/dev/null | grep "SimpleX-" | cut -d'.' -f2 | cut -d'=' -f1 | sort -ru)
|
||||
for rule in $rules; do
|
||||
uci delete firewall.@rule[$rule]
|
||||
done
|
||||
uci commit firewall 2>/dev/null
|
||||
|
||||
warn "Data preserved at $DATA_PATH"
|
||||
warn "Remove manually if desired: rm -rf $DATA_PATH"
|
||||
|
||||
log "Uninstall complete"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Backup / Restore
|
||||
# ============================================================================
|
||||
|
||||
backup() {
|
||||
local backup_file="${1:-/tmp/simplex-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
|
||||
|
||||
load_config
|
||||
log "Creating backup..."
|
||||
|
||||
tar -czf "$backup_file" \
|
||||
-C / \
|
||||
etc/config/simplex \
|
||||
"$SMP_DIR" \
|
||||
"$XFTP_DIR" \
|
||||
"$CERT_PATH" \
|
||||
2>/dev/null
|
||||
|
||||
if [ -f "$backup_file" ]; then
|
||||
local size=$(ls -lh "$backup_file" | awk '{print $5}')
|
||||
log "Backup created: $backup_file ($size)"
|
||||
else
|
||||
error "Backup failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restore() {
|
||||
local backup_file="$1"
|
||||
|
||||
if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then
|
||||
echo "Usage: simplexctl restore <backup_file>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Restoring from $backup_file..."
|
||||
|
||||
# Stop services
|
||||
/etc/init.d/simplex stop 2>/dev/null
|
||||
|
||||
# Extract backup
|
||||
tar -xzf "$backup_file" -C /
|
||||
|
||||
# Restart
|
||||
/etc/init.d/simplex start
|
||||
|
||||
log "Restore complete"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Shell Access
|
||||
# ============================================================================
|
||||
|
||||
container_shell() {
|
||||
if ! container_running; then
|
||||
start_container || return 1
|
||||
fi
|
||||
|
||||
log "Entering container shell..."
|
||||
lxc-attach -n "$CONTAINER_NAME"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main
|
||||
# ============================================================================
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
SimpleX Chat Server Control v$VERSION
|
||||
|
||||
Usage: simplexctl <command> [options]
|
||||
|
||||
Installation:
|
||||
install Install SimpleX servers in LXC container
|
||||
uninstall Remove container (preserves data)
|
||||
update Update SimpleX binaries to latest version
|
||||
|
||||
Service Control:
|
||||
start Start SMP and XFTP servers
|
||||
stop Stop servers
|
||||
restart Restart servers
|
||||
status Show service status
|
||||
|
||||
Server Addresses:
|
||||
get-address Display server addresses for clients
|
||||
|
||||
Certificates:
|
||||
init-certs Generate self-signed TLS certificates
|
||||
init-letsencrypt Request Let's Encrypt certificates
|
||||
|
||||
Logs & Debug:
|
||||
logs [smp|xftp] View server logs
|
||||
shell Access container shell
|
||||
|
||||
Backup:
|
||||
backup [file] Create configuration backup
|
||||
restore <file> Restore from backup
|
||||
|
||||
Configuration:
|
||||
configure-fw Configure firewall rules
|
||||
configure-haproxy Add HAProxy integration
|
||||
reload-config Reload configuration
|
||||
|
||||
Internal (used by init script):
|
||||
service-run <svc> Run service in foreground
|
||||
start-container Start LXC container
|
||||
stop-container Stop LXC container
|
||||
stop-services Stop server processes
|
||||
|
||||
Examples:
|
||||
simplexctl install
|
||||
simplexctl status
|
||||
simplexctl get-address
|
||||
simplexctl logs smp 100
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
install)
|
||||
install_simplex
|
||||
;;
|
||||
uninstall)
|
||||
uninstall_simplex
|
||||
;;
|
||||
update)
|
||||
load_config
|
||||
download_binaries
|
||||
;;
|
||||
start)
|
||||
start_container
|
||||
/etc/init.d/simplex start
|
||||
;;
|
||||
stop)
|
||||
/etc/init.d/simplex stop
|
||||
;;
|
||||
restart)
|
||||
/etc/init.d/simplex restart
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
get-address|get-addresses)
|
||||
show_addresses
|
||||
;;
|
||||
init-certs)
|
||||
load_config
|
||||
generate_self_signed_certs "${2:-$TLS_DOMAIN}"
|
||||
;;
|
||||
init-letsencrypt)
|
||||
load_config
|
||||
init_letsencrypt "${2:-$TLS_DOMAIN}" "${3:-$TLS_EMAIL}"
|
||||
;;
|
||||
logs)
|
||||
show_logs "$2" "$3"
|
||||
;;
|
||||
shell)
|
||||
container_shell
|
||||
;;
|
||||
backup)
|
||||
backup "$2"
|
||||
;;
|
||||
restore)
|
||||
restore "$2"
|
||||
;;
|
||||
configure-fw)
|
||||
configure_firewall
|
||||
;;
|
||||
configure-haproxy)
|
||||
configure_haproxy
|
||||
;;
|
||||
reload-config)
|
||||
reload_config
|
||||
;;
|
||||
service-run)
|
||||
service_run "$2"
|
||||
;;
|
||||
start-container)
|
||||
start_container
|
||||
;;
|
||||
stop-container)
|
||||
stop_container
|
||||
;;
|
||||
stop-services)
|
||||
stop_services
|
||||
;;
|
||||
-h|--help|help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Loading…
Reference in New Issue
Block a user