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",
|
"feed_url": "/secubox-feed",
|
||||||
"generated": "2026-02-02T11:00:39+01:00",
|
"generated": "2026-02-02T12:51:55+01:00",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "luci-app-auth-guardian",
|
"name": "luci-app-auth-guardian",
|
||||||
"version": "0.4.0-r3",
|
"version": "0.4.0-r3",
|
||||||
"filename": "luci-app-auth-guardian_0.4.0-r3_all.ipk",
|
"filename": "luci-app-auth-guardian_0.4.0-r3_all.ipk",
|
||||||
"size": 11737,
|
"size": 11736,
|
||||||
"category": "security",
|
"category": "security",
|
||||||
"icon": "key",
|
"icon": "key",
|
||||||
"description": "Authentication management",
|
"description": "Authentication management",
|
||||||
@ -18,7 +18,7 @@
|
|||||||
"name": "luci-app-bandwidth-manager",
|
"name": "luci-app-bandwidth-manager",
|
||||||
"version": "0.5.0-r2",
|
"version": "0.5.0-r2",
|
||||||
"filename": "luci-app-bandwidth-manager_0.5.0-r2_all.ipk",
|
"filename": "luci-app-bandwidth-manager_0.5.0-r2_all.ipk",
|
||||||
"size": 61540,
|
"size": 61538,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "activity",
|
"icon": "activity",
|
||||||
"description": "Bandwidth monitoring and control",
|
"description": "Bandwidth monitoring and control",
|
||||||
@ -30,7 +30,7 @@
|
|||||||
"name": "luci-app-cdn-cache",
|
"name": "luci-app-cdn-cache",
|
||||||
"version": "0.5.0-r3",
|
"version": "0.5.0-r3",
|
||||||
"filename": "luci-app-cdn-cache_0.5.0-r3_all.ipk",
|
"filename": "luci-app-cdn-cache_0.5.0-r3_all.ipk",
|
||||||
"size": 23184,
|
"size": 23183,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "globe",
|
"icon": "globe",
|
||||||
"description": "CDN caching",
|
"description": "CDN caching",
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"name": "luci-app-client-guardian",
|
"name": "luci-app-client-guardian",
|
||||||
"version": "0.4.0-r7",
|
"version": "0.4.0-r7",
|
||||||
"filename": "luci-app-client-guardian_0.4.0-r7_all.ipk",
|
"filename": "luci-app-client-guardian_0.4.0-r7_all.ipk",
|
||||||
"size": 54534,
|
"size": 54537,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "users",
|
"icon": "users",
|
||||||
"description": "Client management and monitoring",
|
"description": "Client management and monitoring",
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"name": "luci-app-crowdsec-dashboard",
|
"name": "luci-app-crowdsec-dashboard",
|
||||||
"version": "0.7.0-r32",
|
"version": "0.7.0-r32",
|
||||||
"filename": "luci-app-crowdsec-dashboard_0.7.0-r32_all.ipk",
|
"filename": "luci-app-crowdsec-dashboard_0.7.0-r32_all.ipk",
|
||||||
"size": 32630,
|
"size": 32627,
|
||||||
"category": "security",
|
"category": "security",
|
||||||
"icon": "shield",
|
"icon": "shield",
|
||||||
"description": "CrowdSec security monitoring",
|
"description": "CrowdSec security monitoring",
|
||||||
@ -66,7 +66,7 @@
|
|||||||
"name": "luci-app-cyberfeed",
|
"name": "luci-app-cyberfeed",
|
||||||
"version": "0.1.1-r1",
|
"version": "0.1.1-r1",
|
||||||
"filename": "luci-app-cyberfeed_0.1.1-r1_all.ipk",
|
"filename": "luci-app-cyberfeed_0.1.1-r1_all.ipk",
|
||||||
"size": 12838,
|
"size": 12839,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -78,7 +78,7 @@
|
|||||||
"name": "luci-app-dnsguard",
|
"name": "luci-app-dnsguard",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-dnsguard_1.0.0-r1_all.ipk",
|
"filename": "luci-app-dnsguard_1.0.0-r1_all.ipk",
|
||||||
"size": 7546,
|
"size": 7551,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -90,7 +90,7 @@
|
|||||||
"name": "luci-app-exposure",
|
"name": "luci-app-exposure",
|
||||||
"version": "1.0.0-r3",
|
"version": "1.0.0-r3",
|
||||||
"filename": "luci-app-exposure_1.0.0-r3_all.ipk",
|
"filename": "luci-app-exposure_1.0.0-r3_all.ipk",
|
||||||
"size": 20536,
|
"size": 20533,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -114,7 +114,7 @@
|
|||||||
"name": "luci-app-glances",
|
"name": "luci-app-glances",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "luci-app-glances_1.0.0-r2_all.ipk",
|
"filename": "luci-app-glances_1.0.0-r2_all.ipk",
|
||||||
"size": 6962,
|
"size": 6968,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -126,7 +126,7 @@
|
|||||||
"name": "luci-app-haproxy",
|
"name": "luci-app-haproxy",
|
||||||
"version": "1.0.0-r8",
|
"version": "1.0.0-r8",
|
||||||
"filename": "luci-app-haproxy_1.0.0-r8_all.ipk",
|
"filename": "luci-app-haproxy_1.0.0-r8_all.ipk",
|
||||||
"size": 34557,
|
"size": 34561,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -138,7 +138,7 @@
|
|||||||
"name": "luci-app-hexojs",
|
"name": "luci-app-hexojs",
|
||||||
"version": "1.0.0-r3",
|
"version": "1.0.0-r3",
|
||||||
"filename": "luci-app-hexojs_1.0.0-r3_all.ipk",
|
"filename": "luci-app-hexojs_1.0.0-r3_all.ipk",
|
||||||
"size": 30303,
|
"size": 30308,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -150,7 +150,7 @@
|
|||||||
"name": "luci-app-jitsi",
|
"name": "luci-app-jitsi",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-jitsi_1.0.0-r1_all.ipk",
|
"filename": "luci-app-jitsi_1.0.0-r1_all.ipk",
|
||||||
"size": 5139,
|
"size": 5141,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -162,7 +162,7 @@
|
|||||||
"name": "luci-app-ksm-manager",
|
"name": "luci-app-ksm-manager",
|
||||||
"version": "0.4.0-r2",
|
"version": "0.4.0-r2",
|
||||||
"filename": "luci-app-ksm-manager_0.4.0-r2_all.ipk",
|
"filename": "luci-app-ksm-manager_0.4.0-r2_all.ipk",
|
||||||
"size": 18718,
|
"size": 18724,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "cpu",
|
"icon": "cpu",
|
||||||
"description": "Kernel memory management",
|
"description": "Kernel memory management",
|
||||||
@ -174,7 +174,7 @@
|
|||||||
"name": "luci-app-localai",
|
"name": "luci-app-localai",
|
||||||
"version": "0.1.0-r15",
|
"version": "0.1.0-r15",
|
||||||
"filename": "luci-app-localai_0.1.0-r15_all.ipk",
|
"filename": "luci-app-localai_0.1.0-r15_all.ipk",
|
||||||
"size": 13181,
|
"size": 13183,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -198,7 +198,7 @@
|
|||||||
"name": "luci-app-magicmirror2",
|
"name": "luci-app-magicmirror2",
|
||||||
"version": "0.4.0-r6",
|
"version": "0.4.0-r6",
|
||||||
"filename": "luci-app-magicmirror2_0.4.0-r6_all.ipk",
|
"filename": "luci-app-magicmirror2_0.4.0-r6_all.ipk",
|
||||||
"size": 12277,
|
"size": 12279,
|
||||||
"category": "iot",
|
"category": "iot",
|
||||||
"icon": "monitor",
|
"icon": "monitor",
|
||||||
"description": "Smart mirror display",
|
"description": "Smart mirror display",
|
||||||
@ -210,7 +210,7 @@
|
|||||||
"name": "luci-app-mailinabox",
|
"name": "luci-app-mailinabox",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-mailinabox_1.0.0-r1_all.ipk",
|
"filename": "luci-app-mailinabox_1.0.0-r1_all.ipk",
|
||||||
"size": 5484,
|
"size": 5481,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -222,7 +222,7 @@
|
|||||||
"name": "luci-app-media-flow",
|
"name": "luci-app-media-flow",
|
||||||
"version": "0.6.4-r1",
|
"version": "0.6.4-r1",
|
||||||
"filename": "luci-app-media-flow_0.6.4-r1_all.ipk",
|
"filename": "luci-app-media-flow_0.6.4-r1_all.ipk",
|
||||||
"size": 25414,
|
"size": 25420,
|
||||||
"category": "media",
|
"category": "media",
|
||||||
"icon": "film",
|
"icon": "film",
|
||||||
"description": "Media streaming",
|
"description": "Media streaming",
|
||||||
@ -234,7 +234,7 @@
|
|||||||
"name": "luci-app-metablogizer",
|
"name": "luci-app-metablogizer",
|
||||||
"version": "1.0.0-r5",
|
"version": "1.0.0-r5",
|
||||||
"filename": "luci-app-metablogizer_1.0.0-r5_all.ipk",
|
"filename": "luci-app-metablogizer_1.0.0-r5_all.ipk",
|
||||||
"size": 23343,
|
"size": 23348,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -246,7 +246,7 @@
|
|||||||
"name": "luci-app-metabolizer",
|
"name": "luci-app-metabolizer",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "luci-app-metabolizer_1.0.0-r2_all.ipk",
|
"filename": "luci-app-metabolizer_1.0.0-r2_all.ipk",
|
||||||
"size": 4759,
|
"size": 4757,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -258,7 +258,7 @@
|
|||||||
"name": "luci-app-mitmproxy",
|
"name": "luci-app-mitmproxy",
|
||||||
"version": "0.5.0-r2",
|
"version": "0.5.0-r2",
|
||||||
"filename": "luci-app-mitmproxy_0.5.0-r2_all.ipk",
|
"filename": "luci-app-mitmproxy_0.5.0-r2_all.ipk",
|
||||||
"size": 10520,
|
"size": 10527,
|
||||||
"category": "security",
|
"category": "security",
|
||||||
"icon": "lock",
|
"icon": "lock",
|
||||||
"description": "HTTPS proxy and traffic inspection",
|
"description": "HTTPS proxy and traffic inspection",
|
||||||
@ -270,7 +270,7 @@
|
|||||||
"name": "luci-app-mmpm",
|
"name": "luci-app-mmpm",
|
||||||
"version": "0.2.0-r3",
|
"version": "0.2.0-r3",
|
||||||
"filename": "luci-app-mmpm_0.2.0-r3_all.ipk",
|
"filename": "luci-app-mmpm_0.2.0-r3_all.ipk",
|
||||||
"size": 7901,
|
"size": 7904,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -282,7 +282,7 @@
|
|||||||
"name": "luci-app-mqtt-bridge",
|
"name": "luci-app-mqtt-bridge",
|
||||||
"version": "0.4.0-r4",
|
"version": "0.4.0-r4",
|
||||||
"filename": "luci-app-mqtt-bridge_0.4.0-r4_all.ipk",
|
"filename": "luci-app-mqtt-bridge_0.4.0-r4_all.ipk",
|
||||||
"size": 22775,
|
"size": 22778,
|
||||||
"category": "iot",
|
"category": "iot",
|
||||||
"icon": "message-square",
|
"icon": "message-square",
|
||||||
"description": "MQTT bridge",
|
"description": "MQTT bridge",
|
||||||
@ -294,7 +294,7 @@
|
|||||||
"name": "luci-app-ndpid",
|
"name": "luci-app-ndpid",
|
||||||
"version": "1.1.2-r2",
|
"version": "1.1.2-r2",
|
||||||
"filename": "luci-app-ndpid_1.1.2-r2_all.ipk",
|
"filename": "luci-app-ndpid_1.1.2-r2_all.ipk",
|
||||||
"size": 22651,
|
"size": 22652,
|
||||||
"category": "security",
|
"category": "security",
|
||||||
"icon": "eye",
|
"icon": "eye",
|
||||||
"description": "Deep packet inspection",
|
"description": "Deep packet inspection",
|
||||||
@ -306,7 +306,7 @@
|
|||||||
"name": "luci-app-netdata-dashboard",
|
"name": "luci-app-netdata-dashboard",
|
||||||
"version": "0.5.0-r2",
|
"version": "0.5.0-r2",
|
||||||
"filename": "luci-app-netdata-dashboard_0.5.0-r2_all.ipk",
|
"filename": "luci-app-netdata-dashboard_0.5.0-r2_all.ipk",
|
||||||
"size": 20483,
|
"size": 20490,
|
||||||
"category": "monitoring",
|
"category": "monitoring",
|
||||||
"icon": "bar-chart-2",
|
"icon": "bar-chart-2",
|
||||||
"description": "System monitoring dashboard",
|
"description": "System monitoring dashboard",
|
||||||
@ -318,7 +318,7 @@
|
|||||||
"name": "luci-app-network-modes",
|
"name": "luci-app-network-modes",
|
||||||
"version": "0.5.0-r3",
|
"version": "0.5.0-r3",
|
||||||
"filename": "luci-app-network-modes_0.5.0-r3_all.ipk",
|
"filename": "luci-app-network-modes_0.5.0-r3_all.ipk",
|
||||||
"size": 54147,
|
"size": 54150,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "wifi",
|
"icon": "wifi",
|
||||||
"description": "Network configuration",
|
"description": "Network configuration",
|
||||||
@ -330,7 +330,7 @@
|
|||||||
"name": "luci-app-network-tweaks",
|
"name": "luci-app-network-tweaks",
|
||||||
"version": "1.0.0-r7",
|
"version": "1.0.0-r7",
|
||||||
"filename": "luci-app-network-tweaks_1.0.0-r7_all.ipk",
|
"filename": "luci-app-network-tweaks_1.0.0-r7_all.ipk",
|
||||||
"size": 14955,
|
"size": 14966,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "wifi",
|
"icon": "wifi",
|
||||||
"description": "Network configuration",
|
"description": "Network configuration",
|
||||||
@ -342,7 +342,7 @@
|
|||||||
"name": "luci-app-nextcloud",
|
"name": "luci-app-nextcloud",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-nextcloud_1.0.0-r1_all.ipk",
|
"filename": "luci-app-nextcloud_1.0.0-r1_all.ipk",
|
||||||
"size": 6486,
|
"size": 6487,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -354,7 +354,7 @@
|
|||||||
"name": "luci-app-ollama",
|
"name": "luci-app-ollama",
|
||||||
"version": "0.1.0-r1",
|
"version": "0.1.0-r1",
|
||||||
"filename": "luci-app-ollama_0.1.0-r1_all.ipk",
|
"filename": "luci-app-ollama_0.1.0-r1_all.ipk",
|
||||||
"size": 12349,
|
"size": 12351,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -366,7 +366,7 @@
|
|||||||
"name": "luci-app-picobrew",
|
"name": "luci-app-picobrew",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-picobrew_1.0.0-r1_all.ipk",
|
"filename": "luci-app-picobrew_1.0.0-r1_all.ipk",
|
||||||
"size": 9455,
|
"size": 9456,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -378,7 +378,7 @@
|
|||||||
"name": "luci-app-secubox",
|
"name": "luci-app-secubox",
|
||||||
"version": "0.7.1-r4",
|
"version": "0.7.1-r4",
|
||||||
"filename": "luci-app-secubox_0.7.1-r4_all.ipk",
|
"filename": "luci-app-secubox_0.7.1-r4_all.ipk",
|
||||||
"size": 77677,
|
"size": 77680,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -390,7 +390,7 @@
|
|||||||
"name": "luci-app-secubox-admin",
|
"name": "luci-app-secubox-admin",
|
||||||
"version": "1.0.0-r19",
|
"version": "1.0.0-r19",
|
||||||
"filename": "luci-app-secubox-admin_1.0.0-r19_all.ipk",
|
"filename": "luci-app-secubox-admin_1.0.0-r19_all.ipk",
|
||||||
"size": 57246,
|
"size": 57247,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -402,7 +402,7 @@
|
|||||||
"name": "luci-app-secubox-crowdsec",
|
"name": "luci-app-secubox-crowdsec",
|
||||||
"version": "1.0.0-r3",
|
"version": "1.0.0-r3",
|
||||||
"filename": "luci-app-secubox-crowdsec_1.0.0-r3_all.ipk",
|
"filename": "luci-app-secubox-crowdsec_1.0.0-r3_all.ipk",
|
||||||
"size": 13914,
|
"size": 13922,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -414,7 +414,7 @@
|
|||||||
"name": "luci-app-secubox-netdiag",
|
"name": "luci-app-secubox-netdiag",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-secubox-netdiag_1.0.0-r1_all.ipk",
|
"filename": "luci-app-secubox-netdiag_1.0.0-r1_all.ipk",
|
||||||
"size": 15307,
|
"size": 15306,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -426,7 +426,7 @@
|
|||||||
"name": "luci-app-secubox-netifyd",
|
"name": "luci-app-secubox-netifyd",
|
||||||
"version": "1.2.1-r1",
|
"version": "1.2.1-r1",
|
||||||
"filename": "luci-app-secubox-netifyd_1.2.1-r1_all.ipk",
|
"filename": "luci-app-secubox-netifyd_1.2.1-r1_all.ipk",
|
||||||
"size": 36538,
|
"size": 36545,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -450,7 +450,7 @@
|
|||||||
"name": "luci-app-secubox-portal",
|
"name": "luci-app-secubox-portal",
|
||||||
"version": "0.7.0-r2",
|
"version": "0.7.0-r2",
|
||||||
"filename": "luci-app-secubox-portal_0.7.0-r2_all.ipk",
|
"filename": "luci-app-secubox-portal_0.7.0-r2_all.ipk",
|
||||||
"size": 24643,
|
"size": 24644,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -462,7 +462,7 @@
|
|||||||
"name": "luci-app-secubox-security-threats",
|
"name": "luci-app-secubox-security-threats",
|
||||||
"version": "1.0.0-r4",
|
"version": "1.0.0-r4",
|
||||||
"filename": "luci-app-secubox-security-threats_1.0.0-r4_all.ipk",
|
"filename": "luci-app-secubox-security-threats_1.0.0-r4_all.ipk",
|
||||||
"size": 21311,
|
"size": 21314,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox system component",
|
"description": "SecuBox system component",
|
||||||
@ -474,7 +474,19 @@
|
|||||||
"name": "luci-app-service-registry",
|
"name": "luci-app-service-registry",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "luci-app-service-registry_1.0.0-r1_all.ipk",
|
"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",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -486,7 +498,7 @@
|
|||||||
"name": "luci-app-streamlit",
|
"name": "luci-app-streamlit",
|
||||||
"version": "1.0.0-r11",
|
"version": "1.0.0-r11",
|
||||||
"filename": "luci-app-streamlit_1.0.0-r11_all.ipk",
|
"filename": "luci-app-streamlit_1.0.0-r11_all.ipk",
|
||||||
"size": 14747,
|
"size": 14750,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -498,7 +510,7 @@
|
|||||||
"name": "luci-app-system-hub",
|
"name": "luci-app-system-hub",
|
||||||
"version": "0.5.1-r4",
|
"version": "0.5.1-r4",
|
||||||
"filename": "luci-app-system-hub_0.5.1-r4_all.ipk",
|
"filename": "luci-app-system-hub_0.5.1-r4_all.ipk",
|
||||||
"size": 61102,
|
"size": 61103,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "settings",
|
"icon": "settings",
|
||||||
"description": "System management",
|
"description": "System management",
|
||||||
@ -510,7 +522,7 @@
|
|||||||
"name": "luci-app-tor-shield",
|
"name": "luci-app-tor-shield",
|
||||||
"version": "1.0.0-r10",
|
"version": "1.0.0-r10",
|
||||||
"filename": "luci-app-tor-shield_1.0.0-r10_all.ipk",
|
"filename": "luci-app-tor-shield_1.0.0-r10_all.ipk",
|
||||||
"size": 22360,
|
"size": 22363,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -522,7 +534,7 @@
|
|||||||
"name": "luci-app-traffic-shaper",
|
"name": "luci-app-traffic-shaper",
|
||||||
"version": "0.4.0-r2",
|
"version": "0.4.0-r2",
|
||||||
"filename": "luci-app-traffic-shaper_0.4.0-r2_all.ipk",
|
"filename": "luci-app-traffic-shaper_0.4.0-r2_all.ipk",
|
||||||
"size": 14532,
|
"size": 14533,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "filter",
|
"icon": "filter",
|
||||||
"description": "Traffic shaping and QoS",
|
"description": "Traffic shaping and QoS",
|
||||||
@ -534,7 +546,7 @@
|
|||||||
"name": "luci-app-vhost-manager",
|
"name": "luci-app-vhost-manager",
|
||||||
"version": "0.5.0-r5",
|
"version": "0.5.0-r5",
|
||||||
"filename": "luci-app-vhost-manager_0.5.0-r5_all.ipk",
|
"filename": "luci-app-vhost-manager_0.5.0-r5_all.ipk",
|
||||||
"size": 26184,
|
"size": 26186,
|
||||||
"category": "network",
|
"category": "network",
|
||||||
"icon": "server",
|
"icon": "server",
|
||||||
"description": "Virtual host management",
|
"description": "Virtual host management",
|
||||||
@ -546,7 +558,7 @@
|
|||||||
"name": "luci-app-wireguard-dashboard",
|
"name": "luci-app-wireguard-dashboard",
|
||||||
"version": "0.7.0-r5",
|
"version": "0.7.0-r5",
|
||||||
"filename": "luci-app-wireguard-dashboard_0.7.0-r5_all.ipk",
|
"filename": "luci-app-wireguard-dashboard_0.7.0-r5_all.ipk",
|
||||||
"size": 39604,
|
"size": 39608,
|
||||||
"category": "vpn",
|
"category": "vpn",
|
||||||
"icon": "shield",
|
"icon": "shield",
|
||||||
"description": "WireGuard VPN dashboard",
|
"description": "WireGuard VPN dashboard",
|
||||||
@ -558,7 +570,7 @@
|
|||||||
"name": "luci-app-zigbee2mqtt",
|
"name": "luci-app-zigbee2mqtt",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "luci-app-zigbee2mqtt_1.0.0-r2_all.ipk",
|
"filename": "luci-app-zigbee2mqtt_1.0.0-r2_all.ipk",
|
||||||
"size": 6810,
|
"size": 6816,
|
||||||
"category": "iot",
|
"category": "iot",
|
||||||
"icon": "radio",
|
"icon": "radio",
|
||||||
"description": "Zigbee device management",
|
"description": "Zigbee device management",
|
||||||
@ -570,7 +582,7 @@
|
|||||||
"name": "luci-theme-secubox",
|
"name": "luci-theme-secubox",
|
||||||
"version": "0.4.7-r1",
|
"version": "0.4.7-r1",
|
||||||
"filename": "luci-theme-secubox_0.4.7-r1_all.ipk",
|
"filename": "luci-theme-secubox_0.4.7-r1_all.ipk",
|
||||||
"size": 110239,
|
"size": 110242,
|
||||||
"category": "theme",
|
"category": "theme",
|
||||||
"icon": "palette",
|
"icon": "palette",
|
||||||
"description": "LuCI theme",
|
"description": "LuCI theme",
|
||||||
@ -582,7 +594,7 @@
|
|||||||
"name": "secubox-app",
|
"name": "secubox-app",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "secubox-app_1.0.0-r2_all.ipk",
|
"filename": "secubox-app_1.0.0-r2_all.ipk",
|
||||||
"size": 11182,
|
"size": 11186,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox package",
|
"description": "SecuBox package",
|
||||||
@ -594,7 +606,7 @@
|
|||||||
"name": "secubox-app-adguardhome",
|
"name": "secubox-app-adguardhome",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "secubox-app-adguardhome_1.0.0-r2_all.ipk",
|
"filename": "secubox-app-adguardhome_1.0.0-r2_all.ipk",
|
||||||
"size": 2877,
|
"size": 2884,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -618,7 +630,7 @@
|
|||||||
"name": "secubox-app-crowdsec-custom",
|
"name": "secubox-app-crowdsec-custom",
|
||||||
"version": "1.1.0-r1",
|
"version": "1.1.0-r1",
|
||||||
"filename": "secubox-app-crowdsec-custom_1.1.0-r1_all.ipk",
|
"filename": "secubox-app-crowdsec-custom_1.1.0-r1_all.ipk",
|
||||||
"size": 5761,
|
"size": 5762,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -630,7 +642,7 @@
|
|||||||
"name": "secubox-app-cs-firewall-bouncer",
|
"name": "secubox-app-cs-firewall-bouncer",
|
||||||
"version": "0.0.31-r4_aarch64",
|
"version": "0.0.31-r4_aarch64",
|
||||||
"filename": "secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk",
|
"filename": "secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk",
|
||||||
"size": 5049325,
|
"size": 5049323,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -642,7 +654,7 @@
|
|||||||
"name": "secubox-app-cyberfeed",
|
"name": "secubox-app-cyberfeed",
|
||||||
"version": "0.2.1-r1",
|
"version": "0.2.1-r1",
|
||||||
"filename": "secubox-app-cyberfeed_0.2.1-r1_all.ipk",
|
"filename": "secubox-app-cyberfeed_0.2.1-r1_all.ipk",
|
||||||
"size": 12446,
|
"size": 12452,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -654,7 +666,7 @@
|
|||||||
"name": "secubox-app-domoticz",
|
"name": "secubox-app-domoticz",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "secubox-app-domoticz_1.0.0-r2_all.ipk",
|
"filename": "secubox-app-domoticz_1.0.0-r2_all.ipk",
|
||||||
"size": 2546,
|
"size": 2547,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -666,7 +678,7 @@
|
|||||||
"name": "secubox-app-exposure",
|
"name": "secubox-app-exposure",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "secubox-app-exposure_1.0.0-r1_all.ipk",
|
"filename": "secubox-app-exposure_1.0.0-r1_all.ipk",
|
||||||
"size": 6933,
|
"size": 6936,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -678,7 +690,7 @@
|
|||||||
"name": "secubox-app-gitea",
|
"name": "secubox-app-gitea",
|
||||||
"version": "1.0.0-r5",
|
"version": "1.0.0-r5",
|
||||||
"filename": "secubox-app-gitea_1.0.0-r5_all.ipk",
|
"filename": "secubox-app-gitea_1.0.0-r5_all.ipk",
|
||||||
"size": 9400,
|
"size": 9407,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -690,7 +702,7 @@
|
|||||||
"name": "secubox-app-glances",
|
"name": "secubox-app-glances",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "secubox-app-glances_1.0.0-r1_all.ipk",
|
"filename": "secubox-app-glances_1.0.0-r1_all.ipk",
|
||||||
"size": 5535,
|
"size": 5536,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -702,7 +714,7 @@
|
|||||||
"name": "secubox-app-haproxy",
|
"name": "secubox-app-haproxy",
|
||||||
"version": "1.0.0-r23",
|
"version": "1.0.0-r23",
|
||||||
"filename": "secubox-app-haproxy_1.0.0-r23_all.ipk",
|
"filename": "secubox-app-haproxy_1.0.0-r23_all.ipk",
|
||||||
"size": 15676,
|
"size": 15682,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -714,7 +726,7 @@
|
|||||||
"name": "secubox-app-hexojs",
|
"name": "secubox-app-hexojs",
|
||||||
"version": "1.0.0-r8",
|
"version": "1.0.0-r8",
|
||||||
"filename": "secubox-app-hexojs_1.0.0-r8_all.ipk",
|
"filename": "secubox-app-hexojs_1.0.0-r8_all.ipk",
|
||||||
"size": 94926,
|
"size": 94939,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -726,7 +738,7 @@
|
|||||||
"name": "secubox-app-jitsi",
|
"name": "secubox-app-jitsi",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "secubox-app-jitsi_1.0.0-r1_all.ipk",
|
"filename": "secubox-app-jitsi_1.0.0-r1_all.ipk",
|
||||||
"size": 8906,
|
"size": 8911,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -738,7 +750,7 @@
|
|||||||
"name": "secubox-app-localai",
|
"name": "secubox-app-localai",
|
||||||
"version": "2.25.0-r1",
|
"version": "2.25.0-r1",
|
||||||
"filename": "secubox-app-localai_2.25.0-r1_all.ipk",
|
"filename": "secubox-app-localai_2.25.0-r1_all.ipk",
|
||||||
"size": 5707,
|
"size": 5724,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -750,7 +762,7 @@
|
|||||||
"name": "secubox-app-localai-wb",
|
"name": "secubox-app-localai-wb",
|
||||||
"version": "2.25.0-r1",
|
"version": "2.25.0-r1",
|
||||||
"filename": "secubox-app-localai-wb_2.25.0-r1_all.ipk",
|
"filename": "secubox-app-localai-wb_2.25.0-r1_all.ipk",
|
||||||
"size": 7946,
|
"size": 7953,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -762,7 +774,7 @@
|
|||||||
"name": "secubox-app-lyrion",
|
"name": "secubox-app-lyrion",
|
||||||
"version": "2.0.2-r1",
|
"version": "2.0.2-r1",
|
||||||
"filename": "secubox-app-lyrion_2.0.2-r1_all.ipk",
|
"filename": "secubox-app-lyrion_2.0.2-r1_all.ipk",
|
||||||
"size": 7283,
|
"size": 7287,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -774,7 +786,7 @@
|
|||||||
"name": "secubox-app-magicmirror2",
|
"name": "secubox-app-magicmirror2",
|
||||||
"version": "0.4.0-r8",
|
"version": "0.4.0-r8",
|
||||||
"filename": "secubox-app-magicmirror2_0.4.0-r8_all.ipk",
|
"filename": "secubox-app-magicmirror2_0.4.0-r8_all.ipk",
|
||||||
"size": 9245,
|
"size": 9254,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -786,7 +798,7 @@
|
|||||||
"name": "secubox-app-mailinabox",
|
"name": "secubox-app-mailinabox",
|
||||||
"version": "2.0.0-r1",
|
"version": "2.0.0-r1",
|
||||||
"filename": "secubox-app-mailinabox_2.0.0-r1_all.ipk",
|
"filename": "secubox-app-mailinabox_2.0.0-r1_all.ipk",
|
||||||
"size": 7563,
|
"size": 7572,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -798,7 +810,7 @@
|
|||||||
"name": "secubox-app-metabolizer",
|
"name": "secubox-app-metabolizer",
|
||||||
"version": "1.0.0-r3",
|
"version": "1.0.0-r3",
|
||||||
"filename": "secubox-app-metabolizer_1.0.0-r3_all.ipk",
|
"filename": "secubox-app-metabolizer_1.0.0-r3_all.ipk",
|
||||||
"size": 13971,
|
"size": 13979,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -810,7 +822,7 @@
|
|||||||
"name": "secubox-app-mitmproxy",
|
"name": "secubox-app-mitmproxy",
|
||||||
"version": "0.5.0-r19",
|
"version": "0.5.0-r19",
|
||||||
"filename": "secubox-app-mitmproxy_0.5.0-r19_all.ipk",
|
"filename": "secubox-app-mitmproxy_0.5.0-r19_all.ipk",
|
||||||
"size": 22950,
|
"size": 22956,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -834,7 +846,7 @@
|
|||||||
"name": "secubox-app-nextcloud",
|
"name": "secubox-app-nextcloud",
|
||||||
"version": "1.0.0-r2",
|
"version": "1.0.0-r2",
|
||||||
"filename": "secubox-app-nextcloud_1.0.0-r2_all.ipk",
|
"filename": "secubox-app-nextcloud_1.0.0-r2_all.ipk",
|
||||||
"size": 2943,
|
"size": 2963,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -846,7 +858,7 @@
|
|||||||
"name": "secubox-app-ollama",
|
"name": "secubox-app-ollama",
|
||||||
"version": "0.1.0-r1",
|
"version": "0.1.0-r1",
|
||||||
"filename": "secubox-app-ollama_0.1.0-r1_all.ipk",
|
"filename": "secubox-app-ollama_0.1.0-r1_all.ipk",
|
||||||
"size": 5731,
|
"size": 5739,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -858,19 +870,31 @@
|
|||||||
"name": "secubox-app-picobrew",
|
"name": "secubox-app-picobrew",
|
||||||
"version": "1.0.0-r7",
|
"version": "1.0.0-r7",
|
||||||
"filename": "secubox-app-picobrew_1.0.0-r7_all.ipk",
|
"filename": "secubox-app-picobrew_1.0.0-r7_all.ipk",
|
||||||
"size": 5537,
|
"size": 5539,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
"installed": false,
|
"installed": false,
|
||||||
"luci_app": "luci-app-picobrew"
|
"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",
|
"name": "secubox-app-streamlit",
|
||||||
"version": "1.0.0-r5",
|
"version": "1.0.0-r5",
|
||||||
"filename": "secubox-app-streamlit_1.0.0-r5_all.ipk",
|
"filename": "secubox-app-streamlit_1.0.0-r5_all.ipk",
|
||||||
"size": 11716,
|
"size": 11725,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -882,7 +906,7 @@
|
|||||||
"name": "secubox-app-tor",
|
"name": "secubox-app-tor",
|
||||||
"version": "1.0.0-r1",
|
"version": "1.0.0-r1",
|
||||||
"filename": "secubox-app-tor_1.0.0-r1_all.ipk",
|
"filename": "secubox-app-tor_1.0.0-r1_all.ipk",
|
||||||
"size": 7360,
|
"size": 7369,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -894,7 +918,7 @@
|
|||||||
"name": "secubox-app-webapp",
|
"name": "secubox-app-webapp",
|
||||||
"version": "1.5.0-r7",
|
"version": "1.5.0-r7",
|
||||||
"filename": "secubox-app-webapp_1.5.0-r7_all.ipk",
|
"filename": "secubox-app-webapp_1.5.0-r7_all.ipk",
|
||||||
"size": 39169,
|
"size": 39175,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -906,7 +930,7 @@
|
|||||||
"name": "secubox-app-zigbee2mqtt",
|
"name": "secubox-app-zigbee2mqtt",
|
||||||
"version": "1.0.0-r3",
|
"version": "1.0.0-r3",
|
||||||
"filename": "secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk",
|
"filename": "secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk",
|
||||||
"size": 3537,
|
"size": 3542,
|
||||||
"category": "secubox",
|
"category": "secubox",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox backend service",
|
"description": "SecuBox backend service",
|
||||||
@ -918,7 +942,7 @@
|
|||||||
"name": "secubox-core",
|
"name": "secubox-core",
|
||||||
"version": "0.10.0-r11",
|
"version": "0.10.0-r11",
|
||||||
"filename": "secubox-core_0.10.0-r11_all.ipk",
|
"filename": "secubox-core_0.10.0-r11_all.ipk",
|
||||||
"size": 87971,
|
"size": 87975,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"icon": "box",
|
"icon": "box",
|
||||||
"description": "SecuBox core components",
|
"description": "SecuBox core components",
|
||||||
@ -930,7 +954,7 @@
|
|||||||
"name": "secubox-p2p",
|
"name": "secubox-p2p",
|
||||||
"version": "0.6.0-r3",
|
"version": "0.6.0-r3",
|
||||||
"filename": "secubox-p2p_0.6.0-r3_all.ipk",
|
"filename": "secubox-p2p_0.6.0-r3_all.ipk",
|
||||||
"size": 42010,
|
"size": 42016,
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"description": "SecuBox 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