'use strict'; 'require view'; 'require form'; 'require uci'; 'require rpc'; 'require poll'; 'require ui'; 'require secubox/kiss-theme'; 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 = '
'; // LXC/Container status html += '
'; html += '

Container

'; if (!status.lxc_available) { html += '

LXC not installed

'; html += '

Install: opkg install lxc lxc-common

'; } else if (!status.container_exists) { html += '

Not installed

'; html += '

Click "Install" below

'; } else { var containerColor = status.container_status === 'running' ? 'var(--kiss-green, #4caf50)' : 'var(--kiss-orange, #ff9800)'; html += '

Status: ' + status.container_status + '

'; } html += '
'; // SMP Server status if (status.smp_enabled) { html += '
'; html += '

SMP Server

'; var smpColor = status.smp_status === 'running' ? 'var(--kiss-green, #4caf50)' : '#c00'; html += '

Status: ' + status.smp_status + '

'; html += '

Port: ' + status.smp_port + '/tcp

'; if (status.smp_hostname) { html += '

Host: ' + status.smp_hostname + '

'; } html += '
'; } // XFTP Server status if (status.xftp_enabled) { html += '
'; html += '

XFTP Server

'; var xftpColor = status.xftp_status === 'running' ? 'var(--kiss-green, #4caf50)' : '#c00'; html += '

Status: ' + status.xftp_status + '

'; html += '

Port: ' + status.xftp_port + '/tcp

'; if (status.xftp_hostname) { html += '

Host: ' + status.xftp_hostname + '

'; } html += '
'; } // Storage stats if (stats) { html += '
'; html += '

Storage

'; html += '

Used: ' + formatBytes(stats.storage_used || 0) + '

'; html += '

Quota: ' + formatBytes(stats.storage_quota || 0) + '

'; html += '

Files: ' + (stats.file_count || 0) + '

'; if (stats.storage_quota > 0) { var pct = Math.round((stats.storage_used / stats.storage_quota) * 100); var barColor = pct > 80 ? '#c00' : (pct > 60 ? 'var(--kiss-orange, #ff9800)' : 'var(--kiss-green, #4caf50)'); html += '
'; html += '
'; html += '
'; html += '' + pct + '% used'; } html += '
'; } html += '
'; 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 = '
'; if (!addresses.smp_address && !addresses.xftp_address) { html += '

Server addresses will appear here after installation and configuration.

'; html += '

Set hostnames in the configuration below, then restart the service.

'; } else { html += '

Add these addresses to your SimpleX Chat app under Settings > Network & Servers

'; if (addresses.smp_address) { html += '
'; html += ''; html += '
'; html += '' + addresses.smp_address + ''; html += ''; html += '
'; html += '
'; } if (addresses.xftp_address) { html += '
'; html += ''; html += '
'; html += '' + addresses.xftp_address + ''; html += ''; html += '
'; html += '
'; } } html += '
'; // Add copyToClipboard function to window html += ''; 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 '
' + '

What is SimpleX?

' + '

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.

' + '

Server Types:

' + '' + '

Setup Steps:

' + '
    ' + '
  1. Set hostnames for SMP and XFTP servers above
  2. ' + '
  3. Click "Install Servers" to download binaries and create container
  4. ' + '
  5. Enable the service and save configuration
  6. ' + '
  7. Open firewall ports (5223 for SMP, 443 for XFTP)
  8. ' + '
  9. Add server addresses to your SimpleX Chat mobile app
  10. ' + '
' + '

Resources:

' + '' + '

CLI Commands:

' + '' + 'simplexctl status # Show server status
' + 'simplexctl get-address # Show server addresses
' + 'simplexctl logs smp # View SMP logs
' + 'simplexctl shell # Access container shell' + '
' + '
'; }; return m.render().then(function(formEl) { return KissTheme.wrap([formEl], 'admin/services/simplex'); }); } });