diff --git a/build/aarch64_cortex-a72/secubox-app-bonus_0.3.0-r2_all.ipk b/build/aarch64_cortex-a72/secubox-app-bonus_0.3.0-r2_all.ipk new file mode 100644 index 00000000..e8e62b21 Binary files /dev/null and b/build/aarch64_cortex-a72/secubox-app-bonus_0.3.0-r2_all.ipk differ diff --git a/package/secubox/luci-app-metablogizer/Makefile b/package/secubox/luci-app-metablogizer/Makefile index f4cee7b7..c672c8b1 100644 --- a/package/secubox/luci-app-metablogizer/Makefile +++ b/package/secubox/luci-app-metablogizer/Makefile @@ -12,7 +12,7 @@ LUCI_PKGARCH:=all PKG_NAME:=luci-app-metablogizer PKG_VERSION:=1.0.0 -PKG_RELEASE:=3 +PKG_RELEASE:=5 PKG_MAINTAINER:=CyberMind PKG_LICENSE:=GPL-2.0 diff --git a/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/metablogizer/api.js b/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/metablogizer/api.js new file mode 100644 index 00000000..dc6bf063 --- /dev/null +++ b/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/metablogizer/api.js @@ -0,0 +1,234 @@ +'use strict'; +'require rpc'; +'require baseclass'; + +/** + * MetaBlogizer API Module + * RPCD interface for MetaBlogizer static site publisher + */ + +var callStatus = rpc.declare({ + object: 'luci.metablogizer', + method: 'status', + expect: { result: {} } +}); + +var callListSites = rpc.declare({ + object: 'luci.metablogizer', + method: 'list_sites', + expect: { sites: [] } +}); + +var callCreateSite = rpc.declare({ + object: 'luci.metablogizer', + method: 'create_site', + params: ['name', 'domain', 'gitea_repo', 'ssl', 'description'], + expect: { result: {} } +}); + +var callUpdateSite = rpc.declare({ + object: 'luci.metablogizer', + method: 'update_site', + params: ['id', 'name', 'domain', 'gitea_repo', 'ssl', 'enabled', 'description'], + expect: { result: {} } +}); + +var callDeleteSite = rpc.declare({ + object: 'luci.metablogizer', + method: 'delete_site', + params: ['id'], + expect: { result: {} } +}); + +var callSyncSite = rpc.declare({ + object: 'luci.metablogizer', + method: 'sync_site', + params: ['id'], + expect: { result: {} } +}); + +var callGetHostingStatus = rpc.declare({ + object: 'luci.metablogizer', + method: 'get_hosting_status', + expect: { result: {} } +}); + +var callCheckSiteHealth = rpc.declare({ + object: 'luci.metablogizer', + method: 'check_site_health', + params: ['id'], + expect: { result: {} } +}); + +var callGetPublishInfo = rpc.declare({ + object: 'luci.metablogizer', + method: 'get_publish_info', + params: ['id'], + expect: { result: {} } +}); + +var callUploadFile = rpc.declare({ + object: 'luci.metablogizer', + method: 'upload_file', + params: ['site_id', 'filename', 'content'], + expect: { result: {} } +}); + +var callListFiles = rpc.declare({ + object: 'luci.metablogizer', + method: 'list_files', + params: ['site_id'], + expect: { result: {} } +}); + +var callGetSettings = rpc.declare({ + object: 'luci.metablogizer', + method: 'get_settings', + expect: { result: {} } +}); + +var callEnableTor = rpc.declare({ + object: 'luci.metablogizer', + method: 'enable_tor', + params: ['id'], + expect: { result: {} } +}); + +var callDisableTor = rpc.declare({ + object: 'luci.metablogizer', + method: 'disable_tor', + params: ['id'], + expect: { result: {} } +}); + +var callGetTorStatus = rpc.declare({ + object: 'luci.metablogizer', + method: 'get_tor_status', + params: ['id'], + expect: { result: {} } +}); + +var callRepairSite = rpc.declare({ + object: 'luci.metablogizer', + method: 'repair_site', + params: ['id'], + expect: { result: {} } +}); + +var callDiscoverVhosts = rpc.declare({ + object: 'luci.metablogizer', + method: 'discover_vhosts', + expect: { result: {} } +}); + +var callImportVhost = rpc.declare({ + object: 'luci.metablogizer', + method: 'import_vhost', + params: ['instance', 'name', 'domain'], + expect: { result: {} } +}); + +var callSyncConfig = rpc.declare({ + object: 'luci.metablogizer', + method: 'sync_config', + expect: { result: {} } +}); + +return baseclass.extend({ + getStatus: function() { + return callStatus(); + }, + + listSites: function() { + return callListSites().then(function(res) { + return res.sites || []; + }); + }, + + createSite: function(name, domain, giteaRepo, ssl, description) { + return callCreateSite(name, domain, giteaRepo || '', ssl || '1', description || ''); + }, + + updateSite: function(id, name, domain, giteaRepo, ssl, enabled, description) { + return callUpdateSite(id, name, domain, giteaRepo || '', ssl || '1', enabled || '1', description || ''); + }, + + deleteSite: function(id) { + return callDeleteSite(id); + }, + + syncSite: function(id) { + return callSyncSite(id); + }, + + getHostingStatus: function() { + return callGetHostingStatus(); + }, + + checkSiteHealth: function(id) { + return callCheckSiteHealth(id); + }, + + getPublishInfo: function(id) { + return callGetPublishInfo(id); + }, + + uploadFile: function(siteId, filename, content) { + return callUploadFile(siteId, filename, content); + }, + + listFiles: function(siteId) { + return callListFiles(siteId).then(function(res) { + return res.files || []; + }); + }, + + getSettings: function() { + return callGetSettings(); + }, + + enableTor: function(id) { + return callEnableTor(id); + }, + + disableTor: function(id) { + return callDisableTor(id); + }, + + getTorStatus: function(id) { + return callGetTorStatus(id); + }, + + repairSite: function(id) { + return callRepairSite(id); + }, + + discoverVhosts: function() { + return callDiscoverVhosts().then(function(res) { + return res.discovered || []; + }); + }, + + importVhost: function(instance, name, domain) { + return callImportVhost(instance, name, domain); + }, + + syncConfig: function() { + return callSyncConfig(); + }, + + getDashboardData: function() { + var self = this; + return Promise.all([ + self.getStatus(), + self.listSites(), + self.getHostingStatus().catch(function() { return {}; }) + ]).then(function(results) { + return { + status: results[0] || {}, + sites: results[1] || [], + hosting: results[2] || {} + }; + }); + } +}); diff --git a/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/view/metablogizer/dashboard.js b/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/view/metablogizer/dashboard.js new file mode 100644 index 00000000..8fe8d26b --- /dev/null +++ b/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/view/metablogizer/dashboard.js @@ -0,0 +1,695 @@ +'use strict'; +'require view'; +'require ui'; +'require fs'; +'require metablogizer.api as api'; +'require metablogizer/qrcode as qrcode'; + +return view.extend({ + status: {}, + sites: [], + hosting: {}, + uploadFiles: [], + currentSite: null, + + load: function() { + var self = this; + return api.getDashboardData().then(function(data) { + self.status = data.status || {}; + self.sites = data.sites || []; + self.hosting = data.hosting || {}; + }); + }, + + render: function() { + var self = this; + var status = this.status; + var sites = this.sites; + var hosting = this.hosting; + + return E('div', { 'class': 'cbi-map' }, [ + E('h2', {}, _('MetaBlogizer')), + E('div', { 'class': 'cbi-map-descr' }, _('Static site publisher with HAProxy vhosts and SSL')), + + // Status Section + E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Status')), + E('table', { 'class': 'table' }, [ + E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td', 'style': 'width:200px' }, _('Runtime')), + E('td', { 'class': 'td' }, status.detected_runtime || 'uhttpd') + ]), + E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, _('HAProxy')), + E('td', { 'class': 'td' }, hosting.haproxy_status === 'running' ? + E('span', { 'style': 'color:#0a0' }, _('Running')) : + E('span', { 'style': 'color:#a00' }, _('Stopped'))) + ]), + E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, _('Public IP')), + E('td', { 'class': 'td' }, hosting.public_ip || '-') + ]), + E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, _('Sites')), + E('td', { 'class': 'td' }, String(sites.length)) + ]), + E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, _('Backends Running')), + E('td', { 'class': 'td' }, String(sites.filter(function(s) { return s.backend_running; }).length) + ' / ' + sites.length) + ]) + ]), + E('div', { 'style': 'margin-top:1em' }, [ + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(this, 'handleSyncConfig') + }, _('Sync Config')), + ' ', + E('span', { 'class': 'cbi-value-description' }, _('Update port/runtime info for all sites')) + ]) + ]), + + // Sites Section + E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Sites')), + sites.length > 0 ? this.renderSitesTable(sites) : E('div', { 'class': 'cbi-section-descr' }, _('No sites configured')) + ]), + + // Create Site Section + E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Create Site')), + E('div', { 'class': 'cbi-section-descr' }, _('Add a new static site with auto-configured HAProxy vhost and SSL')), + + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Site Name')), + E('div', { 'class': 'cbi-value-field' }, [ + E('input', { 'type': 'text', 'id': 'new-site-name', 'class': 'cbi-input-text', + 'placeholder': 'myblog' }), + E('div', { 'class': 'cbi-value-description' }, _('Lowercase letters, numbers, and hyphens only')) + ]) + ]), + + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Domain')), + E('div', { 'class': 'cbi-value-field' }, + E('input', { 'type': 'text', 'id': 'new-site-domain', 'class': 'cbi-input-text', + 'placeholder': 'blog.example.com' })) + ]), + + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Gitea Repository')), + E('div', { 'class': 'cbi-value-field' }, [ + E('input', { 'type': 'text', 'id': 'new-site-gitea', 'class': 'cbi-input-text', + 'placeholder': 'user/repo' }), + E('div', { 'class': 'cbi-value-description' }, _('Optional: Sync content from Gitea')) + ]) + ]), + + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Description')), + E('div', { 'class': 'cbi-value-field' }, + E('input', { 'type': 'text', 'id': 'new-site-desc', 'class': 'cbi-input-text', + 'placeholder': 'Short description (optional)' })) + ]), + + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('HTTPS')), + E('div', { 'class': 'cbi-value-field' }, + E('select', { 'id': 'new-site-ssl', 'class': 'cbi-input-select' }, [ + E('option', { 'value': '1', 'selected': true }, _('Enabled (ACME)')), + E('option', { 'value': '0' }, _('Disabled')) + ])) + ]), + + E('div', { 'class': 'cbi-page-actions' }, + E('button', { + 'class': 'cbi-button cbi-button-positive', + 'click': ui.createHandlerFn(this, 'handleCreateSite') + }, _('Create Site'))) + ]), + + // Hosting Status Section + this.renderHostingSection(hosting) + ]); + }, + + renderSitesTable: function(sites) { + var self = this; + return E('table', { 'class': 'table' }, [ + E('tr', { 'class': 'tr table-titles' }, [ + E('th', { 'class': 'th' }, _('Name')), + E('th', { 'class': 'th' }, _('Domain')), + E('th', { 'class': 'th' }, _('Port')), + E('th', { 'class': 'th' }, _('Backend')), + E('th', { 'class': 'th' }, _('Content')), + E('th', { 'class': 'th' }, _('Actions')) + ]) + ].concat(sites.map(function(site) { + var backendStatus = site.backend_running ? + E('span', { 'style': 'color:#0a0' }, _('Running')) : + E('span', { 'style': 'color:#a00' }, _('Stopped')); + var contentStatus = site.has_content ? + E('span', { 'style': 'color:#0a0' }, _('OK')) : + E('span', { 'style': 'color:#888' }, _('Empty')); + + return E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, [ + E('strong', {}, site.name), + site.runtime ? E('br') : '', + site.runtime ? E('small', { 'style': 'color:#888' }, site.runtime) : '' + ]), + E('td', { 'class': 'td' }, site.domain ? + E('a', { 'href': site.url || ('https://' + site.domain), 'target': '_blank' }, site.domain) : + E('em', {}, '-')), + E('td', { 'class': 'td' }, site.port ? String(site.port) : '-'), + E('td', { 'class': 'td' }, backendStatus), + E('td', { 'class': 'td' }, contentStatus), + E('td', { 'class': 'td' }, [ + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(self, 'showShareModal', site), + 'title': _('Share') + }, _('Share')), + ' ', + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(self, 'showUploadModal', site), + 'title': _('Upload') + }, _('Upload')), + ' ', + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(self, 'showFilesModal', site), + 'title': _('Files') + }, _('Files')), + ' ', + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(self, 'showEditModal', site), + 'title': _('Edit') + }, _('Edit')), + ' ', + site.gitea_repo ? E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(self, 'handleSync', site), + 'title': _('Sync') + }, _('Sync')) : '', + ' ', + E('button', { + 'class': 'cbi-button cbi-button-remove', + 'click': ui.createHandlerFn(self, 'handleDelete', site), + 'title': _('Delete') + }, _('Delete')) + ]) + ]); + }))); + }, + + renderHostingSection: function(hosting) { + var hostingSites = hosting.sites || []; + if (hostingSites.length === 0) return E('div'); + + return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Hosting Status')), + E('div', { 'class': 'cbi-section-descr' }, _('DNS, SSL certificates, and publish status for each site')), + E('table', { 'class': 'table' }, [ + E('tr', { 'class': 'tr table-titles' }, [ + E('th', { 'class': 'th' }, _('Site')), + E('th', { 'class': 'th' }, _('DNS')), + E('th', { 'class': 'th' }, _('IP')), + E('th', { 'class': 'th' }, _('Certificate')), + E('th', { 'class': 'th' }, _('Status')) + ]) + ].concat(hostingSites.map(function(site) { + return E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, E('strong', {}, site.name)), + E('td', { 'class': 'td' }, site.dns_status === 'ok' ? + E('span', { 'style': 'color:#0a0' }, 'OK') : + E('span', { 'style': 'color:#a00' }, site.dns_status || 'unknown')), + E('td', { 'class': 'td' }, site.dns_ip || '-'), + E('td', { 'class': 'td' }, site.cert_status === 'ok' ? + E('span', { 'style': 'color:#0a0' }, (site.cert_days || 0) + 'd') : + E('span', { 'style': 'color:#a00' }, site.cert_status || 'missing')), + E('td', { 'class': 'td' }, site.publish_status === 'published' ? + E('span', { 'style': 'color:#0a0' }, _('Published')) : + E('span', { 'style': 'color:#888' }, site.publish_status || 'pending')) + ]); + }))) + ]); + }, + + handleCreateSite: function() { + var self = this; + var name = document.getElementById('new-site-name').value.trim(); + var domain = document.getElementById('new-site-domain').value.trim(); + var gitea = document.getElementById('new-site-gitea').value.trim(); + var desc = document.getElementById('new-site-desc').value.trim(); + var ssl = document.getElementById('new-site-ssl').value; + + if (!name) { + ui.addNotification(null, E('p', _('Site name is required')), 'error'); + return; + } + if (!domain) { + ui.addNotification(null, E('p', _('Domain is required')), 'error'); + return; + } + if (!/^[a-z0-9-]+$/.test(name)) { + ui.addNotification(null, E('p', _('Invalid name format: use lowercase letters, numbers, and hyphens')), 'error'); + return; + } + + ui.showModal(_('Creating Site'), [ + E('p', { 'class': 'spinning' }, _('Setting up site and HAProxy vhost...')) + ]); + + api.createSite(name, domain, gitea, ssl, desc).then(function(r) { + ui.hideModal(); + if (r.success) { + ui.addNotification(null, E('p', _('Site created successfully'))); + self.showShareModal({ name: r.name || name, domain: r.domain || domain, url: r.url }); + setTimeout(function() { window.location.reload(); }, 500); + } else { + ui.addNotification(null, E('p', _('Failed: ') + (r.error || 'Unknown error')), 'error'); + } + }).catch(function(e) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }, + + showEditModal: function(site) { + var self = this; + ui.showModal(_('Edit Site: ') + site.name, [ + E('div', { 'class': 'cbi-section' }, [ + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Domain')), + E('div', { 'class': 'cbi-value-field' }, + E('input', { 'type': 'text', 'id': 'edit-site-domain', 'class': 'cbi-input-text', + 'value': site.domain || '' })) + ]), + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Gitea Repository')), + E('div', { 'class': 'cbi-value-field' }, + E('input', { 'type': 'text', 'id': 'edit-site-gitea', 'class': 'cbi-input-text', + 'value': site.gitea_repo || '', 'placeholder': 'user/repo' })) + ]), + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Description')), + E('div', { 'class': 'cbi-value-field' }, + E('input', { 'type': 'text', 'id': 'edit-site-desc', 'class': 'cbi-input-text', + 'value': site.description || '' })) + ]), + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('HTTPS')), + E('div', { 'class': 'cbi-value-field' }, + E('select', { 'id': 'edit-site-ssl', 'class': 'cbi-input-select' }, [ + E('option', { 'value': '1', 'selected': site.ssl !== false }, _('Enabled')), + E('option', { 'value': '0', 'selected': site.ssl === false }, _('Disabled')) + ])) + ]), + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Enabled')), + E('div', { 'class': 'cbi-value-field' }, + E('select', { 'id': 'edit-site-enabled', 'class': 'cbi-input-select' }, [ + E('option', { 'value': '1', 'selected': site.enabled !== false }, _('Yes')), + E('option', { 'value': '0', 'selected': site.enabled === false }, _('No')) + ])) + ]) + ]), + E('div', { 'class': 'right' }, [ + E('button', { 'class': 'cbi-button', 'click': ui.hideModal }, _('Cancel')), + ' ', + E('button', { 'class': 'cbi-button cbi-button-positive', 'click': function() { + var domain = document.getElementById('edit-site-domain').value.trim(); + var gitea = document.getElementById('edit-site-gitea').value.trim(); + var desc = document.getElementById('edit-site-desc').value.trim(); + var ssl = document.getElementById('edit-site-ssl').value; + var enabled = document.getElementById('edit-site-enabled').value; + + if (!domain) { + ui.addNotification(null, E('p', _('Domain required')), 'error'); + return; + } + + ui.hideModal(); + ui.showModal(_('Saving'), [E('p', { 'class': 'spinning' }, _('Updating site...'))]); + + api.updateSite(site.id, site.name, domain, gitea, ssl, enabled, desc).then(function(r) { + ui.hideModal(); + if (r.success) { + ui.addNotification(null, E('p', _('Site updated'))); + window.location.reload(); + } else { + ui.addNotification(null, E('p', _('Failed: ') + (r.error || 'Unknown')), 'error'); + } + }).catch(function(e) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }}, _('Save')) + ]) + ]); + }, + + showUploadModal: function(site) { + var self = this; + this.uploadFiles = []; + this.currentSite = site; + + var fileList = E('div', { 'id': 'upload-file-list', 'style': 'margin:1em 0; max-height:200px; overflow-y:auto' }); + + ui.showModal(_('Upload to: ') + site.name, [ + E('div', { 'class': 'cbi-section' }, [ + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Files')), + E('div', { 'class': 'cbi-value-field' }, [ + E('input', { 'type': 'file', 'id': 'upload-file-input', 'multiple': true, + 'change': function(e) { self.handleFileSelect(e, fileList); } }), + E('div', { 'class': 'cbi-value-description' }, _('Select HTML, CSS, JS, images, etc.')) + ]) + ]), + fileList, + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Set first HTML as index')), + E('div', { 'class': 'cbi-value-field' }, + E('input', { 'type': 'checkbox', 'id': 'upload-as-index', 'checked': true })) + ]), + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, ''), + E('div', { 'class': 'cbi-value-field cbi-value-description' }, + _('After upload, use Ctrl+Shift+R to refresh cached pages')) + ]) + ]), + E('div', { 'class': 'right' }, [ + E('button', { 'class': 'cbi-button', 'click': ui.hideModal }, _('Cancel')), + ' ', + E('button', { 'class': 'cbi-button cbi-button-positive', 'click': ui.createHandlerFn(this, 'handleUpload') }, _('Upload')) + ]) + ]); + }, + + handleFileSelect: function(e, listEl) { + var files = e.target.files; + for (var i = 0; i < files.length; i++) { + this.uploadFiles.push(files[i]); + } + this.updateFileList(listEl); + }, + + updateFileList: function(listEl) { + var self = this; + listEl.innerHTML = ''; + if (this.uploadFiles.length === 0) return; + + this.uploadFiles.forEach(function(f, i) { + var row = E('div', { 'style': 'display:flex; align-items:center; gap:0.5em; margin:0.25em 0; padding:0.25em; background:#f8f8f8; border-radius:4px' }, [ + E('span', { 'style': 'flex:1' }, f.name), + E('span', { 'style': 'color:#888; font-size:0.9em' }, self.formatSize(f.size)), + E('button', { 'class': 'cbi-button cbi-button-remove', 'style': 'padding:0.25em 0.5em', + 'click': function() { self.uploadFiles.splice(i, 1); self.updateFileList(listEl); } }, 'X') + ]); + listEl.appendChild(row); + }); + }, + + handleUpload: function() { + var self = this; + if (!this.uploadFiles.length) { + ui.addNotification(null, E('p', _('No files selected')), 'error'); + return; + } + + var site = this.currentSite; + var asIndex = document.getElementById('upload-as-index').checked; + var firstHtml = null; + + if (asIndex) { + for (var i = 0; i < this.uploadFiles.length; i++) { + if (this.uploadFiles[i].name.endsWith('.html')) { + firstHtml = this.uploadFiles[i]; + break; + } + } + } + + ui.hideModal(); + ui.showModal(_('Uploading'), [E('p', { 'class': 'spinning' }, _('Uploading files...'))]); + + var sitesRoot = '/srv/metablogizer/sites'; + Promise.all(this.uploadFiles.map(function(f) { + return new Promise(function(resolve) { + var reader = new FileReader(); + reader.onload = function(e) { + var dest = (asIndex && f === firstHtml) ? 'index.html' : f.name; + fs.write(sitesRoot + '/' + site.name + '/' + dest, e.target.result) + .then(function() { resolve({ ok: true, name: f.name }); }) + .catch(function() { resolve({ ok: false, name: f.name }); }); + }; + reader.onerror = function() { resolve({ ok: false, name: f.name }); }; + reader.readAsText(f); + }); + })).then(function(results) { + ui.hideModal(); + var ok = results.filter(function(r) { return r.ok; }).length; + ui.addNotification(null, E('p', ok + _(' file(s) uploaded successfully'))); + self.uploadFiles = []; + }); + }, + + showFilesModal: function(site) { + var self = this; + this.currentSite = site; + var sitesRoot = '/srv/metablogizer/sites'; + + ui.showModal(_('Files: ') + site.name, [ + E('div', { 'id': 'files-list' }, [ + E('p', { 'class': 'spinning' }, _('Loading files...')) + ]), + E('div', { 'class': 'right' }, [ + E('button', { 'class': 'cbi-button', 'click': ui.hideModal }, _('Close')) + ]) + ]); + + fs.list(sitesRoot + '/' + site.name).then(function(files) { + var container = document.getElementById('files-list'); + container.innerHTML = ''; + + if (!files || !files.length) { + container.appendChild(E('p', { 'style': 'color:#888' }, _('No files'))); + return; + } + + var table = E('table', { 'class': 'table' }, [ + E('tr', { 'class': 'tr table-titles' }, [ + E('th', { 'class': 'th' }, _('File')), + E('th', { 'class': 'th' }, _('Size')), + E('th', { 'class': 'th' }, _('Actions')) + ]) + ]); + + files.forEach(function(f) { + if (f.type !== 'file') return; + var isIndex = f.name === 'index.html'; + table.appendChild(E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td' }, [ + isIndex ? E('strong', {}, f.name + ' (homepage)') : f.name + ]), + E('td', { 'class': 'td' }, self.formatSize(f.size)), + E('td', { 'class': 'td' }, [ + (!isIndex && f.name.endsWith('.html')) ? E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': function() { self.setAsHomepage(site, f.name); } + }, _('Set as Homepage')) : '', + ' ', + E('button', { + 'class': 'cbi-button cbi-button-remove', + 'click': function() { self.deleteFile(site, f.name); } + }, _('Delete')) + ]) + ])); + }); + + container.appendChild(table); + }).catch(function(e) { + var container = document.getElementById('files-list'); + container.innerHTML = ''; + container.appendChild(E('p', { 'style': 'color:#a00' }, _('Error: ') + e.message)); + }); + }, + + setAsHomepage: function(site, filename) { + var sitesRoot = '/srv/metablogizer/sites'; + var path = sitesRoot + '/' + site.name; + + ui.showModal(_('Setting Homepage'), [E('p', { 'class': 'spinning' }, _('Renaming...'))]); + + fs.read(path + '/' + filename).then(function(content) { + return fs.write(path + '/index.html', content); + }).then(function() { + return fs.remove(path + '/' + filename); + }).then(function() { + ui.hideModal(); + ui.addNotification(null, E('p', filename + _(' set as homepage'))); + }).catch(function(e) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }, + + deleteFile: function(site, filename) { + var self = this; + var sitesRoot = '/srv/metablogizer/sites'; + + if (!confirm(_('Delete ') + filename + '?')) return; + + fs.remove(sitesRoot + '/' + site.name + '/' + filename).then(function() { + ui.addNotification(null, E('p', _('File deleted'))); + self.showFilesModal(site); + }).catch(function(e) { + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }, + + showShareModal: function(site) { + var self = this; + var url = site.url || ('https://' + site.domain); + var title = site.name + ' - SecuBox'; + var enc = encodeURIComponent; + + var qrSvg = ''; + try { + qrSvg = qrcode.generateSVG(url, 180); + } catch (e) { + qrSvg = '

QR code unavailable

'; + } + + ui.showModal(_('Share: ') + site.name, [ + E('div', { 'style': 'text-align:center' }, [ + E('div', { 'class': 'cbi-value', 'style': 'display:flex; gap:0.5em; margin-bottom:1em' }, [ + E('input', { 'type': 'text', 'readonly': true, 'value': url, 'id': 'share-url', + 'class': 'cbi-input-text', 'style': 'flex:1' }), + E('button', { 'class': 'cbi-button cbi-button-action', 'click': function() { + self.copyToClipboard(url); + }}, _('Copy')) + ]), + E('div', { 'style': 'display:inline-block; padding:1em; background:#f8f8f8; border-radius:8px; margin:1em 0' }, [ + E('div', { 'innerHTML': qrSvg }) + ]), + E('div', { 'style': 'margin-top:1em' }, [ + E('p', { 'style': 'margin-bottom:0.5em' }, _('Share on:')), + E('div', { 'style': 'display:flex; gap:0.5em; justify-content:center; flex-wrap:wrap' }, [ + E('a', { 'href': 'https://twitter.com/intent/tweet?url=' + enc(url) + '&text=' + enc(title), + 'target': '_blank', 'class': 'cbi-button cbi-button-action' }, 'Twitter'), + E('a', { 'href': 'https://www.linkedin.com/sharing/share-offsite/?url=' + enc(url), + 'target': '_blank', 'class': 'cbi-button cbi-button-action' }, 'LinkedIn'), + E('a', { 'href': 'https://t.me/share/url?url=' + enc(url) + '&text=' + enc(title), + 'target': '_blank', 'class': 'cbi-button cbi-button-action' }, 'Telegram'), + E('a', { 'href': 'https://wa.me/?text=' + enc(title + ' ' + url), + 'target': '_blank', 'class': 'cbi-button cbi-button-action' }, 'WhatsApp'), + E('a', { 'href': 'mailto:?subject=' + enc(title) + '&body=' + enc(url), + 'class': 'cbi-button cbi-button-action' }, 'Email') + ]) + ]) + ]), + E('div', { 'class': 'right', 'style': 'margin-top:1em' }, [ + E('a', { 'href': url, 'target': '_blank', 'class': 'cbi-button cbi-button-positive', + 'style': 'text-decoration:none' }, _('Visit Site')), + ' ', + E('button', { 'class': 'cbi-button', 'click': ui.hideModal }, _('Close')) + ]) + ]); + }, + + handleSync: function(site) { + ui.showModal(_('Syncing'), [E('p', { 'class': 'spinning' }, _('Pulling from Gitea...'))]); + + api.syncSite(site.id).then(function(r) { + ui.hideModal(); + if (r.success) { + ui.addNotification(null, E('p', _('Site synced successfully'))); + } else { + ui.addNotification(null, E('p', _('Sync failed: ') + (r.error || 'Unknown')), 'error'); + } + }).catch(function(e) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }, + + handleDelete: function(site) { + var self = this; + ui.showModal(_('Delete Site'), [ + E('p', {}, _('Are you sure you want to delete "') + site.name + '"?'), + E('p', { 'style': 'color:#a00' }, _('This will remove the site, HAProxy vhost, and all files.')), + E('div', { 'class': 'right', 'style': 'margin-top:1em' }, [ + E('button', { 'class': 'cbi-button', 'click': ui.hideModal }, _('Cancel')), + ' ', + E('button', { 'class': 'cbi-button cbi-button-remove', 'click': function() { + ui.hideModal(); + ui.showModal(_('Deleting'), [E('p', { 'class': 'spinning' }, _('Removing site...'))]); + + api.deleteSite(site.id).then(function(r) { + ui.hideModal(); + if (r.success) { + ui.addNotification(null, E('p', _('Site deleted'))); + window.location.reload(); + } else { + ui.addNotification(null, E('p', _('Failed: ') + (r.error || 'Unknown')), 'error'); + } + }).catch(function(e) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }}, _('Delete')) + ]) + ]); + }, + + copyToClipboard: function(text) { + if (navigator.clipboard) { + navigator.clipboard.writeText(text).then(function() { + ui.addNotification(null, E('p', _('URL copied to clipboard'))); + }); + } else { + var input = document.getElementById('share-url'); + if (input) { + input.select(); + document.execCommand('copy'); + ui.addNotification(null, E('p', _('URL copied to clipboard'))); + } + } + }, + + handleSyncConfig: function() { + ui.showModal(_('Syncing Configuration'), [ + E('p', { 'class': 'spinning' }, _('Updating port and runtime info for all sites...')) + ]); + + api.syncConfig().then(function(r) { + ui.hideModal(); + if (r.success) { + var msg = _('Configuration synced'); + if (r.fixed > 0) { + msg += ' (' + r.fixed + _(' entries updated)'); + } + ui.addNotification(null, E('p', msg)); + window.location.reload(); + } else { + ui.addNotification(null, E('p', _('Sync failed: ') + (r.error || 'Unknown')), 'error'); + } + }).catch(function(e) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); + }); + }, + + formatSize: function(bytes) { + if (bytes < 1024) return bytes + ' B'; + if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'; + return (bytes / 1048576).toFixed(1) + ' MB'; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/view/metablogizer/overview.js b/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/view/metablogizer/overview.js deleted file mode 100644 index 429741fe..00000000 --- a/package/secubox/luci-app-metablogizer/htdocs/luci-static/resources/view/metablogizer/overview.js +++ /dev/null @@ -1,396 +0,0 @@ -'use strict'; -'require view'; -'require ui'; -'require rpc'; -'require fs'; -'require metablogizer/qrcode as qrcode'; - -var callStatus = rpc.declare({ object: 'luci.metablogizer', method: 'status', expect: {} }); -var callListSites = rpc.declare({ object: 'luci.metablogizer', method: 'list_sites', expect: { sites: [] } }); -var callCreateSite = rpc.declare({ object: 'luci.metablogizer', method: 'create_site', params: ['name', 'domain', 'gitea_repo', 'ssl', 'description'], expect: {} }); -var callUpdateSite = rpc.declare({ object: 'luci.metablogizer', method: 'update_site', params: ['id', 'name', 'domain', 'gitea_repo', 'ssl', 'enabled', 'description'], expect: {} }); -var callDeleteSite = rpc.declare({ object: 'luci.metablogizer', method: 'delete_site', params: ['id'], expect: {} }); -var callSyncSite = rpc.declare({ object: 'luci.metablogizer', method: 'sync_site', params: ['id'], expect: {} }); -var callGetHostingStatus = rpc.declare({ object: 'luci.metablogizer', method: 'get_hosting_status', expect: {} }); -var callCheckSiteHealth = rpc.declare({ object: 'luci.metablogizer', method: 'check_site_health', params: ['id'], expect: {} }); - -var SITES_ROOT = '/srv/metablogizer/sites'; - -var styles = '.mb-container{max-width:1200px;margin:0 auto}.mb-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.5rem;padding:1rem;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);border-radius:12px;color:#fff}.mb-header h2{margin:0;font-size:1.5rem}.mb-status-pills{display:flex;gap:.75rem}.mb-pill{padding:.4rem .8rem;border-radius:20px;font-size:.85rem;background:rgba(255,255,255,.2)}.mb-pill.active{background:rgba(255,255,255,.95);color:#667eea}.mb-hosting-panel{background:#fff;border-radius:12px;padding:1.25rem;margin-bottom:1.5rem;box-shadow:0 2px 12px rgba(0,0,0,.08);border:1px solid #e8e8e8}.mb-hosting-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem}.mb-hosting-header h3{margin:0;font-size:1.1rem;color:#333}.mb-hosting-ip{background:#f8f9fa;padding:.5rem 1rem;border-radius:8px;font-family:monospace;font-size:.9rem}.mb-hosting-table{width:100%;border-collapse:collapse}.mb-hosting-table th,.mb-hosting-table td{padding:.6rem .8rem;text-align:left;border-bottom:1px solid #eee}.mb-hosting-table th{font-weight:600;color:#666;font-size:.85rem;text-transform:uppercase}.mb-hosting-table td{font-size:.9rem}.mb-status-badge{display:inline-flex;align-items:center;gap:.35rem;padding:.25rem .6rem;border-radius:12px;font-size:.75rem;font-weight:500}.mb-status-ok{background:#d4edda;color:#155724}.mb-status-warning{background:#fff3cd;color:#856404}.mb-status-error{background:#f8d7da;color:#721c24}.mb-status-pending{background:#cfe2ff;color:#084298}.mb-status-none{background:#e9ecef;color:#6c757d}.mb-dns-ip{font-family:monospace;font-size:.85rem;color:#666}.mb-btn-primary{background:#fff;color:#667eea;border:none;padding:.6rem 1.2rem;border-radius:8px;cursor:pointer;font-weight:600;transition:transform .2s}.mb-btn-primary:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,.15)}.mb-sites-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:1.25rem}.mb-site-card{background:#fff;border-radius:12px;padding:1.25rem;box-shadow:0 2px 12px rgba(0,0,0,.08);border:1px solid #e8e8e8;transition:transform .2s}.mb-site-card:hover{transform:translateY(-4px);box-shadow:0 8px 24px rgba(0,0,0,.12)}.mb-site-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:.75rem}.mb-site-name{font-size:1.15rem;font-weight:600;color:#333;margin:0}.mb-site-status{padding:.25rem .6rem;border-radius:12px;font-size:.75rem;font-weight:500}.mb-site-status.online{background:#d4edda;color:#155724}.mb-site-status.offline{background:#f8d7da;color:#721c24}.mb-site-domain{color:#667eea;font-size:.9rem;margin-bottom:.5rem;word-break:break-all}.mb-site-domain a{color:inherit;text-decoration:none}.mb-site-domain a:hover{text-decoration:underline}.mb-site-meta{font-size:.8rem;color:#888;margin-bottom:1rem}.mb-site-actions{display:flex;gap:.4rem;flex-wrap:wrap}.mb-btn{padding:.35rem .6rem;border-radius:6px;border:1px solid #ddd;background:#f8f9fa;cursor:pointer;font-size:.8rem;transition:all .2s}.mb-btn:hover{background:#e9ecef}.mb-btn-share{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;border:none}.mb-btn-upload{background:#17a2b8;color:#fff;border:none}.mb-btn-upload:hover{background:#138496}.mb-btn-files{background:#6c757d;color:#fff;border:none}.mb-btn-files:hover{background:#5a6268}.mb-btn-edit{background:#fd7e14;color:#fff;border:none}.mb-btn-edit:hover{background:#e96b02}.mb-btn-sync{background:#28a745;color:#fff;border:none}.mb-btn-sync:hover{background:#218838}.mb-btn-delete,.mb-btn-danger{background:#dc3545;color:#fff;border:none}.mb-btn-delete:hover,.mb-btn-danger:hover{background:#c82333}.mb-empty-state{text-align:center;padding:4rem 2rem;background:#fff;border-radius:12px;border:2px dashed #ddd}.mb-empty-state h3{color:#666;margin-bottom:.5rem}.mb-empty-state p{color:#888;margin-bottom:1.5rem}.mb-modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);display:flex;justify-content:center;align-items:center;z-index:10000}.mb-modal{background:#fff;border-radius:16px;max-width:500px;width:90%;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,.3)}.mb-modal-header{padding:1.25rem;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}.mb-modal-header h3{margin:0;color:#333}.mb-modal-close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:#888;padding:0;line-height:1}.mb-modal-close:hover{color:#333}.mb-modal-body{padding:1.25rem}.mb-form-group{margin-bottom:1rem}.mb-form-group label{display:block;margin-bottom:.4rem;font-weight:500;color:#333;font-size:.9rem}.mb-form-group input,.mb-form-group textarea{width:100%;padding:.6rem .8rem;border:1px solid #ddd;border-radius:8px;font-size:.95rem;box-sizing:border-box}.mb-form-group input:focus,.mb-form-group textarea:focus{border-color:#667eea;outline:none}.mb-form-group textarea{resize:vertical;min-height:60px}.mb-form-group small{color:#888;font-size:.8rem}.mb-form-checkbox{display:flex;align-items:center;gap:.5rem}.mb-form-checkbox input{width:auto}.mb-modal-footer{padding:1rem 1.25rem;border-top:1px solid #eee;display:flex;justify-content:flex-end;gap:.75rem}.mb-btn-cancel{background:#f8f9fa;color:#333}.mb-btn-submit{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;border:none;font-weight:600}.mb-published-card{text-align:center}.mb-url-box{display:flex;gap:.5rem;margin-bottom:1.25rem}.mb-url-box input{flex:1;padding:.6rem;border:1px solid #ddd;border-radius:8px;font-family:monospace;font-size:.9rem;background:#f8f9fa}.mb-url-box button{padding:.6rem 1rem;border:none;background:#667eea;color:#fff;border-radius:8px;cursor:pointer}.mb-qr-container{margin:1.25rem 0;padding:1rem;background:#f8f9fa;border-radius:12px;display:inline-block}.mb-share-buttons{display:flex;justify-content:center;gap:.75rem;flex-wrap:wrap;margin-top:1.25rem}.mb-share-btn{width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;text-decoration:none;color:#fff;font-weight:700;font-size:1.1rem;transition:transform .2s}.mb-share-btn:hover{transform:scale(1.1)}.mb-share-twitter{background:#1da1f2}.mb-share-linkedin{background:#0077b5}.mb-share-facebook{background:#1877f2}.mb-share-telegram{background:#0088cc}.mb-share-whatsapp{background:#25d366}.mb-share-email{background:#666}.mb-dropzone{border:2px dashed #ddd;border-radius:12px;padding:2rem;text-align:center;margin-bottom:1rem;cursor:pointer}.mb-dropzone:hover,.mb-dropzone.dragover{border-color:#667eea;background:rgba(102,126,234,.05)}.mb-dropzone-icon{font-size:2.5rem;margin-bottom:.5rem}.mb-dropzone-text{color:#666}.mb-dropzone-text strong{color:#667eea}.mb-file-list{margin-top:1rem;max-height:200px;overflow-y:auto}.mb-file-item{display:flex;align-items:center;gap:.5rem;padding:.5rem;background:#f8f9fa;border-radius:6px;margin-bottom:.5rem;font-size:.85rem}.mb-file-item-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mb-file-item-size{color:#888;font-size:.8rem}.mb-file-item-actions{display:flex;gap:.25rem}.mb-file-item-btn{background:none;border:none;cursor:pointer;padding:.25rem;font-size:.9rem;opacity:.7}.mb-file-item-btn:hover{opacity:1}.mb-file-item-btn.delete{color:#dc3545}.mb-file-item-btn.home{color:#28a745}.mb-cache-hint{background:#fff3cd;border:1px solid #ffc107;border-radius:8px;padding:.75rem;margin-top:1rem;font-size:.85rem;color:#856404}@media(max-width:600px){.mb-header{flex-direction:column;gap:1rem}.mb-sites-grid{grid-template-columns:1fr}}'; - -return view.extend({ - uploadFiles: [], - currentSite: null, - - load: function() { return Promise.all([callStatus(), callListSites(), callGetHostingStatus().catch(function() { return {}; })]); }, - - render: function(data) { - var self = this, status = data[0] || {}, sites = data[1] || [], hosting = data[2] || {}; - if (!document.getElementById('mb-styles')) { - var s = document.createElement('style'); s.id = 'mb-styles'; s.textContent = styles; document.head.appendChild(s); - } - return E('div', { 'class': 'mb-container' }, [ - E('div', { 'class': 'mb-header' }, [ - E('div', {}, [ - E('h2', {}, _('MetaBlogizer')), - E('div', { 'class': 'mb-status-pills' }, [ - E('span', { 'class': 'mb-pill active' }, status.detected_runtime || 'uhttpd'), - E('span', { 'class': 'mb-pill' }, (status.site_count || sites.length || 0) + ' ' + _('Sites')), - E('span', { 'class': 'mb-pill' }, (hosting.haproxy_status === 'running' ? '\u{2705}' : '\u{1F534}') + ' HAProxy') - ]) - ]), - E('button', { 'class': 'mb-btn-primary', 'click': ui.createHandlerFn(this, 'showCreateModal') }, _('+ New Site')) - ]), - this.renderHostingPanel(hosting, sites), - sites.length > 0 ? E('div', { 'class': 'mb-sites-grid' }, sites.map(function(site) { return self.renderSiteCard(site); })) : - E('div', { 'class': 'mb-empty-state' }, [ - E('div', { 'style': 'font-size:3rem;margin-bottom:1rem' }, '\u{1F310}'), - E('h3', {}, _('No Sites Yet')), - E('p', {}, _('Create your first static site')), - E('button', { 'class': 'mb-btn-primary', 'style': 'background:linear-gradient(135deg,#667eea,#764ba2);color:#fff', 'click': ui.createHandlerFn(this, 'showCreateModal') }, _('Create Site')) - ]) - ]); - }, - - renderHostingPanel: function(hosting, sites) { - var hostingSites = hosting.sites || []; - if (hostingSites.length === 0) return E('div'); - - var statusBadge = function(status, text) { - var cls = 'mb-status-badge '; - switch (status) { - case 'ok': cls += 'mb-status-ok'; break; - case 'warning': cls += 'mb-status-warning'; break; - case 'error': case 'failed': case 'expired': case 'critical': case 'mismatch': cls += 'mb-status-error'; break; - case 'pending': case 'missing': cls += 'mb-status-pending'; break; - default: cls += 'mb-status-none'; - } - return E('span', { 'class': cls }, text || status); - }; - - var dnsIcon = function(status) { - switch (status) { - case 'ok': return '\u{1F310}'; - case 'private': return '\u{26A0}'; - case 'mismatch': return '\u{2757}'; - case 'failed': return '\u{274C}'; - default: return '\u{2796}'; - } - }; - - var certIcon = function(status) { - switch (status) { - case 'ok': return '\u{1F512}'; - case 'warning': return '\u{26A0}'; - case 'critical': case 'expired': return '\u{1F534}'; - case 'missing': return '\u{26AA}'; - default: return '\u{2796}'; - } - }; - - var publishIcon = function(status) { - switch (status) { - case 'published': return '\u{2705}'; - case 'pending': return '\u{1F551}'; - case 'draft': return '\u{1F4DD}'; - default: return '\u{2796}'; - } - }; - - return E('div', { 'class': 'mb-hosting-panel' }, [ - E('div', { 'class': 'mb-hosting-header' }, [ - E('h3', {}, '\u{1F310} ' + _('Web Hosting Status')), - hosting.public_ip ? E('div', { 'class': 'mb-hosting-ip' }, _('Public IP: ') + hosting.public_ip) : '' - ]), - E('table', { 'class': 'mb-hosting-table' }, [ - E('thead', {}, E('tr', {}, [ - E('th', {}, _('Site')), - E('th', {}, _('Domain')), - E('th', {}, _('DNS')), - E('th', {}, _('Resolved IP')), - E('th', {}, _('Certificate')), - E('th', {}, _('Status')) - ])), - E('tbody', {}, hostingSites.map(function(site) { - return E('tr', {}, [ - E('td', {}, E('strong', {}, site.name)), - E('td', {}, site.domain ? E('a', { 'href': site.url, 'target': '_blank' }, site.domain) : E('em', { 'style': 'color:#888' }, _('No domain'))), - E('td', {}, statusBadge(site.dns_status, dnsIcon(site.dns_status) + ' ' + (site.dns_status || 'none'))), - E('td', { 'class': 'mb-dns-ip' }, site.dns_ip || '-'), - E('td', {}, statusBadge(site.cert_status, certIcon(site.cert_status) + ' ' + (site.cert_days ? site.cert_days + 'd' : site.cert_status || 'none'))), - E('td', {}, statusBadge(site.publish_status, publishIcon(site.publish_status) + ' ' + (site.publish_status || 'draft'))) - ]); - })) - ]) - ]); - }, - - renderSiteCard: function(site) { - return E('div', { 'class': 'mb-site-card' }, [ - E('div', { 'class': 'mb-site-header' }, [ - E('h4', { 'class': 'mb-site-name' }, site.name), - E('span', { 'class': 'mb-site-status ' + (site.has_content ? 'online' : 'offline') }, site.has_content ? _('Published') : _('Pending')) - ]), - E('div', { 'class': 'mb-site-domain' }, [E('a', { 'href': site.url, 'target': '_blank' }, site.domain)]), - E('div', { 'class': 'mb-site-meta' }, site.last_sync ? _('Last sync: ') + site.last_sync : _('Not synced yet')), - E('div', { 'class': 'mb-site-actions' }, [ - E('button', { 'class': 'mb-btn mb-btn-share', 'click': ui.createHandlerFn(this, 'showShareModal', site) }, _('Share')), - E('button', { 'class': 'mb-btn mb-btn-upload', 'click': ui.createHandlerFn(this, 'showUploadModal', site) }, _('Upload')), - E('button', { 'class': 'mb-btn mb-btn-files', 'click': ui.createHandlerFn(this, 'showFilesModal', site) }, _('Files')), - E('button', { 'class': 'mb-btn mb-btn-edit', 'click': ui.createHandlerFn(this, 'showEditModal', site) }, _('Edit')), - E('button', { 'class': 'mb-btn mb-btn-sync', 'click': ui.createHandlerFn(this, 'handleSync', site) }, _('Sync')), - E('button', { 'class': 'mb-btn mb-btn-delete', 'click': ui.createHandlerFn(this, 'handleDelete', site) }, _('Delete')) - ]) - ]); - }, - - showCreateModal: function() { - var self = this; - var modal = E('div', { 'class': 'mb-modal-overlay', 'id': 'mb-create-modal' }, [ - E('div', { 'class': 'mb-modal' }, [ - E('div', { 'class': 'mb-modal-header' }, [E('h3', {}, _('Create New Site')), E('button', { 'class': 'mb-modal-close', 'click': function() { self.closeModal('mb-create-modal'); } }, '\u00D7')]), - E('div', { 'class': 'mb-modal-body' }, [ - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Site Name')), E('input', { 'type': 'text', 'id': 'mb-site-name', 'placeholder': 'myblog' }), E('small', {}, _('Lowercase, numbers, hyphens'))]), - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Domain')), E('input', { 'type': 'text', 'id': 'mb-site-domain', 'placeholder': 'blog.example.com' })]), - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Gitea Repository')), E('input', { 'type': 'text', 'id': 'mb-gitea-repo', 'placeholder': 'user/repo (optional)' })]), - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Description')), E('textarea', { 'id': 'mb-site-description', 'placeholder': 'Short description (optional)' })]), - E('div', { 'class': 'mb-form-group' }, [E('label', { 'class': 'mb-form-checkbox' }, [E('input', { 'type': 'checkbox', 'id': 'mb-site-ssl', 'checked': true }), E('span', {}, _('Enable HTTPS'))])]) - ]), - E('div', { 'class': 'mb-modal-footer' }, [ - E('button', { 'class': 'mb-btn mb-btn-cancel', 'click': function() { self.closeModal('mb-create-modal'); } }, _('Cancel')), - E('button', { 'class': 'mb-btn mb-btn-submit', 'click': ui.createHandlerFn(this, 'handleCreate') }, _('Create')) - ]) - ]) - ]); - document.body.appendChild(modal); - }, - - handleCreate: function() { - var self = this; - var name = document.getElementById('mb-site-name').value.trim(); - var domain = document.getElementById('mb-site-domain').value.trim(); - var gitea = document.getElementById('mb-gitea-repo').value.trim(); - var desc = document.getElementById('mb-site-description').value.trim(); - var ssl = document.getElementById('mb-site-ssl').checked ? '1' : '0'; - if (!name || !domain) { ui.addNotification(null, E('p', _('Name and domain required')), 'error'); return; } - if (!/^[a-z0-9-]+$/.test(name)) { ui.addNotification(null, E('p', _('Invalid name format')), 'error'); return; } - this.closeModal('mb-create-modal'); - ui.showModal(_('Creating...'), [E('p', { 'class': 'spinning' }, _('Setting up site...'))]); - callCreateSite(name, domain, gitea, ssl, desc).then(function(r) { - ui.hideModal(); - if (r.success) { self.showShareModal({ name: r.name, domain: r.domain, url: r.url }); setTimeout(function() { window.location.reload(); }, 100); } - else { ui.addNotification(null, E('p', _('Failed: ') + r.error), 'error'); } - }).catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); }); - }, - - showEditModal: function(site) { - var self = this; - var modal = E('div', { 'class': 'mb-modal-overlay', 'id': 'mb-edit-modal' }, [ - E('div', { 'class': 'mb-modal' }, [ - E('div', { 'class': 'mb-modal-header' }, [E('h3', {}, _('Edit: ') + site.name), E('button', { 'class': 'mb-modal-close', 'click': function() { self.closeModal('mb-edit-modal'); } }, '\u00D7')]), - E('div', { 'class': 'mb-modal-body' }, [ - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Site Name')), E('input', { 'type': 'text', 'id': 'mb-edit-name', 'value': site.name, 'readonly': true, 'style': 'background:#eee' }), E('small', {}, _('Cannot be changed'))]), - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Domain')), E('input', { 'type': 'text', 'id': 'mb-edit-domain', 'value': site.domain || '' })]), - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Gitea Repository')), E('input', { 'type': 'text', 'id': 'mb-edit-gitea', 'value': site.gitea_repo || '', 'placeholder': 'user/repo' })]), - E('div', { 'class': 'mb-form-group' }, [E('label', {}, _('Description')), E('textarea', { 'id': 'mb-edit-description' }, site.description || '')]), - E('div', { 'class': 'mb-form-group' }, [E('label', { 'class': 'mb-form-checkbox' }, [E('input', { 'type': 'checkbox', 'id': 'mb-edit-ssl', 'checked': site.ssl }), E('span', {}, _('Enable HTTPS'))])]), - E('div', { 'class': 'mb-form-group' }, [E('label', { 'class': 'mb-form-checkbox' }, [E('input', { 'type': 'checkbox', 'id': 'mb-edit-enabled', 'checked': site.enabled !== false }), E('span', {}, _('Site enabled'))])]) - ]), - E('div', { 'class': 'mb-modal-footer' }, [ - E('button', { 'class': 'mb-btn mb-btn-cancel', 'click': function() { self.closeModal('mb-edit-modal'); } }, _('Cancel')), - E('button', { 'class': 'mb-btn mb-btn-submit', 'click': ui.createHandlerFn(this, 'handleUpdate', site) }, _('Save')) - ]) - ]) - ]); - document.body.appendChild(modal); - }, - - handleUpdate: function(site) { - var domain = document.getElementById('mb-edit-domain').value.trim(); - var gitea = document.getElementById('mb-edit-gitea').value.trim(); - var desc = document.getElementById('mb-edit-description').value.trim(); - var ssl = document.getElementById('mb-edit-ssl').checked ? '1' : '0'; - var enabled = document.getElementById('mb-edit-enabled').checked ? '1' : '0'; - if (!domain) { ui.addNotification(null, E('p', _('Domain required')), 'error'); return; } - this.closeModal('mb-edit-modal'); - ui.showModal(_('Saving...'), [E('p', { 'class': 'spinning' }, _('Updating...'))]); - callUpdateSite(site.id, site.name, domain, gitea, ssl, enabled, desc).then(function(r) { - ui.hideModal(); - if (r.success) { ui.addNotification(null, E('p', _('Site updated'))); window.location.reload(); } - else { ui.addNotification(null, E('p', _('Failed: ') + r.error), 'error'); } - }).catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); }); - }, - - showUploadModal: function(site) { - var self = this; this.uploadFiles = []; this.currentSite = site; - var modal = E('div', { 'class': 'mb-modal-overlay', 'id': 'mb-upload-modal' }, [ - E('div', { 'class': 'mb-modal' }, [ - E('div', { 'class': 'mb-modal-header' }, [E('h3', {}, _('Upload to ') + site.name), E('button', { 'class': 'mb-modal-close', 'click': function() { self.closeModal('mb-upload-modal'); } }, '\u00D7')]), - E('div', { 'class': 'mb-modal-body' }, [ - E('div', { 'class': 'mb-dropzone', 'id': 'mb-dropzone', 'click': function() { document.getElementById('mb-file-input').click(); } }, [ - E('div', { 'class': 'mb-dropzone-icon' }, '\u{1F4C1}'), - E('div', { 'class': 'mb-dropzone-text' }, [E('strong', {}, _('Drop files here')), E('br'), _('or click to browse')]) - ]), - E('input', { 'type': 'file', 'id': 'mb-file-input', 'multiple': true, 'style': 'display:none', 'change': function(e) { self.handleFileSelect(e); } }), - E('div', { 'class': 'mb-file-list', 'id': 'mb-file-list' }), - E('div', { 'class': 'mb-form-group', 'style': 'margin-top:1rem' }, [E('label', { 'class': 'mb-form-checkbox' }, [E('input', { 'type': 'checkbox', 'id': 'mb-as-index', 'checked': true }), E('span', {}, _('Set first HTML as homepage'))])]), - E('div', { 'class': 'mb-cache-hint' }, _('After upload, Ctrl+Shift+R to refresh.')) - ]), - E('div', { 'class': 'mb-modal-footer' }, [ - E('button', { 'class': 'mb-btn mb-btn-cancel', 'click': function() { self.closeModal('mb-upload-modal'); } }, _('Cancel')), - E('button', { 'class': 'mb-btn mb-btn-submit', 'click': ui.createHandlerFn(this, 'handleUpload') }, _('Upload')) - ]) - ]) - ]); - document.body.appendChild(modal); - this.setupDropzone('mb-dropzone'); - }, - - handleUpload: function() { - var self = this; - if (!this.uploadFiles.length) { ui.addNotification(null, E('p', _('No files')), 'error'); return; } - var name = this.currentSite.name, asIndex = document.getElementById('mb-as-index').checked, firstHtml = null; - if (asIndex) { for (var i = 0; i < this.uploadFiles.length; i++) { if (this.uploadFiles[i].name.endsWith('.html')) { firstHtml = this.uploadFiles[i]; break; } } } - this.closeModal('mb-upload-modal'); - ui.showModal(_('Uploading...'), [E('p', { 'class': 'spinning' }, _('Uploading...'))]); - Promise.all(this.uploadFiles.map(function(f) { - return new Promise(function(resolve) { - var reader = new FileReader(); - reader.onload = function(e) { - var dest = (asIndex && f === firstHtml) ? 'index.html' : f.name; - fs.write(SITES_ROOT + '/' + name + '/' + dest, e.target.result).then(function() { resolve({ ok: true }); }).catch(function() { resolve({ ok: false }); }); - }; - reader.onerror = function() { resolve({ ok: false }); }; - reader.readAsText(f); - }); - })).then(function(r) { - ui.hideModal(); - var ok = r.filter(function(x) { return x.ok; }).length; - ui.addNotification(null, E('p', ok + ' file(s) uploaded')); - self.uploadFiles = []; - }); - }, - - showFilesModal: function(site) { - var self = this; this.currentSite = site; - var modal = E('div', { 'class': 'mb-modal-overlay', 'id': 'mb-files-modal' }, [ - E('div', { 'class': 'mb-modal' }, [ - E('div', { 'class': 'mb-modal-header' }, [E('h3', {}, _('Files: ') + site.name), E('button', { 'class': 'mb-modal-close', 'click': function() { self.closeModal('mb-files-modal'); } }, '\u00D7')]), - E('div', { 'class': 'mb-modal-body' }, [E('div', { 'id': 'mb-files-list', 'class': 'mb-file-list' }, [E('p', { 'class': 'spinning' }, _('Loading...'))])]), - E('div', { 'class': 'mb-modal-footer' }, [E('button', { 'class': 'mb-btn', 'click': function() { self.closeModal('mb-files-modal'); } }, _('Close'))]) - ]) - ]); - document.body.appendChild(modal); - fs.list(SITES_ROOT + '/' + site.name).then(function(files) { - var c = document.getElementById('mb-files-list'); c.innerHTML = ''; - if (!files || !files.length) { c.appendChild(E('p', { 'style': 'color:#888;text-align:center' }, _('No files'))); return; } - files.forEach(function(f) { - if (f.type === 'file') { - var isIdx = f.name === 'index.html'; - c.appendChild(E('div', { 'class': 'mb-file-item' }, [ - E('span', {}, isIdx ? '\u{1F3E0}' : '\u{1F4C4}'), - E('span', { 'class': 'mb-file-item-name' }, f.name + (isIdx ? ' (homepage)' : '')), - E('span', { 'class': 'mb-file-item-size' }, self.formatFileSize(f.size)), - E('span', { 'class': 'mb-file-item-actions' }, [ - (!isIdx && f.name.endsWith('.html')) ? E('button', { 'class': 'mb-file-item-btn home', 'title': _('Set as homepage'), 'click': function() { self.setAsHomepage(site, f.name); } }, '\u{1F3E0}') : '', - E('button', { 'class': 'mb-file-item-btn delete', 'title': _('Delete'), 'click': function() { self.deleteFile(site, f.name); } }, '\u{1F5D1}') - ]) - ])); - } - }); - }).catch(function(e) { document.getElementById('mb-files-list').innerHTML = '

Error: ' + e.message + '

'; }); - }, - - setAsHomepage: function(site, fname) { - var self = this, path = SITES_ROOT + '/' + site.name; - ui.showModal(_('Setting...'), [E('p', { 'class': 'spinning' }, _('Renaming...'))]); - fs.read(path + '/' + fname).then(function(c) { return fs.write(path + '/index.html', c); }).then(function() { return fs.remove(path + '/' + fname); }).then(function() { - ui.hideModal(); ui.addNotification(null, E('p', fname + ' set as homepage')); self.closeModal('mb-files-modal'); - }).catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); }); - }, - - deleteFile: function(site, fname) { - var self = this; - if (!confirm(_('Delete ') + fname + '?')) return; - fs.remove(SITES_ROOT + '/' + site.name + '/' + fname).then(function() { ui.addNotification(null, E('p', _('Deleted'))); self.showFilesModal(site); }).catch(function(e) { ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); }); - }, - - showShareModal: function(site) { - var self = this, url = site.url || ('https://' + site.domain), title = site.name + ' - SecuBox', enc = encodeURIComponent, qr = qrcode.generateSVG(url, 180); - var modal = E('div', { 'class': 'mb-modal-overlay', 'id': 'mb-share-modal' }, [ - E('div', { 'class': 'mb-modal' }, [ - E('div', { 'class': 'mb-modal-header' }, [E('h3', {}, '\u{2705} ' + site.name), E('button', { 'class': 'mb-modal-close', 'click': function() { self.closeModal('mb-share-modal'); } }, '\u00D7')]), - E('div', { 'class': 'mb-modal-body' }, [ - E('div', { 'class': 'mb-published-card' }, [ - E('div', { 'class': 'mb-url-box' }, [E('input', { 'type': 'text', 'readonly': true, 'value': url, 'id': 'mb-url' }), E('button', { 'click': function() { self.copyUrl(url); } }, '\u{1F4CB}')]), - E('div', { 'class': 'mb-qr-container' }, [E('div', { 'innerHTML': qr || 'QR unavailable' })]), - E('div', { 'class': 'mb-share-buttons' }, [ - E('a', { 'href': 'https://twitter.com/intent/tweet?url=' + enc(url) + '&text=' + enc(title), 'target': '_blank', 'class': 'mb-share-btn mb-share-twitter' }, '\u{1D54F}'), - E('a', { 'href': 'https://www.linkedin.com/sharing/share-offsite/?url=' + enc(url), 'target': '_blank', 'class': 'mb-share-btn mb-share-linkedin' }, 'in'), - E('a', { 'href': 'https://www.facebook.com/sharer/sharer.php?u=' + enc(url), 'target': '_blank', 'class': 'mb-share-btn mb-share-facebook' }, 'f'), - E('a', { 'href': 'https://t.me/share/url?url=' + enc(url) + '&text=' + enc(title), 'target': '_blank', 'class': 'mb-share-btn mb-share-telegram' }, '\u{2708}'), - E('a', { 'href': 'https://wa.me/?text=' + enc(title + ' ' + url), 'target': '_blank', 'class': 'mb-share-btn mb-share-whatsapp' }, '\u{260E}'), - E('a', { 'href': 'mailto:?subject=' + enc(title) + '&body=' + enc(url), 'class': 'mb-share-btn mb-share-email' }, '\u{2709}') - ]) - ]) - ]), - E('div', { 'class': 'mb-modal-footer' }, [ - E('a', { 'href': url, 'target': '_blank', 'class': 'mb-btn mb-btn-submit', 'style': 'text-decoration:none' }, _('Visit')), - E('button', { 'class': 'mb-btn', 'click': function() { self.closeModal('mb-share-modal'); } }, _('Close')) - ]) - ]) - ]); - document.body.appendChild(modal); - }, - - handleSync: function(site) { - ui.showModal(_('Syncing...'), [E('p', { 'class': 'spinning' }, _('Pulling...'))]); - callSyncSite(site.id).then(function(r) { ui.hideModal(); ui.addNotification(null, E('p', r.success ? _('Synced') : _('Failed: ') + r.error), r.success ? null : 'error'); }).catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); }); - }, - - handleDelete: function(site) { - ui.showModal(_('Delete?'), [ - E('p', {}, _('Delete "') + site.name + '"?'), - E('p', { 'style': 'color:#dc3545' }, _('This removes site, vhost, and files.')), - E('div', { 'style': 'display:flex;gap:1rem;justify-content:flex-end;margin-top:1rem' }, [ - E('button', { 'class': 'mb-btn', 'click': ui.hideModal }, _('Cancel')), - E('button', { 'class': 'mb-btn mb-btn-danger', 'click': function() { - ui.hideModal(); ui.showModal(_('Deleting...'), [E('p', { 'class': 'spinning' }, _('Removing...'))]); - callDeleteSite(site.id).then(function(r) { ui.hideModal(); if (r.success) { ui.addNotification(null, E('p', _('Deleted'))); window.location.reload(); } else { ui.addNotification(null, E('p', _('Failed: ') + r.error), 'error'); } }).catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', _('Error: ') + e.message), 'error'); }); - }}, _('Delete')) - ]) - ]); - }, - - setupDropzone: function(id) { - var self = this, dz = document.getElementById(id); if (!dz) return; - ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(function(e) { dz.addEventListener(e, function(ev) { ev.preventDefault(); ev.stopPropagation(); }); }); - ['dragenter', 'dragover'].forEach(function(e) { dz.addEventListener(e, function() { dz.classList.add('dragover'); }); }); - ['dragleave', 'drop'].forEach(function(e) { dz.addEventListener(e, function() { dz.classList.remove('dragover'); }); }); - dz.addEventListener('drop', function(e) { self.handleDroppedFiles(e.dataTransfer.files); }); - }, - - handleFileSelect: function(e) { this.handleDroppedFiles(e.target.files); }, - handleDroppedFiles: function(files) { for (var i = 0; i < files.length; i++) this.uploadFiles.push(files[i]); this.updateFileList(); }, - - updateFileList: function() { - var self = this, c = document.getElementById('mb-file-list'); if (!c) return; c.innerHTML = ''; - this.uploadFiles.forEach(function(f, i) { - c.appendChild(E('div', { 'class': 'mb-file-item' }, [ - E('span', {}, '\u{1F4C4}'), E('span', { 'class': 'mb-file-item-name' }, f.name), E('span', { 'class': 'mb-file-item-size' }, self.formatFileSize(f.size)), - E('button', { 'class': 'mb-file-item-btn delete', 'click': function() { self.uploadFiles.splice(i, 1); self.updateFileList(); } }, '\u00D7') - ])); - }); - }, - - formatFileSize: function(b) { if (b < 1024) return b + ' B'; if (b < 1048576) return (b / 1024).toFixed(1) + ' KB'; return (b / 1048576).toFixed(1) + ' MB'; }, - - copyUrl: function(url) { - if (navigator.clipboard) navigator.clipboard.writeText(url).then(function() { ui.addNotification(null, E('p', _('Copied!'))); }); - else { var i = document.getElementById('mb-url'); if (i) { i.select(); document.execCommand('copy'); } ui.addNotification(null, E('p', _('Copied!'))); } - }, - - closeModal: function(id) { var m = document.getElementById(id); if (m) m.remove(); }, - - handleSaveApply: null, handleSave: null, handleReset: null -}); diff --git a/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer b/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer index b2147ab9..45695a31 100755 --- a/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer +++ b/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer @@ -213,8 +213,8 @@ method_list_sites() { _add_site() { local section="$1" - local name domain gitea_repo ssl enabled description tor_enabled - local has_content last_sync onion_address + local name domain gitea_repo ssl enabled description tor_enabled port runtime + local has_content last_sync onion_address backend_running config_get name "$section" name "" config_get domain "$section" domain "" @@ -223,6 +223,8 @@ _add_site() { config_get enabled "$section" enabled "1" config_get description "$section" description "" config_get tor_enabled "$section" tor_enabled "0" + config_get port "$section" port "" + config_get runtime "$section" runtime "" # Check if site has content has_content="0" @@ -242,6 +244,16 @@ _add_site() { onion_address=$(get_onion_address "$name") fi + # Check if backend is running (uhttpd listening on port) + backend_running="0" + if [ -n "$port" ]; then + # Check if port is listening using /proc/net/tcp (hex port) + local hex_port=$(printf '%04X' "$port" 2>/dev/null) + if grep -qi ":${hex_port}" /proc/net/tcp 2>/dev/null; then + backend_running="1" + fi + fi + json_add_object json_add_string "id" "$section" json_add_string "name" "$name" @@ -253,6 +265,9 @@ _add_site() { json_add_boolean "has_content" "$has_content" json_add_string "last_sync" "$last_sync" json_add_string "url" "https://$domain" + [ -n "$port" ] && json_add_int "port" "$port" + [ -n "$runtime" ] && json_add_string "runtime" "$runtime" + json_add_boolean "backend_running" "$backend_running" # Tor hidden service info json_add_boolean "tor_enabled" "$(has_tor_service "$name" && echo 1 || echo 0)" @@ -1623,6 +1638,149 @@ method_save_settings() { json_dump } +# Discover uhttpd vhosts not tracked in metablogizer +method_discover_vhosts() { + SITES_ROOT=$(get_uci main sites_root "$SITES_ROOT") + + json_init + json_add_array "discovered" + + # Find all uhttpd instances with metablog prefix + uci show uhttpd 2>/dev/null | grep "=uhttpd" | while read -r line; do + local instance=$(echo "$line" | cut -d'=' -f1 | cut -d'.' -f2) + + # Skip non-metablog instances + case "$instance" in + metablog_*) ;; + *) continue ;; + esac + + # Extract site ID from instance name (metablog_site_xxx -> site_xxx) + local section_id="${instance#metablog_}" + + # Check if this site exists in metablogizer config + local tracked_name=$(get_uci "$section_id" name "") + + # If not tracked, discover it + if [ -z "$tracked_name" ]; then + local home=$(uci -q get "uhttpd.${instance}.home") + local listen=$(uci -q get "uhttpd.${instance}.listen_http") + local port=$(echo "$listen" | sed 's/.*://') + local name=$(basename "$home" 2>/dev/null) + + if [ -n "$name" ] && [ -n "$port" ]; then + json_add_object + json_add_string "instance" "$instance" + json_add_string "section_id" "$section_id" + json_add_string "name" "$name" + json_add_string "home" "$home" + json_add_int "port" "$port" + json_add_boolean "has_content" "$([ -f "$home/index.html" ] && echo 1 || echo 0)" + json_close_object + fi + fi + done + + json_close_array + json_dump +} + +# Import a discovered uhttpd vhost into metablogizer +method_import_vhost() { + local instance name domain + + read -r input + json_load "$input" + json_get_var instance instance + json_get_var name name + json_get_var domain domain + + if [ -z "$instance" ] || [ -z "$name" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "Missing instance or name" + json_dump + return + fi + + # Get uhttpd instance details + local home=$(uci -q get "uhttpd.${instance}.home") + local listen=$(uci -q get "uhttpd.${instance}.listen_http") + local port=$(echo "$listen" | sed 's/.*://') + + if [ -z "$home" ] || [ -z "$port" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "uhttpd instance not found or invalid" + json_dump + return + fi + + # Extract section_id from instance name + local section_id="${instance#metablog_}" + + # Create metablogizer site entry + uci set "$UCI_CONFIG.$section_id=site" + uci set "$UCI_CONFIG.$section_id.name=$name" + uci set "$UCI_CONFIG.$section_id.domain=${domain:-$name.local}" + uci set "$UCI_CONFIG.$section_id.port=$port" + uci set "$UCI_CONFIG.$section_id.runtime=uhttpd" + uci set "$UCI_CONFIG.$section_id.ssl=1" + uci set "$UCI_CONFIG.$section_id.enabled=1" + uci commit "$UCI_CONFIG" + + json_init + json_add_boolean "success" 1 + json_add_string "id" "$section_id" + json_add_string "name" "$name" + json_add_int "port" "$port" + json_dump +} + +# Sync all sites - ensure all have correct port/runtime in UCI +method_sync_config() { + local fixed=0 + + # Iterate through all metablog uhttpd instances + uci show uhttpd 2>/dev/null | grep "=uhttpd" | while read -r line; do + local instance=$(echo "$line" | cut -d'=' -f1 | cut -d'.' -f2) + + case "$instance" in + metablog_*) ;; + *) continue ;; + esac + + local section_id="${instance#metablog_}" + local listen=$(uci -q get "uhttpd.${instance}.listen_http") + local port=$(echo "$listen" | sed 's/.*://') + local home=$(uci -q get "uhttpd.${instance}.home") + + # Check if site exists in metablogizer + local tracked_name=$(get_uci "$section_id" name "") + if [ -n "$tracked_name" ]; then + # Update port and runtime if missing + local current_port=$(get_uci "$section_id" port "") + local current_runtime=$(get_uci "$section_id" runtime "") + + if [ -z "$current_port" ] && [ -n "$port" ]; then + uci set "$UCI_CONFIG.$section_id.port=$port" + fixed=$((fixed + 1)) + fi + if [ -z "$current_runtime" ]; then + uci set "$UCI_CONFIG.$section_id.runtime=uhttpd" + fixed=$((fixed + 1)) + fi + fi + done + + uci commit "$UCI_CONFIG" + + json_init + json_add_boolean "success" 1 + json_add_int "fixed" "$fixed" + json_dump +} + # Main RPC interface case "$1" in list) @@ -1645,7 +1803,10 @@ case "$1" in "repair_site": { "id": "string" }, "enable_tor": { "id": "string" }, "disable_tor": { "id": "string" }, - "get_tor_status": {} + "get_tor_status": {}, + "discover_vhosts": {}, + "import_vhost": { "instance": "string", "name": "string", "domain": "string" }, + "sync_config": {} } EOF ;; @@ -1669,6 +1830,9 @@ EOF enable_tor) method_enable_tor ;; disable_tor) method_disable_tor ;; get_tor_status) method_get_tor_status ;; + discover_vhosts) method_discover_vhosts ;; + import_vhost) method_import_vhost ;; + sync_config) method_sync_config ;; *) echo '{"error": "unknown method"}' ;; esac ;; diff --git a/package/secubox/luci-app-metablogizer/root/usr/share/luci/menu.d/luci-app-metablogizer.json b/package/secubox/luci-app-metablogizer/root/usr/share/luci/menu.d/luci-app-metablogizer.json index f3940947..fd3323b7 100644 --- a/package/secubox/luci-app-metablogizer/root/usr/share/luci/menu.d/luci-app-metablogizer.json +++ b/package/secubox/luci-app-metablogizer/root/usr/share/luci/menu.d/luci-app-metablogizer.json @@ -10,12 +10,12 @@ "uci": {"metablogizer": true} } }, - "admin/services/metablogizer/overview": { - "title": "Sites", + "admin/services/metablogizer/dashboard": { + "title": "Dashboard", "order": 10, "action": { "type": "view", - "path": "metablogizer/overview" + "path": "metablogizer/dashboard" } }, "admin/services/metablogizer/settings": { diff --git a/package/secubox/luci-app-metablogizer/root/usr/share/rpcd/acl.d/luci-app-metablogizer.json b/package/secubox/luci-app-metablogizer/root/usr/share/rpcd/acl.d/luci-app-metablogizer.json index 280971c5..56b10358 100644 --- a/package/secubox/luci-app-metablogizer/root/usr/share/rpcd/acl.d/luci-app-metablogizer.json +++ b/package/secubox/luci-app-metablogizer/root/usr/share/rpcd/acl.d/luci-app-metablogizer.json @@ -10,7 +10,9 @@ "get_publish_info", "get_settings", "get_hosting_status", - "check_site_health" + "check_site_health", + "get_tor_status", + "discover_vhosts" ], "file": ["read", "list", "stat"] }, @@ -28,7 +30,12 @@ "sync_site", "save_settings", "upload_file", - "list_files" + "list_files", + "repair_site", + "enable_tor", + "disable_tor", + "import_vhost", + "sync_config" ], "luci.haproxy": [ "create_backend", diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages b/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages index 2bb135cf..64ebed92 100644 --- a/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages +++ b/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages @@ -8,7 +8,7 @@ Architecture: all Installed-Size: 71680 Description: Comprehensive authentication and session management with captive portal, OAuth2/OIDC integration, voucher system, and time-based access control Filename: luci-app-auth-guardian_0.4.0-r3_all.ipk -Size: 12081 +Size: 12078 Package: luci-app-bandwidth-manager Version: 0.5.0-r2 @@ -20,7 +20,7 @@ Architecture: all Installed-Size: 378880 Description: Advanced bandwidth management with QoS rules, client quotas, and SQM integration Filename: luci-app-bandwidth-manager_0.5.0-r2_all.ipk -Size: 66970 +Size: 66969 Package: luci-app-cdn-cache Version: 0.5.0-r3 @@ -32,7 +32,7 @@ Architecture: all Installed-Size: 122880 Description: Dashboard for managing local CDN caching proxy on OpenWrt Filename: luci-app-cdn-cache_0.5.0-r3_all.ipk -Size: 23190 +Size: 23192 Package: luci-app-client-guardian Version: 0.4.0-r7 @@ -44,7 +44,7 @@ Architecture: all Installed-Size: 307200 Description: Network Access Control with client monitoring, zone management, captive portal, parental controls, and SMS/email alerts Filename: luci-app-client-guardian_0.4.0-r7_all.ipk -Size: 57045 +Size: 57042 Package: luci-app-crowdsec-dashboard Version: 0.7.0-r29 @@ -56,7 +56,7 @@ Architecture: all Installed-Size: 296960 Description: Real-time security monitoring dashboard for CrowdSec on OpenWrt Filename: luci-app-crowdsec-dashboard_0.7.0-r29_all.ipk -Size: 55583 +Size: 55584 Package: luci-app-cyberfeed Version: 0.1.1-r1 @@ -68,7 +68,7 @@ Architecture: all Installed-Size: 71680 Description: Cyberpunk-themed RSS feed aggregator dashboard with social media support Filename: luci-app-cyberfeed_0.1.1-r1_all.ipk -Size: 12838 +Size: 12837 Package: luci-app-exposure Version: 1.0.0-r3 @@ -80,7 +80,7 @@ Architecture: all Installed-Size: 153600 Description: LuCI SecuBox Service Exposure Manager Filename: luci-app-exposure_1.0.0-r3_all.ipk -Size: 20535 +Size: 20536 Package: luci-app-gitea Version: 1.0.0-r2 @@ -92,7 +92,7 @@ Architecture: all Installed-Size: 92160 Description: Modern dashboard for Gitea Platform management on OpenWrt Filename: luci-app-gitea_1.0.0-r2_all.ipk -Size: 15586 +Size: 15587 Package: luci-app-glances Version: 1.0.0-r2 @@ -104,7 +104,7 @@ Architecture: all Installed-Size: 40960 Description: Modern dashboard for Glances system monitoring with SecuBox theme Filename: luci-app-glances_1.0.0-r2_all.ipk -Size: 6969 +Size: 6966 Package: luci-app-haproxy Version: 1.0.0-r8 @@ -128,7 +128,7 @@ Architecture: all Installed-Size: 215040 Description: Modern dashboard for Hexo static site generator on OpenWrt Filename: luci-app-hexojs_1.0.0-r3_all.ipk -Size: 32980 +Size: 32975 Package: luci-app-jitsi Version: 1.0.0-r1 @@ -152,7 +152,7 @@ Architecture: all Installed-Size: 112640 Description: Centralized cryptographic key management with hardware security module (HSM) support for Nitrokey and YubiKey devices. Provides secure key storage, certificate management, SSH key handling, and secret storage with audit logging. Filename: luci-app-ksm-manager_0.4.0-r2_all.ipk -Size: 18719 +Size: 18723 Package: luci-app-localai Version: 0.1.0-r15 @@ -164,7 +164,7 @@ Architecture: all Installed-Size: 81920 Description: Modern dashboard for LocalAI LLM management on OpenWrt Filename: luci-app-localai_0.1.0-r15_all.ipk -Size: 14361 +Size: 14362 Package: luci-app-lyrion Version: 1.0.0-r1 @@ -176,7 +176,7 @@ Architecture: all Installed-Size: 40960 Description: LuCI support for Lyrion Music Server Filename: luci-app-lyrion_1.0.0-r1_all.ipk -Size: 6729 +Size: 6725 Package: luci-app-magicmirror2 Version: 0.4.0-r6 @@ -188,7 +188,7 @@ Architecture: all Installed-Size: 71680 Description: Modern dashboard for MagicMirror2 smart display platform with module manager and SecuBox theme Filename: luci-app-magicmirror2_0.4.0-r6_all.ipk -Size: 12279 +Size: 12277 Package: luci-app-mailinabox Version: 1.0.0-r1 @@ -200,7 +200,7 @@ Architecture: all Installed-Size: 30720 Description: LuCI support for Mail-in-a-Box Filename: luci-app-mailinabox_1.0.0-r1_all.ipk -Size: 5482 +Size: 5481 Package: luci-app-media-flow Version: 0.6.4-r1 @@ -224,7 +224,7 @@ Architecture: all Installed-Size: 112640 Description: LuCI support for MetaBlogizer Static Site Publisher Filename: luci-app-metablogizer_1.0.0-r3_all.ipk -Size: 23506 +Size: 23505 Package: luci-app-metabolizer Version: 1.0.0-r2 @@ -236,7 +236,7 @@ Architecture: all Installed-Size: 30720 Description: LuCI support for Metabolizer CMS Filename: luci-app-metabolizer_1.0.0-r2_all.ipk -Size: 4758 +Size: 4756 Package: luci-app-mitmproxy Version: 0.5.0-r1 @@ -260,7 +260,7 @@ Architecture: all Installed-Size: 51200 Description: Web interface for MMPM - MagicMirror Package Manager Filename: luci-app-mmpm_0.2.0-r3_all.ipk -Size: 7901 +Size: 7900 Package: luci-app-mqtt-bridge Version: 0.4.0-r4 @@ -272,7 +272,7 @@ Architecture: all Installed-Size: 122880 Description: USB-to-MQTT IoT hub with SecuBox theme Filename: luci-app-mqtt-bridge_0.4.0-r4_all.ipk -Size: 22782 +Size: 22774 Package: luci-app-ndpid Version: 1.1.2-r2 @@ -284,7 +284,7 @@ Architecture: all Installed-Size: 122880 Description: Modern dashboard for nDPId deep packet inspection on OpenWrt Filename: luci-app-ndpid_1.1.2-r2_all.ipk -Size: 22454 +Size: 22456 Package: luci-app-netdata-dashboard Version: 0.5.0-r2 @@ -296,7 +296,7 @@ Architecture: all Installed-Size: 133120 Description: Real-time system monitoring dashboard with Netdata integration for OpenWrt Filename: luci-app-netdata-dashboard_0.5.0-r2_all.ipk -Size: 22399 +Size: 22401 Package: luci-app-network-modes Version: 0.5.0-r3 @@ -308,7 +308,7 @@ Architecture: all Installed-Size: 307200 Description: Configure OpenWrt for different network modes: Sniffer, Access Point, Relay, Router Filename: luci-app-network-modes_0.5.0-r3_all.ipk -Size: 55611 +Size: 55613 Package: luci-app-network-tweaks Version: 1.0.0-r7 @@ -320,7 +320,7 @@ Architecture: all Installed-Size: 81920 Description: Unified network services dashboard with DNS/hosts sync, CDN cache control, and WPAD auto-proxy configuration Filename: luci-app-network-tweaks_1.0.0-r7_all.ipk -Size: 15460 +Size: 15461 Package: luci-app-nextcloud Version: 1.0.0-r1 @@ -344,7 +344,7 @@ Architecture: all Installed-Size: 71680 Description: Modern dashboard for Ollama LLM management on OpenWrt Filename: luci-app-ollama_0.1.0-r1_all.ipk -Size: 11998 +Size: 11997 Package: luci-app-picobrew Version: 1.0.0-r1 @@ -356,7 +356,7 @@ Architecture: all Installed-Size: 51200 Description: Modern dashboard for PicoBrew Server management on OpenWrt Filename: luci-app-picobrew_1.0.0-r1_all.ipk -Size: 9981 +Size: 9975 Package: luci-app-secubox Version: 0.7.1-r4 @@ -368,7 +368,7 @@ Architecture: all Installed-Size: 266240 Description: Central control hub for all SecuBox modules. Provides unified dashboard, module status, system health monitoring, and quick actions. Filename: luci-app-secubox_0.7.1-r4_all.ipk -Size: 49907 +Size: 49901 Package: luci-app-secubox-admin Version: 1.0.0-r19 @@ -379,7 +379,7 @@ Architecture: all Installed-Size: 337920 Description: Unified admin control center for SecuBox appstore plugins with system monitoring Filename: luci-app-secubox-admin_1.0.0-r19_all.ipk -Size: 57096 +Size: 57098 Package: luci-app-secubox-crowdsec Version: 1.0.0-r3 @@ -391,7 +391,7 @@ Architecture: all Installed-Size: 81920 Description: LuCI SecuBox CrowdSec Dashboard Filename: luci-app-secubox-crowdsec_1.0.0-r3_all.ipk -Size: 13918 +Size: 13919 Package: luci-app-secubox-netdiag Version: 1.0.0-r1 @@ -451,7 +451,7 @@ Architecture: all Installed-Size: 71680 Description: Unified dashboard integrating netifyd DPI threats with CrowdSec intelligence for real-time threat monitoring and automated blocking Filename: luci-app-secubox-security-threats_1.0.0-r4_all.ipk -Size: 13905 +Size: 13903 Package: luci-app-service-registry Version: 1.0.0-r1 @@ -463,19 +463,19 @@ Architecture: all Installed-Size: 194560 Description: Unified service aggregation with HAProxy vhosts, Tor hidden services, and QR-coded landing page Filename: luci-app-service-registry_1.0.0-r1_all.ipk -Size: 39828 +Size: 39826 Package: luci-app-streamlit -Version: 1.0.0-r9 +Version: 1.0.0-r11 Depends: luci-base, luci-lib-jsonc, rpcd, rpcd-mod-luci, secubox-app-streamlit License: Apache-2.0 Section: luci Maintainer: OpenWrt LuCI community Architecture: all -Installed-Size: 122880 -Description: Modern dashboard for Streamlit Platform management on OpenWrt -Filename: luci-app-streamlit_1.0.0-r9_all.ipk -Size: 20472 +Installed-Size: 81920 +Description: Multi-instance Streamlit management with Gitea integration +Filename: luci-app-streamlit_1.0.0-r11_all.ipk +Size: 14749 Package: luci-app-system-hub Version: 0.5.1-r4 @@ -487,7 +487,7 @@ Architecture: all Installed-Size: 358400 Description: Central system control with monitoring, services, logs, and backup Filename: luci-app-system-hub_0.5.1-r4_all.ipk -Size: 66351 +Size: 66349 Package: luci-app-tor-shield Version: 1.0.0-r10 @@ -499,7 +499,7 @@ Architecture: all Installed-Size: 133120 Description: Modern dashboard for Tor anonymization on OpenWrt Filename: luci-app-tor-shield_1.0.0-r10_all.ipk -Size: 24535 +Size: 24537 Package: luci-app-traffic-shaper Version: 0.4.0-r2 @@ -511,7 +511,7 @@ Architecture: all Installed-Size: 92160 Description: Advanced traffic shaping with TC/CAKE for precise bandwidth control Filename: luci-app-traffic-shaper_0.4.0-r2_all.ipk -Size: 15637 +Size: 15636 Package: luci-app-vhost-manager Version: 0.5.0-r5 @@ -547,7 +547,7 @@ Architecture: all Installed-Size: 40960 Description: Graphical interface for managing the Zigbee2MQTT docker application. Filename: luci-app-zigbee2mqtt_1.0.0-r2_all.ipk -Size: 7090 +Size: 7087 Package: luci-theme-secubox Version: 0.4.7-r1 @@ -559,7 +559,7 @@ Architecture: all Installed-Size: 460800 Description: Global CyberMood design system (CSS/JS/i18n) shared by all SecuBox dashboards. Filename: luci-theme-secubox_0.4.7-r1_all.ipk -Size: 111797 +Size: 111796 Package: secubox-app Version: 1.0.0-r2 @@ -570,7 +570,7 @@ Installed-Size: 92160 Description: Command line helper for SecuBox App Store manifests. Installs /usr/sbin/secubox-app and ships the default manifests under /usr/share/secubox/plugins/. Filename: secubox-app_1.0.0-r2_all.ipk -Size: 11186 +Size: 11184 Package: secubox-app-adguardhome Version: 1.0.0-r2 @@ -584,7 +584,7 @@ Description: Installer, configuration, and service manager for running AdGuard inside Docker on SecuBox-powered OpenWrt systems. Network-wide ad blocker with DNS-over-HTTPS/TLS support and detailed analytics. Filename: secubox-app-adguardhome_1.0.0-r2_all.ipk -Size: 2882 +Size: 2880 Package: secubox-app-auth-logger Version: 1.2.2-r1 @@ -625,7 +625,7 @@ Description: Custom CrowdSec configurations for SecuBox web interface protectio - Webapp generic auth bruteforce protection - Whitelist for trusted networks Filename: secubox-app-crowdsec-custom_1.1.0-r1_all.ipk -Size: 5763 +Size: 5762 Package: secubox-app-cs-firewall-bouncer Version: 0.0.31-r4 @@ -666,7 +666,7 @@ Description: Cyberpunk-themed RSS feed aggregator for OpenWrt/SecuBox. Features emoji injection, neon styling, and RSS-Bridge support for social media feeds (Facebook, Twitter, Mastodon). Filename: secubox-app-cyberfeed_0.2.1-r1_all.ipk -Size: 12454 +Size: 12450 Package: secubox-app-domoticz Version: 1.0.0-r2 @@ -679,7 +679,7 @@ Installed-Size: 10240 Description: Installer, configuration, and service manager for running Domoticz inside Docker on SecuBox-powered OpenWrt systems. Filename: secubox-app-domoticz_1.0.0-r2_all.ipk -Size: 2548 +Size: 2546 Package: secubox-app-exposure Version: 1.0.0-r1 @@ -694,7 +694,7 @@ Description: Unified service exposure manager for SecuBox. - Dynamic Tor hidden service management - HAProxy SSL reverse proxy configuration Filename: secubox-app-exposure_1.0.0-r1_all.ipk -Size: 6837 +Size: 6829 Package: secubox-app-gitea Version: 1.0.0-r5 @@ -740,7 +740,7 @@ Description: Glances - Cross-platform system monitoring tool for SecuBox. Runs in LXC container for isolation and security. Configure in /etc/config/glances. Filename: secubox-app-glances_1.0.0-r1_all.ipk -Size: 5536 +Size: 5534 Package: secubox-app-haproxy Version: 1.0.0-r23 @@ -784,7 +784,7 @@ Description: Hexo CMS - Self-hosted static blog generator for OpenWrt Runs in LXC container with Alpine Linux. Configure in /etc/config/hexojs. Filename: secubox-app-hexojs_1.0.0-r8_all.ipk -Size: 94935 +Size: 94931 Package: secubox-app-jitsi Version: 1.0.0-r1 @@ -809,7 +809,7 @@ Description: Jitsi Meet - Secure, fully featured video conferencing for SecuBox Integrates with HAProxy for SSL termination. Configure in /etc/config/jitsi. Filename: secubox-app-jitsi_1.0.0-r1_all.ipk -Size: 8918 +Size: 8913 Package: secubox-app-localai Version: 2.25.0-r1 @@ -831,7 +831,7 @@ Description: LocalAI native binary package for OpenWrt. API: http://:8081/v1 Filename: secubox-app-localai_2.25.0-r1_all.ipk -Size: 5721 +Size: 5727 Package: secubox-app-localai-wb Version: 2.25.0-r1 @@ -855,7 +855,7 @@ Description: LocalAI native binary package for OpenWrt. API: http://:8080/v1 Filename: secubox-app-localai-wb_2.25.0-r1_all.ipk -Size: 7951 +Size: 7949 Package: secubox-app-lyrion Version: 2.0.2-r1 @@ -875,7 +875,7 @@ Description: Lyrion Media Server (formerly Logitech Media Server / Squeezebox S Auto-detects available runtime, preferring LXC for lower resource usage. Configure runtime in /etc/config/lyrion. Filename: secubox-app-lyrion_2.0.2-r1_all.ipk -Size: 7287 +Size: 7278 Package: secubox-app-magicmirror2 Version: 0.4.0-r8 @@ -897,7 +897,7 @@ Description: MagicMirror² - Open source modular smart mirror platform for Secu Runs in LXC container for isolation and security. Configure in /etc/config/magicmirror2. Filename: secubox-app-magicmirror2_0.4.0-r8_all.ipk -Size: 9253 +Size: 9254 Package: secubox-app-mailinabox Version: 2.0.0-r1 @@ -922,7 +922,7 @@ Description: Complete email server solution using docker-mailserver for SecuBox Commands: mailinaboxctl --help Filename: secubox-app-mailinabox_2.0.0-r1_all.ipk -Size: 7574 +Size: 7571 Package: secubox-app-metabolizer Version: 1.0.0-r3 @@ -943,7 +943,7 @@ Description: Metabolizer Blog Pipeline - Integrated CMS with Git-based workflow Pipeline: Edit in Streamlit -> Push to Gitea -> Build with Hexo -> Publish Filename: secubox-app-metabolizer_1.0.0-r3_all.ipk -Size: 13980 +Size: 13978 Package: secubox-app-mitmproxy Version: 0.5.0-r19 @@ -991,7 +991,7 @@ Description: MMPM (MagicMirror Package Manager) for SecuBox. Runs inside the MagicMirror2 LXC container. Filename: secubox-app-mmpm_0.2.0-r5_all.ipk -Size: 3977 +Size: 3980 Package: secubox-app-nextcloud Version: 1.0.0-r2 @@ -1005,7 +1005,7 @@ Description: Installer, configuration, and service manager for running Nextclou inside Docker on SecuBox-powered OpenWrt systems. Self-hosted file sync and share with calendar, contacts, and collaboration. Filename: secubox-app-nextcloud_1.0.0-r2_all.ipk -Size: 2958 +Size: 2961 Package: secubox-app-ollama Version: 0.1.0-r1 @@ -1027,7 +1027,7 @@ Description: Ollama - Simple local LLM runtime for SecuBox-powered OpenWrt syst Runs in Docker/Podman container. Configure in /etc/config/ollama. Filename: secubox-app-ollama_0.1.0-r1_all.ipk -Size: 5739 +Size: 5735 Package: secubox-app-picobrew Version: 1.0.0-r7 @@ -1049,7 +1049,7 @@ Description: PicoBrew Server - Self-hosted brewing controller for PicoBrew devi Runs in LXC container with Python/Flask backend. Configure in /etc/config/picobrew. Filename: secubox-app-picobrew_1.0.0-r7_all.ipk -Size: 5542 +Size: 5539 Package: secubox-app-streamlit Version: 1.0.0-r5 @@ -1076,7 +1076,7 @@ Description: Streamlit App Platform - Self-hosted Python data app platform Configure in /etc/config/streamlit. Filename: secubox-app-streamlit_1.0.0-r5_all.ipk -Size: 11722 +Size: 11720 Package: secubox-app-tor Version: 1.0.0-r1 @@ -1099,7 +1099,7 @@ Description: SecuBox Tor Shield - One-click Tor anonymization for OpenWrt Configure in /etc/config/tor-shield. Filename: secubox-app-tor_1.0.0-r1_all.ipk -Size: 7380 +Size: 7382 Package: secubox-app-webapp Version: 1.5.0-r7 @@ -1117,7 +1117,7 @@ Description: SecuBox Control Center Dashboard - A web-based dashboard for monit - Service management - Network interface control Filename: secubox-app-webapp_1.5.0-r7_all.ipk -Size: 39170 +Size: 39169 Package: secubox-app-zigbee2mqtt Version: 1.0.0-r3 @@ -1130,7 +1130,7 @@ Installed-Size: 20480 Description: Installer, configuration, and service manager for running Zigbee2MQTT inside Docker on SecuBox-powered OpenWrt systems. Filename: secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk -Size: 3546 +Size: 3545 Package: secubox-core Version: 0.10.0-r11 @@ -1150,7 +1150,7 @@ Description: SecuBox Core Framework provides the foundational infrastructure fo - Unified CLI interface - ubus RPC backend Filename: secubox-core_0.10.0-r11_all.ipk -Size: 87810 +Size: 87809 Package: secubox-p2p Version: 0.6.0-r3 @@ -1169,5 +1169,5 @@ Description: SecuBox P2P Hub backend providing peer discovery, mesh networking and MirrorBox NetMesh Catalog for cross-chain distributed service registry with HAProxy vhost discovery and multi-endpoint access URLs. Filename: secubox-p2p_0.6.0-r3_all.ipk -Size: 42021 +Size: 42014 diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages.gz b/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages.gz index b01e3108..4b2838b1 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages.gz and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/Packages.gz differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/apps-local.json b/package/secubox/secubox-app-bonus/root/www/secubox-feed/apps-local.json index 15c39d35..598c5113 100644 --- a/package/secubox/secubox-app-bonus/root/www/secubox-feed/apps-local.json +++ b/package/secubox/secubox-app-bonus/root/www/secubox-feed/apps-local.json @@ -1,12 +1,12 @@ { "feed_url": "/secubox-feed", - "generated": "2026-02-01T07:08:21+01:00", + "generated": "2026-02-01T07:26:25+01:00", "packages": [ { "name": "luci-app-auth-guardian", "version": "0.4.0-r3", "filename": "luci-app-auth-guardian_0.4.0-r3_all.ipk", - "size": 12081, + "size": 12078, "category": "security", "icon": "key", "description": "Authentication management", @@ -18,7 +18,7 @@ "name": "luci-app-bandwidth-manager", "version": "0.5.0-r2", "filename": "luci-app-bandwidth-manager_0.5.0-r2_all.ipk", - "size": 66970, + "size": 66969, "category": "network", "icon": "activity", "description": "Bandwidth monitoring and control", @@ -30,7 +30,7 @@ "name": "luci-app-cdn-cache", "version": "0.5.0-r3", "filename": "luci-app-cdn-cache_0.5.0-r3_all.ipk", - "size": 23190, + "size": 23192, "category": "network", "icon": "globe", "description": "CDN caching", @@ -42,7 +42,7 @@ "name": "luci-app-client-guardian", "version": "0.4.0-r7", "filename": "luci-app-client-guardian_0.4.0-r7_all.ipk", - "size": 57045, + "size": 57042, "category": "network", "icon": "users", "description": "Client management and monitoring", @@ -54,7 +54,7 @@ "name": "luci-app-crowdsec-dashboard", "version": "0.7.0-r29", "filename": "luci-app-crowdsec-dashboard_0.7.0-r29_all.ipk", - "size": 55583, + "size": 55584, "category": "security", "icon": "shield", "description": "CrowdSec security monitoring", @@ -66,7 +66,7 @@ "name": "luci-app-cyberfeed", "version": "0.1.1-r1", "filename": "luci-app-cyberfeed_0.1.1-r1_all.ipk", - "size": 12838, + "size": 12837, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -78,7 +78,7 @@ "name": "luci-app-exposure", "version": "1.0.0-r3", "filename": "luci-app-exposure_1.0.0-r3_all.ipk", - "size": 20535, + "size": 20536, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -90,7 +90,7 @@ "name": "luci-app-gitea", "version": "1.0.0-r2", "filename": "luci-app-gitea_1.0.0-r2_all.ipk", - "size": 15586, + "size": 15587, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -102,7 +102,7 @@ "name": "luci-app-glances", "version": "1.0.0-r2", "filename": "luci-app-glances_1.0.0-r2_all.ipk", - "size": 6969, + "size": 6966, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -126,7 +126,7 @@ "name": "luci-app-hexojs", "version": "1.0.0-r3", "filename": "luci-app-hexojs_1.0.0-r3_all.ipk", - "size": 32980, + "size": 32975, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -150,7 +150,7 @@ "name": "luci-app-ksm-manager", "version": "0.4.0-r2", "filename": "luci-app-ksm-manager_0.4.0-r2_all.ipk", - "size": 18719, + "size": 18723, "category": "system", "icon": "cpu", "description": "Kernel memory management", @@ -162,7 +162,7 @@ "name": "luci-app-localai", "version": "0.1.0-r15", "filename": "luci-app-localai_0.1.0-r15_all.ipk", - "size": 14361, + "size": 14362, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -174,7 +174,7 @@ "name": "luci-app-lyrion", "version": "1.0.0-r1", "filename": "luci-app-lyrion_1.0.0-r1_all.ipk", - "size": 6729, + "size": 6725, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -186,7 +186,7 @@ "name": "luci-app-magicmirror2", "version": "0.4.0-r6", "filename": "luci-app-magicmirror2_0.4.0-r6_all.ipk", - "size": 12279, + "size": 12277, "category": "iot", "icon": "monitor", "description": "Smart mirror display", @@ -198,7 +198,7 @@ "name": "luci-app-mailinabox", "version": "1.0.0-r1", "filename": "luci-app-mailinabox_1.0.0-r1_all.ipk", - "size": 5482, + "size": 5481, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -222,7 +222,7 @@ "name": "luci-app-metablogizer", "version": "1.0.0-r3", "filename": "luci-app-metablogizer_1.0.0-r3_all.ipk", - "size": 23506, + "size": 23505, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -234,7 +234,7 @@ "name": "luci-app-metabolizer", "version": "1.0.0-r2", "filename": "luci-app-metabolizer_1.0.0-r2_all.ipk", - "size": 4758, + "size": 4756, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -258,7 +258,7 @@ "name": "luci-app-mmpm", "version": "0.2.0-r3", "filename": "luci-app-mmpm_0.2.0-r3_all.ipk", - "size": 7901, + "size": 7900, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -270,7 +270,7 @@ "name": "luci-app-mqtt-bridge", "version": "0.4.0-r4", "filename": "luci-app-mqtt-bridge_0.4.0-r4_all.ipk", - "size": 22782, + "size": 22774, "category": "iot", "icon": "message-square", "description": "MQTT bridge", @@ -282,7 +282,7 @@ "name": "luci-app-ndpid", "version": "1.1.2-r2", "filename": "luci-app-ndpid_1.1.2-r2_all.ipk", - "size": 22454, + "size": 22456, "category": "security", "icon": "eye", "description": "Deep packet inspection", @@ -294,7 +294,7 @@ "name": "luci-app-netdata-dashboard", "version": "0.5.0-r2", "filename": "luci-app-netdata-dashboard_0.5.0-r2_all.ipk", - "size": 22399, + "size": 22401, "category": "monitoring", "icon": "bar-chart-2", "description": "System monitoring dashboard", @@ -306,7 +306,7 @@ "name": "luci-app-network-modes", "version": "0.5.0-r3", "filename": "luci-app-network-modes_0.5.0-r3_all.ipk", - "size": 55611, + "size": 55613, "category": "network", "icon": "wifi", "description": "Network configuration", @@ -318,7 +318,7 @@ "name": "luci-app-network-tweaks", "version": "1.0.0-r7", "filename": "luci-app-network-tweaks_1.0.0-r7_all.ipk", - "size": 15460, + "size": 15461, "category": "network", "icon": "wifi", "description": "Network configuration", @@ -342,7 +342,7 @@ "name": "luci-app-ollama", "version": "0.1.0-r1", "filename": "luci-app-ollama_0.1.0-r1_all.ipk", - "size": 11998, + "size": 11997, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -354,7 +354,7 @@ "name": "luci-app-picobrew", "version": "1.0.0-r1", "filename": "luci-app-picobrew_1.0.0-r1_all.ipk", - "size": 9981, + "size": 9975, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -366,7 +366,7 @@ "name": "luci-app-secubox", "version": "0.7.1-r4", "filename": "luci-app-secubox_0.7.1-r4_all.ipk", - "size": 49907, + "size": 49901, "category": "system", "icon": "box", "description": "SecuBox system component", @@ -378,7 +378,7 @@ "name": "luci-app-secubox-admin", "version": "1.0.0-r19", "filename": "luci-app-secubox-admin_1.0.0-r19_all.ipk", - "size": 57096, + "size": 57098, "category": "system", "icon": "box", "description": "SecuBox system component", @@ -390,7 +390,7 @@ "name": "luci-app-secubox-crowdsec", "version": "1.0.0-r3", "filename": "luci-app-secubox-crowdsec_1.0.0-r3_all.ipk", - "size": 13918, + "size": 13919, "category": "system", "icon": "box", "description": "SecuBox system component", @@ -450,7 +450,7 @@ "name": "luci-app-secubox-security-threats", "version": "1.0.0-r4", "filename": "luci-app-secubox-security-threats_1.0.0-r4_all.ipk", - "size": 13905, + "size": 13903, "category": "system", "icon": "box", "description": "SecuBox system component", @@ -462,7 +462,7 @@ "name": "luci-app-service-registry", "version": "1.0.0-r1", "filename": "luci-app-service-registry_1.0.0-r1_all.ipk", - "size": 39828, + "size": 39826, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -472,9 +472,9 @@ , { "name": "luci-app-streamlit", - "version": "1.0.0-r9", - "filename": "luci-app-streamlit_1.0.0-r9_all.ipk", - "size": 20472, + "version": "1.0.0-r11", + "filename": "luci-app-streamlit_1.0.0-r11_all.ipk", + "size": 14749, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -486,7 +486,7 @@ "name": "luci-app-system-hub", "version": "0.5.1-r4", "filename": "luci-app-system-hub_0.5.1-r4_all.ipk", - "size": 66351, + "size": 66349, "category": "system", "icon": "settings", "description": "System management", @@ -498,7 +498,7 @@ "name": "luci-app-tor-shield", "version": "1.0.0-r10", "filename": "luci-app-tor-shield_1.0.0-r10_all.ipk", - "size": 24535, + "size": 24537, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -510,7 +510,7 @@ "name": "luci-app-traffic-shaper", "version": "0.4.0-r2", "filename": "luci-app-traffic-shaper_0.4.0-r2_all.ipk", - "size": 15637, + "size": 15636, "category": "network", "icon": "filter", "description": "Traffic shaping and QoS", @@ -546,7 +546,7 @@ "name": "luci-app-zigbee2mqtt", "version": "1.0.0-r2", "filename": "luci-app-zigbee2mqtt_1.0.0-r2_all.ipk", - "size": 7090, + "size": 7087, "category": "iot", "icon": "radio", "description": "Zigbee device management", @@ -558,7 +558,7 @@ "name": "luci-theme-secubox", "version": "0.4.7-r1", "filename": "luci-theme-secubox_0.4.7-r1_all.ipk", - "size": 111797, + "size": 111796, "category": "theme", "icon": "palette", "description": "LuCI theme", @@ -570,7 +570,7 @@ "name": "secubox-app", "version": "1.0.0-r2", "filename": "secubox-app_1.0.0-r2_all.ipk", - "size": 11186, + "size": 11184, "category": "utility", "icon": "package", "description": "SecuBox package", @@ -582,7 +582,7 @@ "name": "secubox-app-adguardhome", "version": "1.0.0-r2", "filename": "secubox-app-adguardhome_1.0.0-r2_all.ipk", - "size": 2882, + "size": 2880, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -606,7 +606,7 @@ "name": "secubox-app-crowdsec-custom", "version": "1.1.0-r1", "filename": "secubox-app-crowdsec-custom_1.1.0-r1_all.ipk", - "size": 5763, + "size": 5762, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -630,7 +630,7 @@ "name": "secubox-app-cyberfeed", "version": "0.2.1-r1", "filename": "secubox-app-cyberfeed_0.2.1-r1_all.ipk", - "size": 12454, + "size": 12450, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -642,7 +642,7 @@ "name": "secubox-app-domoticz", "version": "1.0.0-r2", "filename": "secubox-app-domoticz_1.0.0-r2_all.ipk", - "size": 2548, + "size": 2546, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -654,7 +654,7 @@ "name": "secubox-app-exposure", "version": "1.0.0-r1", "filename": "secubox-app-exposure_1.0.0-r1_all.ipk", - "size": 6837, + "size": 6829, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -678,7 +678,7 @@ "name": "secubox-app-glances", "version": "1.0.0-r1", "filename": "secubox-app-glances_1.0.0-r1_all.ipk", - "size": 5536, + "size": 5534, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -702,7 +702,7 @@ "name": "secubox-app-hexojs", "version": "1.0.0-r8", "filename": "secubox-app-hexojs_1.0.0-r8_all.ipk", - "size": 94935, + "size": 94931, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -714,7 +714,7 @@ "name": "secubox-app-jitsi", "version": "1.0.0-r1", "filename": "secubox-app-jitsi_1.0.0-r1_all.ipk", - "size": 8918, + "size": 8913, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -726,7 +726,7 @@ "name": "secubox-app-localai", "version": "2.25.0-r1", "filename": "secubox-app-localai_2.25.0-r1_all.ipk", - "size": 5721, + "size": 5727, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -738,7 +738,7 @@ "name": "secubox-app-localai-wb", "version": "2.25.0-r1", "filename": "secubox-app-localai-wb_2.25.0-r1_all.ipk", - "size": 7951, + "size": 7949, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -750,7 +750,7 @@ "name": "secubox-app-lyrion", "version": "2.0.2-r1", "filename": "secubox-app-lyrion_2.0.2-r1_all.ipk", - "size": 7287, + "size": 7278, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -762,7 +762,7 @@ "name": "secubox-app-magicmirror2", "version": "0.4.0-r8", "filename": "secubox-app-magicmirror2_0.4.0-r8_all.ipk", - "size": 9253, + "size": 9254, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -774,7 +774,7 @@ "name": "secubox-app-mailinabox", "version": "2.0.0-r1", "filename": "secubox-app-mailinabox_2.0.0-r1_all.ipk", - "size": 7574, + "size": 7571, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -786,7 +786,7 @@ "name": "secubox-app-metabolizer", "version": "1.0.0-r3", "filename": "secubox-app-metabolizer_1.0.0-r3_all.ipk", - "size": 13980, + "size": 13978, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -810,7 +810,7 @@ "name": "secubox-app-mmpm", "version": "0.2.0-r5", "filename": "secubox-app-mmpm_0.2.0-r5_all.ipk", - "size": 3977, + "size": 3980, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -822,7 +822,7 @@ "name": "secubox-app-nextcloud", "version": "1.0.0-r2", "filename": "secubox-app-nextcloud_1.0.0-r2_all.ipk", - "size": 2958, + "size": 2961, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -834,7 +834,7 @@ "name": "secubox-app-ollama", "version": "0.1.0-r1", "filename": "secubox-app-ollama_0.1.0-r1_all.ipk", - "size": 5739, + "size": 5735, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -846,7 +846,7 @@ "name": "secubox-app-picobrew", "version": "1.0.0-r7", "filename": "secubox-app-picobrew_1.0.0-r7_all.ipk", - "size": 5542, + "size": 5539, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -858,7 +858,7 @@ "name": "secubox-app-streamlit", "version": "1.0.0-r5", "filename": "secubox-app-streamlit_1.0.0-r5_all.ipk", - "size": 11722, + "size": 11720, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -870,7 +870,7 @@ "name": "secubox-app-tor", "version": "1.0.0-r1", "filename": "secubox-app-tor_1.0.0-r1_all.ipk", - "size": 7380, + "size": 7382, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -882,7 +882,7 @@ "name": "secubox-app-webapp", "version": "1.5.0-r7", "filename": "secubox-app-webapp_1.5.0-r7_all.ipk", - "size": 39170, + "size": 39169, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -894,7 +894,7 @@ "name": "secubox-app-zigbee2mqtt", "version": "1.0.0-r3", "filename": "secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk", - "size": 3546, + "size": 3545, "category": "secubox", "icon": "package", "description": "SecuBox backend service", @@ -906,7 +906,7 @@ "name": "secubox-core", "version": "0.10.0-r11", "filename": "secubox-core_0.10.0-r11_all.ipk", - "size": 87810, + "size": 87809, "category": "system", "icon": "box", "description": "SecuBox core components", @@ -918,7 +918,7 @@ "name": "secubox-p2p", "version": "0.6.0-r3", "filename": "secubox-p2p_0.6.0-r3_all.ipk", - "size": 42021, + "size": 42014, "category": "utility", "icon": "package", "description": "SecuBox package", diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-auth-guardian_0.4.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-auth-guardian_0.4.0-r3_all.ipk index 97fc2afd..6440cb57 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-auth-guardian_0.4.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-auth-guardian_0.4.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-bandwidth-manager_0.5.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-bandwidth-manager_0.5.0-r2_all.ipk index 0effc271..237011bf 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-bandwidth-manager_0.5.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-bandwidth-manager_0.5.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cdn-cache_0.5.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cdn-cache_0.5.0-r3_all.ipk index 8a3e5163..ddeb0ca3 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cdn-cache_0.5.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cdn-cache_0.5.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-client-guardian_0.4.0-r7_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-client-guardian_0.4.0-r7_all.ipk index ab423f49..fa3e79b4 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-client-guardian_0.4.0-r7_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-client-guardian_0.4.0-r7_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-crowdsec-dashboard_0.7.0-r29_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-crowdsec-dashboard_0.7.0-r29_all.ipk index 9182d645..c0323b81 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-crowdsec-dashboard_0.7.0-r29_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-crowdsec-dashboard_0.7.0-r29_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cyberfeed_0.1.1-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cyberfeed_0.1.1-r1_all.ipk index c0026aac..ebf46a66 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cyberfeed_0.1.1-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-cyberfeed_0.1.1-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-exposure_1.0.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-exposure_1.0.0-r3_all.ipk index 7873f283..1af317f2 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-exposure_1.0.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-exposure_1.0.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-gitea_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-gitea_1.0.0-r2_all.ipk index 6a74c06c..0c4912da 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-gitea_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-gitea_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-glances_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-glances_1.0.0-r2_all.ipk index 4ce42388..ee157e18 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-glances_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-glances_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-haproxy_1.0.0-r8_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-haproxy_1.0.0-r8_all.ipk index 527b7f8f..cbe61155 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-haproxy_1.0.0-r8_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-haproxy_1.0.0-r8_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-hexojs_1.0.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-hexojs_1.0.0-r3_all.ipk index 6f130f71..52ce1d96 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-hexojs_1.0.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-hexojs_1.0.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-jitsi_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-jitsi_1.0.0-r1_all.ipk index 9c9c6054..317c6a75 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-jitsi_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-jitsi_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ksm-manager_0.4.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ksm-manager_0.4.0-r2_all.ipk index f695cd6e..30fcc083 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ksm-manager_0.4.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ksm-manager_0.4.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-localai_0.1.0-r15_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-localai_0.1.0-r15_all.ipk index 627c6df1..83b80cdd 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-localai_0.1.0-r15_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-localai_0.1.0-r15_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-lyrion_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-lyrion_1.0.0-r1_all.ipk index f41f2380..1efa5ba7 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-lyrion_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-lyrion_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-magicmirror2_0.4.0-r6_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-magicmirror2_0.4.0-r6_all.ipk index ddce525e..75f6fa65 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-magicmirror2_0.4.0-r6_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-magicmirror2_0.4.0-r6_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mailinabox_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mailinabox_1.0.0-r1_all.ipk index b1464698..87717019 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mailinabox_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mailinabox_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-media-flow_0.6.4-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-media-flow_0.6.4-r1_all.ipk index 1cf5de03..84ad9de3 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-media-flow_0.6.4-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-media-flow_0.6.4-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metablogizer_1.0.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metablogizer_1.0.0-r3_all.ipk index 41eaffb9..007a45d9 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metablogizer_1.0.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metablogizer_1.0.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metabolizer_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metabolizer_1.0.0-r2_all.ipk index 7079d181..7a6b6ec5 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metabolizer_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-metabolizer_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mitmproxy_0.5.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mitmproxy_0.5.0-r1_all.ipk index 375cec6b..b9d1fb15 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mitmproxy_0.5.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mitmproxy_0.5.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mmpm_0.2.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mmpm_0.2.0-r3_all.ipk index 9de0a4e5..af1988f4 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mmpm_0.2.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mmpm_0.2.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mqtt-bridge_0.4.0-r4_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mqtt-bridge_0.4.0-r4_all.ipk index 518ff835..c125a245 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mqtt-bridge_0.4.0-r4_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-mqtt-bridge_0.4.0-r4_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ndpid_1.1.2-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ndpid_1.1.2-r2_all.ipk index 3b2aa92f..3d1f432e 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ndpid_1.1.2-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ndpid_1.1.2-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-netdata-dashboard_0.5.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-netdata-dashboard_0.5.0-r2_all.ipk index d55a1f05..224f4f68 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-netdata-dashboard_0.5.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-netdata-dashboard_0.5.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-modes_0.5.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-modes_0.5.0-r3_all.ipk index 34697ab2..bd1bcaa1 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-modes_0.5.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-modes_0.5.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-tweaks_1.0.0-r7_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-tweaks_1.0.0-r7_all.ipk index 49c0db0e..926b8172 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-tweaks_1.0.0-r7_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-network-tweaks_1.0.0-r7_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-nextcloud_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-nextcloud_1.0.0-r1_all.ipk index 891afb46..7a00a327 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-nextcloud_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-nextcloud_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ollama_0.1.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ollama_0.1.0-r1_all.ipk index cddf9ee6..106c1678 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ollama_0.1.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-ollama_0.1.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-picobrew_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-picobrew_1.0.0-r1_all.ipk index fc656e5b..29085637 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-picobrew_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-picobrew_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-admin_1.0.0-r19_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-admin_1.0.0-r19_all.ipk index 0b9c5f7c..63b192b9 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-admin_1.0.0-r19_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-admin_1.0.0-r19_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-crowdsec_1.0.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-crowdsec_1.0.0-r3_all.ipk index 0ae9de50..b536ab0e 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-crowdsec_1.0.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-crowdsec_1.0.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netdiag_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netdiag_1.0.0-r1_all.ipk index 222c5917..f036c2b7 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netdiag_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netdiag_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netifyd_1.2.1-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netifyd_1.2.1-r1_all.ipk index acff29f9..65235275 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netifyd_1.2.1-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-netifyd_1.2.1-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-p2p_0.1.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-p2p_0.1.0-r1_all.ipk index d1628f13..3977c938 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-p2p_0.1.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-p2p_0.1.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-portal_0.7.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-portal_0.7.0-r2_all.ipk index 6bc7081b..20504b0d 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-portal_0.7.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-portal_0.7.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-security-threats_1.0.0-r4_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-security-threats_1.0.0-r4_all.ipk index 9e5c7371..47b8abe9 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-security-threats_1.0.0-r4_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox-security-threats_1.0.0-r4_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox_0.7.1-r4_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox_0.7.1-r4_all.ipk index a7c6e7cc..19c7db1b 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox_0.7.1-r4_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-secubox_0.7.1-r4_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-service-registry_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-service-registry_1.0.0-r1_all.ipk index d35fe134..ee3e10a6 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-service-registry_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-service-registry_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-streamlit_1.0.0-r9_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-streamlit_1.0.0-r9_all.ipk deleted file mode 100644 index 66dda375..00000000 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-streamlit_1.0.0-r9_all.ipk and /dev/null differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-system-hub_0.5.1-r4_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-system-hub_0.5.1-r4_all.ipk index 157c086f..0f885adf 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-system-hub_0.5.1-r4_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-system-hub_0.5.1-r4_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-tor-shield_1.0.0-r10_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-tor-shield_1.0.0-r10_all.ipk index 0b730e8e..ab1000b9 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-tor-shield_1.0.0-r10_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-tor-shield_1.0.0-r10_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-traffic-shaper_0.4.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-traffic-shaper_0.4.0-r2_all.ipk index 3a1c0154..690b1254 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-traffic-shaper_0.4.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-traffic-shaper_0.4.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-vhost-manager_0.5.0-r5_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-vhost-manager_0.5.0-r5_all.ipk index 7016fe52..ef25c93f 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-vhost-manager_0.5.0-r5_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-vhost-manager_0.5.0-r5_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-wireguard-dashboard_0.7.0-r5_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-wireguard-dashboard_0.7.0-r5_all.ipk index fee8dd07..28d14389 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-wireguard-dashboard_0.7.0-r5_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-wireguard-dashboard_0.7.0-r5_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-zigbee2mqtt_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-zigbee2mqtt_1.0.0-r2_all.ipk index 25252d98..cb0b6e81 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-zigbee2mqtt_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-app-zigbee2mqtt_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-theme-secubox_0.4.7-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-theme-secubox_0.4.7-r1_all.ipk index 77a577b8..fb825f0d 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-theme-secubox_0.4.7-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/luci-theme-secubox_0.4.7-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-adguardhome_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-adguardhome_1.0.0-r2_all.ipk index 5cd80885..32b7a616 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-adguardhome_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-adguardhome_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-auth-logger_1.2.2-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-auth-logger_1.2.2-r1_all.ipk index 9f670ddd..526a5ca4 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-auth-logger_1.2.2-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-auth-logger_1.2.2-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-crowdsec-custom_1.1.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-crowdsec-custom_1.1.0-r1_all.ipk index 2b1ab60a..1a4e2583 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-crowdsec-custom_1.1.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-crowdsec-custom_1.1.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk index 94a2ddd8..26514934 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cs-firewall-bouncer_0.0.31-r4_aarch64_cortex-a72.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cyberfeed_0.2.1-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cyberfeed_0.2.1-r1_all.ipk index fe58c6f6..4980022b 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cyberfeed_0.2.1-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-cyberfeed_0.2.1-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-domoticz_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-domoticz_1.0.0-r2_all.ipk index 4c70480c..7ace986b 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-domoticz_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-domoticz_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-exposure_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-exposure_1.0.0-r1_all.ipk index 45e885d5..1701f479 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-exposure_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-exposure_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-gitea_1.0.0-r5_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-gitea_1.0.0-r5_all.ipk index 7ef68b83..de3cdf99 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-gitea_1.0.0-r5_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-gitea_1.0.0-r5_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-glances_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-glances_1.0.0-r1_all.ipk index 8721e6b8..3a46a562 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-glances_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-glances_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-haproxy_1.0.0-r23_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-haproxy_1.0.0-r23_all.ipk index 11be1101..f9932506 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-haproxy_1.0.0-r23_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-haproxy_1.0.0-r23_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-hexojs_1.0.0-r8_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-hexojs_1.0.0-r8_all.ipk index 27f6bcef..ddfc7f25 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-hexojs_1.0.0-r8_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-hexojs_1.0.0-r8_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-jitsi_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-jitsi_1.0.0-r1_all.ipk index ed4dc7e3..9ca419b4 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-jitsi_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-jitsi_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai-wb_2.25.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai-wb_2.25.0-r1_all.ipk index 21c7cfc6..c22f91b8 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai-wb_2.25.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai-wb_2.25.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai_2.25.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai_2.25.0-r1_all.ipk index 48769d70..7ef19f6e 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai_2.25.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-localai_2.25.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-lyrion_2.0.2-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-lyrion_2.0.2-r1_all.ipk index e23e7545..c1fef271 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-lyrion_2.0.2-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-lyrion_2.0.2-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-magicmirror2_0.4.0-r8_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-magicmirror2_0.4.0-r8_all.ipk index 675461f9..3d0688ee 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-magicmirror2_0.4.0-r8_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-magicmirror2_0.4.0-r8_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mailinabox_2.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mailinabox_2.0.0-r1_all.ipk index 312a3357..98675cee 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mailinabox_2.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mailinabox_2.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-metabolizer_1.0.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-metabolizer_1.0.0-r3_all.ipk index b8b80e64..8477aed8 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-metabolizer_1.0.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-metabolizer_1.0.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mitmproxy_0.5.0-r19_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mitmproxy_0.5.0-r19_all.ipk index 09de1d7a..c9e23f3f 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mitmproxy_0.5.0-r19_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mitmproxy_0.5.0-r19_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mmpm_0.2.0-r5_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mmpm_0.2.0-r5_all.ipk index 6c83937f..e6a2f2c6 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mmpm_0.2.0-r5_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-mmpm_0.2.0-r5_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-nextcloud_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-nextcloud_1.0.0-r2_all.ipk index b59a6ba9..31f06a4a 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-nextcloud_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-nextcloud_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-ollama_0.1.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-ollama_0.1.0-r1_all.ipk index fa02c4f1..2aec9b8a 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-ollama_0.1.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-ollama_0.1.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-picobrew_1.0.0-r7_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-picobrew_1.0.0-r7_all.ipk index 66efc7d7..05639342 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-picobrew_1.0.0-r7_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-picobrew_1.0.0-r7_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-streamlit_1.0.0-r5_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-streamlit_1.0.0-r5_all.ipk index 3e360f5b..1784dc1b 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-streamlit_1.0.0-r5_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-streamlit_1.0.0-r5_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-tor_1.0.0-r1_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-tor_1.0.0-r1_all.ipk index 64e09606..c5569c1f 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-tor_1.0.0-r1_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-tor_1.0.0-r1_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-webapp_1.5.0-r7_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-webapp_1.5.0-r7_all.ipk index 156ae359..bebbdfcd 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-webapp_1.5.0-r7_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-webapp_1.5.0-r7_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk index 25f84be6..428fac8d 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app-zigbee2mqtt_1.0.0-r3_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app_1.0.0-r2_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app_1.0.0-r2_all.ipk index b9e78ba3..9fcd3005 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app_1.0.0-r2_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-app_1.0.0-r2_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-core_0.10.0-r11_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-core_0.10.0-r11_all.ipk index c5d4abeb..30b5dcfa 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-core_0.10.0-r11_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-core_0.10.0-r11_all.ipk differ diff --git a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-p2p_0.6.0-r3_all.ipk b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-p2p_0.6.0-r3_all.ipk index 94268f32..4e86c999 100644 Binary files a/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-p2p_0.6.0-r3_all.ipk and b/package/secubox/secubox-app-bonus/root/www/secubox-feed/secubox-p2p_0.6.0-r3_all.ipk differ