feat(peertube,portal): Add PeerTube config and generative luci-tree
- secubox-app-peertube: Update default port to 9001, hostname to tube.gk2.secubox.in - luci-app-secubox-portal: Add RPCD backend for dynamic tree generation - get_tree: Auto-discovers luci-app-* packages grouped by category - get_containers: Lists LXC containers with running state - get_vhosts: Lists HAProxy virtual hosts - luci-tree.js: Rewritten to use RPC for live data with refresh button Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ffb9fe3785
commit
1a8beb45e0
@ -1,7 +1,26 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require dom';
|
||||
'require secubox/kiss-theme';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
|
||||
var callGetTree = rpc.declare({
|
||||
object: 'luci.secubox-portal',
|
||||
method: 'get_tree',
|
||||
expect: { }
|
||||
});
|
||||
|
||||
var callGetContainers = rpc.declare({
|
||||
object: 'luci.secubox-portal',
|
||||
method: 'get_containers',
|
||||
expect: { }
|
||||
});
|
||||
|
||||
var callGetVhosts = rpc.declare({
|
||||
object: 'luci.secubox-portal',
|
||||
method: 'get_vhosts',
|
||||
expect: { }
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: null,
|
||||
@ -9,180 +28,128 @@ return view.extend({
|
||||
handleReset: null,
|
||||
|
||||
load: function() {
|
||||
return Promise.resolve();
|
||||
return Promise.all([
|
||||
callGetTree(),
|
||||
callGetContainers(),
|
||||
callGetVhosts()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var TREE = [
|
||||
{ cat: 'SecuBox Core', items: [
|
||||
{ name: 'Dashboard', path: 'admin/secubox/dashboard' },
|
||||
{ name: 'App Store', path: 'admin/secubox/apps' },
|
||||
{ name: 'Modules', path: 'admin/secubox/modules' },
|
||||
{ name: 'Alerts', path: 'admin/secubox/alerts' },
|
||||
{ name: 'Settings', path: 'admin/secubox/settings' }
|
||||
]},
|
||||
{ cat: 'Admin Control', items: [
|
||||
{ name: 'Control Panel', path: 'admin/secubox/admin/dashboard' },
|
||||
{ name: 'Cyber Console', path: 'admin/secubox/admin/cyber-dashboard' },
|
||||
{ name: 'Apps Manager', path: 'admin/secubox/admin/apps' },
|
||||
{ name: 'Profiles', path: 'admin/secubox/admin/profiles' },
|
||||
{ name: 'Skills', path: 'admin/secubox/admin/skills' },
|
||||
{ name: 'System Health', path: 'admin/secubox/admin/health' },
|
||||
{ name: 'System Logs', path: 'admin/secubox/admin/logs' }
|
||||
]},
|
||||
{ cat: 'Security', items: [
|
||||
{ name: 'CrowdSec Overview', path: 'admin/secubox/security/crowdsec/overview' },
|
||||
{ name: 'CrowdSec Decisions', path: 'admin/secubox/security/crowdsec/decisions' },
|
||||
{ name: 'CrowdSec Alerts', path: 'admin/secubox/security/crowdsec/alerts' },
|
||||
{ name: 'CrowdSec Bouncers', path: 'admin/secubox/security/crowdsec/bouncers' },
|
||||
{ name: 'mitmproxy Status', path: 'admin/secubox/security/mitmproxy/status' },
|
||||
{ name: 'mitmproxy Settings', path: 'admin/secubox/security/mitmproxy/settings' },
|
||||
{ name: 'Client Guardian', path: 'admin/secubox/security/guardian' },
|
||||
{ name: 'DNS Guard', path: 'admin/secubox/security/dnsguard' },
|
||||
{ name: 'Threat Analyst', path: 'admin/secubox/security/threat-analyst' },
|
||||
{ name: 'Network Anomaly', path: 'admin/secubox/security/network-anomaly' },
|
||||
{ name: 'Auth Guardian', path: 'admin/secubox/security/auth-guardian' },
|
||||
{ name: 'Key Storage Manager', path: 'admin/secubox/security/ksm-manager' }
|
||||
]},
|
||||
{ cat: 'AI Gateway', items: [
|
||||
{ name: 'AI Insights', path: 'admin/secubox/ai/insights' },
|
||||
{ name: 'LocalRecall', path: 'admin/secubox/ai/localrecall' }
|
||||
]},
|
||||
{ cat: 'MirrorBox P2P', items: [
|
||||
{ name: 'Overview', path: 'admin/secubox/mirrorbox/overview' },
|
||||
{ name: 'P2P Hub', path: 'admin/secubox/mirrorbox/hub' },
|
||||
{ name: 'Peers', path: 'admin/secubox/mirrorbox/peers' },
|
||||
{ name: 'Services', path: 'admin/secubox/mirrorbox/services' },
|
||||
{ name: 'Factory', path: 'admin/secubox/mirrorbox/factory' },
|
||||
{ name: 'App Store', path: 'admin/secubox/mirrorbox/packages' },
|
||||
{ name: 'Dev Status', path: 'admin/secubox/mirrorbox/devstatus' }
|
||||
]},
|
||||
{ cat: 'Network', items: [
|
||||
{ name: 'Network Modes', path: 'admin/secubox/network/modes' },
|
||||
{ name: 'DNS Providers', path: 'admin/secubox/network/dns-provider' },
|
||||
{ name: 'Service Exposure', path: 'admin/secubox/network/exposure' },
|
||||
{ name: 'Bandwidth Manager', path: 'admin/secubox/network/bandwidth-manager' },
|
||||
{ name: 'Traffic Shaper', path: 'admin/secubox/network/traffic-shaper' },
|
||||
{ name: 'MQTT Bridge', path: 'admin/secubox/network/mqtt-bridge' },
|
||||
{ name: 'Network Tweaks', path: 'admin/network/network-tweaks' }
|
||||
]},
|
||||
{ cat: 'Monitoring', items: [
|
||||
{ name: 'Netdata Dashboard', path: 'admin/secubox/monitoring/netdata' },
|
||||
{ name: 'Glances', path: 'admin/secubox/monitoring/glances' },
|
||||
{ name: 'Media Flow', path: 'admin/secubox/monitoring/mediaflow' }
|
||||
]},
|
||||
{ cat: 'System', items: [
|
||||
{ name: 'System Hub', path: 'admin/secubox/system/system-hub' },
|
||||
{ name: 'Cloning Station', path: 'admin/secubox/system/cloner' }
|
||||
]},
|
||||
{ cat: 'Device Intelligence', items: [
|
||||
{ name: 'Dashboard', path: 'admin/secubox/device-intel/dashboard' },
|
||||
{ name: 'Devices', path: 'admin/secubox/device-intel/devices' },
|
||||
{ name: 'Mesh', path: 'admin/secubox/device-intel/mesh' }
|
||||
]},
|
||||
{ cat: 'InterceptoR', items: [
|
||||
{ name: 'Overview', path: 'admin/secubox/interceptor/overview' }
|
||||
]},
|
||||
{ cat: 'IoT & Automation', items: [
|
||||
{ name: 'IoT Guard', path: 'admin/secubox/services/iot-guard' },
|
||||
{ name: 'Zigbee2MQTT', path: 'admin/secubox/zigbee2mqtt' },
|
||||
{ name: 'nDPId', path: 'admin/secubox/ndpid' },
|
||||
{ name: 'Netifyd', path: 'admin/secubox/netifyd' }
|
||||
]},
|
||||
{ cat: 'Services - Proxy & VPN', items: [
|
||||
{ name: 'HAProxy', path: 'admin/services/haproxy' },
|
||||
{ name: 'VHost Manager', path: 'admin/services/vhosts' },
|
||||
{ name: 'WireGuard', path: 'admin/services/wireguard' },
|
||||
{ name: 'Tor Shield', path: 'admin/services/tor-shield' },
|
||||
{ name: 'CDN Cache', path: 'admin/services/cdn-cache' }
|
||||
]},
|
||||
{ cat: 'Services - AI & Chat', items: [
|
||||
{ name: 'LocalAI', path: 'admin/services/localai' },
|
||||
{ name: 'Ollama', path: 'admin/services/ollama' },
|
||||
{ name: 'SimpleX Chat', path: 'admin/services/simplex' },
|
||||
{ name: 'Jitsi Meet', path: 'admin/services/jitsi' }
|
||||
]},
|
||||
{ cat: 'Services - Media & Cloud', items: [
|
||||
{ name: 'Nextcloud', path: 'admin/services/nextcloud' },
|
||||
{ name: 'Jellyfin', path: 'admin/services/jellyfin' },
|
||||
{ name: 'Lyrion', path: 'admin/services/lyrion' },
|
||||
{ name: 'MagicMirror', path: 'admin/services/magicmirror2' },
|
||||
{ name: 'MMPM', path: 'admin/services/mmpm' },
|
||||
{ name: 'Streamlit', path: 'admin/services/streamlit' }
|
||||
]},
|
||||
{ cat: 'Services - Home & IoT', items: [
|
||||
{ name: 'Domoticz', path: 'admin/services/domoticz' },
|
||||
{ name: 'MAC Guardian', path: 'admin/services/mac-guardian' },
|
||||
{ name: 'Mesh Link', path: 'admin/services/secubox-mesh' }
|
||||
]},
|
||||
{ cat: 'Services - Dev & CMS', items: [
|
||||
{ name: 'Gitea', path: 'admin/services/gitea' },
|
||||
{ name: 'Hexo CMS', path: 'admin/services/hexojs' },
|
||||
{ name: 'MetaBlogizer', path: 'admin/services/metablogizer' },
|
||||
{ name: 'Metabolizer', path: 'admin/services/metabolizer' },
|
||||
{ name: 'PicoBrew', path: 'admin/services/picobrew' }
|
||||
]},
|
||||
{ cat: 'Services - DNS & Security', items: [
|
||||
{ name: 'Service Registry', path: 'admin/services/service-registry' },
|
||||
{ name: 'Vortex DNS', path: 'admin/services/vortex-dns' },
|
||||
{ name: 'Vortex Firewall', path: 'admin/services/vortex-firewall' },
|
||||
{ name: 'Threat Monitor', path: 'admin/services/threat-monitor' },
|
||||
{ name: 'CyberFeed', path: 'admin/services/cyberfeed' },
|
||||
{ name: 'Config Advisor', path: 'admin/services/config-advisor' },
|
||||
{ name: 'Network Diagnostics', path: 'admin/services/network-diagnostics' }
|
||||
]},
|
||||
{ cat: 'Public Portal', items: [
|
||||
{ name: 'C3BOX Portal', path: 'secubox-public/portal' },
|
||||
{ name: 'Crowdfunding', path: 'secubox-public/crowdfunding' },
|
||||
{ name: 'Bug Bounty', path: 'secubox-public/bugbounty' },
|
||||
{ name: 'Dev Status', path: 'secubox-public/devstatus' }
|
||||
]}
|
||||
];
|
||||
render: function(data) {
|
||||
var treeData = data[0] || {};
|
||||
var containersData = data[1] || {};
|
||||
var vhostsData = data[2] || {};
|
||||
|
||||
var categories = treeData.categories || [];
|
||||
var stats = treeData.stats || {};
|
||||
var containers = containersData.containers || [];
|
||||
var vhosts = vhostsData.vhosts || [];
|
||||
|
||||
var totalLinks = 0;
|
||||
TREE.forEach(function(cat) { totalLinks += cat.items.length; });
|
||||
categories.forEach(function(cat) {
|
||||
if (cat.items) totalLinks += cat.items.length;
|
||||
});
|
||||
|
||||
var runningContainers = containers.filter(function(c) { return c.state === 'running'; }).length;
|
||||
var enabledVhosts = vhosts.filter(function(v) { return v.enabled === '1'; }).length;
|
||||
|
||||
var style = E('style', {}, [
|
||||
'.luci-tree-page { background: #111; min-height: 100vh; padding: 20px; font-family: monospace; }',
|
||||
'.luci-tree-header { text-align: center; margin-bottom: 30px; }',
|
||||
'.luci-tree-header h1 { color: #0f0; font-size: 24px; margin: 0 0 10px 0; }',
|
||||
'.luci-tree-header p { color: #888; margin: 0; }',
|
||||
'.luci-tree-stats { display: flex; justify-content: center; gap: 30px; margin: 20px 0; flex-wrap: wrap; }',
|
||||
'.luci-tree-stat { text-align: center; padding: 10px 20px; background: #222; border-radius: 8px; }',
|
||||
'.luci-tree-stat-value { font-size: 24px; color: #0ff; }',
|
||||
'.luci-tree-stat-label { font-size: 12px; color: #888; }',
|
||||
'.luci-tree-stats { display: flex; justify-content: center; gap: 20px; margin: 20px 0; flex-wrap: wrap; }',
|
||||
'.luci-tree-stat { text-align: center; padding: 10px 15px; background: #222; border-radius: 8px; min-width: 80px; }',
|
||||
'.luci-tree-stat-value { font-size: 20px; color: #0ff; }',
|
||||
'.luci-tree-stat-label { font-size: 11px; color: #888; }',
|
||||
'.luci-tree-search { margin: 20px auto; max-width: 400px; }',
|
||||
'.luci-tree-search input { width: 100%; padding: 10px; background: #222; border: 1px solid #333; color: #fff; border-radius: 4px; box-sizing: border-box; }',
|
||||
'.luci-tree-search input:focus { outline: none; border-color: #0f0; }',
|
||||
'.luci-tree-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; }',
|
||||
'.luci-tree-tabs { display: flex; justify-content: center; gap: 10px; margin: 20px 0; flex-wrap: wrap; }',
|
||||
'.luci-tree-tab { padding: 8px 16px; background: #222; border: 1px solid #333; border-radius: 4px; color: #888; cursor: pointer; }',
|
||||
'.luci-tree-tab:hover { border-color: #0f0; color: #0f0; }',
|
||||
'.luci-tree-tab.active { background: #0f0; color: #000; border-color: #0f0; }',
|
||||
'.luci-tree-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 15px; }',
|
||||
'.luci-tree-section { background: #1a1a1a; border-left: 3px solid #0f0; border-radius: 4px; padding: 15px; }',
|
||||
'.luci-tree-section-title { color: #0f0; font-size: 16px; margin: 0 0 10px 0; border-bottom: 1px solid #333; padding-bottom: 8px; }',
|
||||
'.luci-tree-item { padding: 4px 0; }',
|
||||
'.luci-tree-section-title { color: #0f0; font-size: 14px; margin: 0 0 10px 0; border-bottom: 1px solid #333; padding-bottom: 8px; display: flex; justify-content: space-between; }',
|
||||
'.luci-tree-section-count { color: #888; font-size: 12px; }',
|
||||
'.luci-tree-item { padding: 3px 0; font-size: 13px; }',
|
||||
'.luci-tree-item a { color: #0ff; text-decoration: none; }',
|
||||
'.luci-tree-item a:hover { color: #fff; text-decoration: underline; }',
|
||||
'.luci-tree-item::before { content: "- "; color: #555; }'
|
||||
'.luci-tree-item::before { content: "- "; color: #555; }',
|
||||
'.luci-tree-item .pkg { color: #666; font-size: 10px; margin-left: 5px; }',
|
||||
'.luci-tree-panel { display: none; }',
|
||||
'.luci-tree-panel.active { display: block; }',
|
||||
'.luci-tree-container { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 15px; }',
|
||||
'.luci-tree-container-item { padding: 8px 12px; background: #222; border-radius: 4px; display: flex; align-items: center; gap: 8px; }',
|
||||
'.luci-tree-container-status { width: 8px; height: 8px; border-radius: 50%; }',
|
||||
'.luci-tree-container-status.running { background: #0f0; }',
|
||||
'.luci-tree-container-status.stopped { background: #f00; }',
|
||||
'.luci-tree-vhost { padding: 8px 12px; background: #222; border-radius: 4px; margin-bottom: 8px; }',
|
||||
'.luci-tree-vhost-domain { color: #0ff; }',
|
||||
'.luci-tree-vhost-backend { color: #888; font-size: 11px; }',
|
||||
'.luci-tree-refresh { margin-left: 10px; padding: 5px 10px; background: #333; border: none; color: #0f0; border-radius: 4px; cursor: pointer; }',
|
||||
'.luci-tree-refresh:hover { background: #444; }'
|
||||
].join('\n'));
|
||||
|
||||
var self = this;
|
||||
|
||||
var refreshBtn = E('button', {
|
||||
'class': 'luci-tree-refresh',
|
||||
'click': function() {
|
||||
self.load().then(function(newData) {
|
||||
var content = document.querySelector('.luci-tree-page');
|
||||
if (content) {
|
||||
dom.content(content.parentNode, self.render(newData));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 'Refresh');
|
||||
|
||||
var header = E('div', { 'class': 'luci-tree-header' }, [
|
||||
E('h1', {}, 'SecuBox LuCI Navigation Tree'),
|
||||
E('p', {}, 'Clickable map of all LuCI dashboards and modules')
|
||||
E('h1', {}, ['SecuBox Navigation Tree ', refreshBtn]),
|
||||
E('p', {}, 'Auto-generated map of all SecuBox components and services')
|
||||
]);
|
||||
|
||||
var stats = E('div', { 'class': 'luci-tree-stats' }, [
|
||||
var statsEl = E('div', { 'class': 'luci-tree-stats' }, [
|
||||
E('div', { 'class': 'luci-tree-stat' }, [
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, String(TREE.length)),
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, String(categories.length)),
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'Categories')
|
||||
]),
|
||||
E('div', { 'class': 'luci-tree-stat' }, [
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, String(totalLinks)),
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'Total Links')
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'LuCI Apps')
|
||||
]),
|
||||
E('div', { 'class': 'luci-tree-stat' }, [
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, '60+'),
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'LuCI Apps')
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, String(stats.packages || 0)),
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'Packages')
|
||||
]),
|
||||
E('div', { 'class': 'luci-tree-stat' }, [
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, runningContainers + '/' + containers.length),
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'Containers')
|
||||
]),
|
||||
E('div', { 'class': 'luci-tree-stat' }, [
|
||||
E('div', { 'class': 'luci-tree-stat-value' }, String(enabledVhosts)),
|
||||
E('div', { 'class': 'luci-tree-stat-label' }, 'Vhosts')
|
||||
])
|
||||
]);
|
||||
|
||||
var tabs = E('div', { 'class': 'luci-tree-tabs' });
|
||||
var tabNames = ['LuCI Apps', 'Containers', 'Vhosts'];
|
||||
tabNames.forEach(function(name, idx) {
|
||||
var tab = E('div', {
|
||||
'class': 'luci-tree-tab' + (idx === 0 ? ' active' : ''),
|
||||
'data-tab': idx,
|
||||
'click': function(ev) {
|
||||
document.querySelectorAll('.luci-tree-tab').forEach(function(t) { t.classList.remove('active'); });
|
||||
document.querySelectorAll('.luci-tree-panel').forEach(function(p) { p.classList.remove('active'); });
|
||||
ev.target.classList.add('active');
|
||||
document.querySelector('.luci-tree-panel[data-panel="' + idx + '"]').classList.add('active');
|
||||
}
|
||||
}, name);
|
||||
tabs.appendChild(tab);
|
||||
});
|
||||
|
||||
var searchInput = E('input', {
|
||||
'type': 'text',
|
||||
'placeholder': 'Search modules...',
|
||||
@ -206,22 +173,62 @@ return view.extend({
|
||||
|
||||
var search = E('div', { 'class': 'luci-tree-search' }, [searchInput]);
|
||||
|
||||
// Panel 0: LuCI Apps Grid
|
||||
var grid = E('div', { 'class': 'luci-tree-grid' });
|
||||
categories.forEach(function(category) {
|
||||
if (!category.items || category.items.length === 0) return;
|
||||
|
||||
TREE.forEach(function(category) {
|
||||
var section = E('div', { 'class': 'luci-tree-section' }, [
|
||||
E('div', { 'class': 'luci-tree-section-title' }, category.cat)
|
||||
E('div', { 'class': 'luci-tree-section-title' }, [
|
||||
E('span', {}, category.cat),
|
||||
E('span', { 'class': 'luci-tree-section-count' }, '(' + category.items.length + ')')
|
||||
])
|
||||
]);
|
||||
|
||||
category.items.forEach(function(item) {
|
||||
section.appendChild(E('div', { 'class': 'luci-tree-item' }, [
|
||||
var itemEl = E('div', { 'class': 'luci-tree-item' }, [
|
||||
E('a', { 'href': '/cgi-bin/luci/' + item.path, 'target': '_blank' }, item.name)
|
||||
]));
|
||||
]);
|
||||
if (item.package) {
|
||||
itemEl.appendChild(E('span', { 'class': 'pkg' }, item.package));
|
||||
}
|
||||
section.appendChild(itemEl);
|
||||
});
|
||||
|
||||
grid.appendChild(section);
|
||||
});
|
||||
|
||||
return KissTheme.wrap([E('div', { 'class': 'luci-tree-page' }, [style, header, stats, search, grid])], 'admin/secubox/portal/luci-tree');
|
||||
var panel0 = E('div', { 'class': 'luci-tree-panel active', 'data-panel': '0' }, [search, grid]);
|
||||
|
||||
// Panel 1: Containers
|
||||
var containerGrid = E('div', { 'class': 'luci-tree-container' });
|
||||
containers.forEach(function(c) {
|
||||
containerGrid.appendChild(E('div', { 'class': 'luci-tree-container-item' }, [
|
||||
E('div', { 'class': 'luci-tree-container-status ' + c.state }),
|
||||
E('span', {}, c.name)
|
||||
]));
|
||||
});
|
||||
var panel1 = E('div', { 'class': 'luci-tree-panel', 'data-panel': '1' }, [
|
||||
E('h3', { 'style': 'color: #0f0; margin: 20px 0 10px 0;' }, 'LXC Containers (' + containers.length + ')'),
|
||||
containerGrid
|
||||
]);
|
||||
|
||||
// Panel 2: Vhosts
|
||||
var vhostList = E('div', { 'style': 'max-height: 500px; overflow-y: auto; margin-top: 15px;' });
|
||||
vhosts.forEach(function(v) {
|
||||
if (!v.domain) return;
|
||||
vhostList.appendChild(E('div', { 'class': 'luci-tree-vhost' }, [
|
||||
E('div', { 'class': 'luci-tree-vhost-domain' }, [
|
||||
E('a', { 'href': 'https://' + v.domain, 'target': '_blank', 'style': 'color: #0ff; text-decoration: none;' }, v.domain)
|
||||
]),
|
||||
E('div', { 'class': 'luci-tree-vhost-backend' }, 'Backend: ' + (v.backend || 'N/A') + (v.ssl === '1' ? ' | SSL' : ''))
|
||||
]));
|
||||
});
|
||||
var panel2 = E('div', { 'class': 'luci-tree-panel', 'data-panel': '2' }, [
|
||||
E('h3', { 'style': 'color: #0f0; margin: 20px 0 10px 0;' }, 'HAProxy Virtual Hosts (' + vhosts.length + ')'),
|
||||
vhostList
|
||||
]);
|
||||
|
||||
return E('div', { 'class': 'luci-tree-page' }, [style, header, statsEl, tabs, panel0, panel1, panel2]);
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,347 @@
|
||||
#!/bin/sh
|
||||
|
||||
# RPCD backend for SecuBox Portal - Generative LuCI Tree
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
# Discover LuCI menu entries from menu.d JSON files
|
||||
discover_luci_menus() {
|
||||
local menus=""
|
||||
for f in /usr/share/luci/menu.d/*.json; do
|
||||
[ -f "$f" ] || continue
|
||||
# Extract paths from JSON - format: "admin/path": {...}
|
||||
local paths=$(grep -oE '"admin/[^"]+":' "$f" 2>/dev/null | tr -d '":' | sort -u)
|
||||
for p in $paths; do
|
||||
menus="$menus $p"
|
||||
done
|
||||
done
|
||||
echo "$menus" | tr ' ' '\n' | sort -u | grep -v '^$'
|
||||
}
|
||||
|
||||
# Discover installed SecuBox packages
|
||||
discover_packages() {
|
||||
opkg list-installed 2>/dev/null | grep -E '^(secubox|luci-app-)' | awk '{print $1}'
|
||||
}
|
||||
|
||||
# Discover LXC containers
|
||||
discover_containers() {
|
||||
ls /srv/lxc/ 2>/dev/null | while read c; do
|
||||
local state="stopped"
|
||||
if lxc-info -n "$c" 2>/dev/null | grep -q "State:.*RUNNING"; then
|
||||
state="running"
|
||||
fi
|
||||
echo "$c:$state"
|
||||
done
|
||||
}
|
||||
|
||||
# Discover HAProxy vhosts
|
||||
discover_vhosts() {
|
||||
uci show haproxy 2>/dev/null | grep "=vhost" | sed 's/haproxy\.//;s/=vhost//' | while read vh; do
|
||||
local domain=$(uci -q get haproxy.$vh.domain)
|
||||
local backend=$(uci -q get haproxy.$vh.backend)
|
||||
local enabled=$(uci -q get haproxy.$vh.enabled)
|
||||
[ "$enabled" = "1" ] && echo "$domain:$backend"
|
||||
done
|
||||
}
|
||||
|
||||
# Discover running services from init.d
|
||||
discover_services() {
|
||||
for f in /etc/init.d/*; do
|
||||
[ -x "$f" ] || continue
|
||||
local name=$(basename "$f")
|
||||
# Skip system services
|
||||
case "$name" in
|
||||
boot|done|rcS|umount|sysctl|urandom|gpio*|led*|log*) continue ;;
|
||||
esac
|
||||
local enabled="0"
|
||||
local running="0"
|
||||
[ -L "/etc/rc.d/S"*"$name" ] 2>/dev/null && enabled="1"
|
||||
"$f" status >/dev/null 2>&1 && running="1"
|
||||
echo "$name:$enabled:$running"
|
||||
done 2>/dev/null
|
||||
}
|
||||
|
||||
# Build tree categories from LuCI menus
|
||||
build_tree() {
|
||||
json_init
|
||||
json_add_array "categories"
|
||||
|
||||
# SecuBox Core
|
||||
json_add_object ""
|
||||
json_add_string "cat" "SecuBox Core"
|
||||
json_add_array "items"
|
||||
for path in admin/secubox/dashboard admin/secubox/apps admin/secubox/modules admin/secubox/alerts admin/secubox/settings; do
|
||||
if [ -f "/usr/share/luci/menu.d/"*".json" ]; then
|
||||
json_add_object ""
|
||||
json_add_string "name" "$(echo "$path" | sed 's|.*/||' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')"
|
||||
json_add_string "path" "$path"
|
||||
json_close_object
|
||||
fi
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# Discover all luci-app-* packages and group them
|
||||
local apps=$(opkg list-installed 2>/dev/null | grep "^luci-app-" | awk '{print $1}' | sort)
|
||||
|
||||
# Security Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "Security"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-crowdsec*|luci-app-mitmproxy*|luci-app-guardian*|luci-app-dnsguard*|luci-app-threat*|luci-app-wazuh*|luci-app-vortex*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# Media & Streaming Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "Media & Streaming"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-jellyfin*|luci-app-lyrion*|luci-app-streamlit*|luci-app-peertube*|luci-app-magicmirror*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# Network & Proxy Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "Network & Proxy"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-haproxy*|luci-app-wireguard*|luci-app-tor*|luci-app-cdn*|luci-app-exposure*|luci-app-dns-provider*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# Development & CMS Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "Development & CMS"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-gitea*|luci-app-hexo*|luci-app-metablog*|luci-app-metabol*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# IoT & Home Automation Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "IoT & Home"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-domoticz*|luci-app-zigbee*|luci-app-iot*|luci-app-mqtt*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# AI & Chat Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "AI & Communication"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-localai*|luci-app-ollama*|luci-app-simplex*|luci-app-gotosocial*|luci-app-ai-*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# System & Management Apps
|
||||
json_add_object ""
|
||||
json_add_string "cat" "System & Management"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-cloner*|luci-app-backup*|luci-app-system*|luci-app-config-advisor*|luci-app-service-registry*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# Other SecuBox Apps (catch-all)
|
||||
json_add_object ""
|
||||
json_add_string "cat" "Other SecuBox Apps"
|
||||
json_add_array "items"
|
||||
for app in $apps; do
|
||||
case "$app" in
|
||||
luci-app-crowdsec*|luci-app-mitmproxy*|luci-app-guardian*|luci-app-dnsguard*|luci-app-threat*|luci-app-wazuh*|luci-app-vortex*) continue ;;
|
||||
luci-app-jellyfin*|luci-app-lyrion*|luci-app-streamlit*|luci-app-peertube*|luci-app-magicmirror*) continue ;;
|
||||
luci-app-haproxy*|luci-app-wireguard*|luci-app-tor*|luci-app-cdn*|luci-app-exposure*|luci-app-dns-provider*) continue ;;
|
||||
luci-app-gitea*|luci-app-hexo*|luci-app-metablog*|luci-app-metabol*) continue ;;
|
||||
luci-app-domoticz*|luci-app-zigbee*|luci-app-iot*|luci-app-mqtt*) continue ;;
|
||||
luci-app-localai*|luci-app-ollama*|luci-app-simplex*|luci-app-gotosocial*|luci-app-ai-*) continue ;;
|
||||
luci-app-cloner*|luci-app-backup*|luci-app-system*|luci-app-config-advisor*|luci-app-service-registry*) continue ;;
|
||||
luci-app-secubox*|luci-app-*secubox*)
|
||||
local name=$(echo "$app" | sed 's/luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1')
|
||||
json_add_object ""
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "path" "admin/secubox/$(echo "$app" | sed 's/luci-app-secubox-//' | sed 's/luci-app-//')"
|
||||
json_add_string "package" "$app"
|
||||
json_close_object
|
||||
;;
|
||||
esac
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
json_close_array
|
||||
|
||||
# Add stats
|
||||
local pkg_count=$(echo "$apps" | wc -w)
|
||||
local container_count=$(ls /srv/lxc/ 2>/dev/null | wc -l)
|
||||
local vhost_count=$(uci show haproxy 2>/dev/null | grep "=vhost" | wc -l)
|
||||
|
||||
json_add_object "stats"
|
||||
json_add_int "packages" "$pkg_count"
|
||||
json_add_int "containers" "$container_count"
|
||||
json_add_int "vhosts" "$vhost_count"
|
||||
json_close_object
|
||||
|
||||
json_dump
|
||||
}
|
||||
|
||||
# Method: get_tree
|
||||
method_get_tree() {
|
||||
build_tree
|
||||
}
|
||||
|
||||
# Method: get_containers
|
||||
method_get_containers() {
|
||||
json_init
|
||||
json_add_array "containers"
|
||||
|
||||
for c in $(ls /srv/lxc/ 2>/dev/null); do
|
||||
json_add_object ""
|
||||
json_add_string "name" "$c"
|
||||
if lxc-info -n "$c" 2>/dev/null | grep -q "State:.*RUNNING"; then
|
||||
json_add_string "state" "running"
|
||||
else
|
||||
json_add_string "state" "stopped"
|
||||
fi
|
||||
json_close_object
|
||||
done
|
||||
|
||||
json_close_array
|
||||
json_dump
|
||||
}
|
||||
|
||||
# Method: get_vhosts
|
||||
method_get_vhosts() {
|
||||
json_init
|
||||
json_add_array "vhosts"
|
||||
|
||||
for vh in $(uci show haproxy 2>/dev/null | grep "=vhost" | sed 's/haproxy\.//;s/=vhost//'); do
|
||||
local domain=$(uci -q get haproxy.$vh.domain)
|
||||
local backend=$(uci -q get haproxy.$vh.backend)
|
||||
local enabled=$(uci -q get haproxy.$vh.enabled)
|
||||
local ssl=$(uci -q get haproxy.$vh.ssl)
|
||||
|
||||
json_add_object ""
|
||||
json_add_string "id" "$vh"
|
||||
json_add_string "domain" "$domain"
|
||||
json_add_string "backend" "$backend"
|
||||
json_add_string "enabled" "${enabled:-0}"
|
||||
json_add_string "ssl" "${ssl:-0}"
|
||||
json_close_object
|
||||
done
|
||||
|
||||
json_close_array
|
||||
json_dump
|
||||
}
|
||||
|
||||
# List available methods
|
||||
list_methods() {
|
||||
json_init
|
||||
json_add_object "get_tree"
|
||||
json_close_object
|
||||
json_add_object "get_containers"
|
||||
json_close_object
|
||||
json_add_object "get_vhosts"
|
||||
json_close_object
|
||||
json_dump
|
||||
}
|
||||
|
||||
# Main dispatcher
|
||||
case "$1" in
|
||||
list)
|
||||
list_methods
|
||||
;;
|
||||
call)
|
||||
case "$2" in
|
||||
get_tree)
|
||||
method_get_tree
|
||||
;;
|
||||
get_containers)
|
||||
method_get_containers
|
||||
;;
|
||||
get_vhosts)
|
||||
method_get_vhosts
|
||||
;;
|
||||
*)
|
||||
echo '{"error":"Method not found"}'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo '{"error":"Invalid action"}'
|
||||
;;
|
||||
esac
|
||||
@ -7,6 +7,11 @@
|
||||
"/bin/sh": ["exec"]
|
||||
},
|
||||
"ubus": {
|
||||
"luci.secubox-portal": [
|
||||
"get_tree",
|
||||
"get_containers",
|
||||
"get_vhosts"
|
||||
],
|
||||
"luci.secubox": [
|
||||
"modules",
|
||||
"getModules",
|
||||
@ -33,6 +38,11 @@
|
||||
"description": "Public access for SecuBox Portal",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.secubox-portal": [
|
||||
"get_tree",
|
||||
"get_containers",
|
||||
"get_vhosts"
|
||||
],
|
||||
"luci.secubox": [
|
||||
"modules",
|
||||
"getModules",
|
||||
|
||||
@ -6,7 +6,7 @@ config peertube 'main'
|
||||
option timezone 'Europe/Paris'
|
||||
|
||||
config peertube 'server'
|
||||
option hostname 'peertube.local'
|
||||
option hostname 'tube.gk2.secubox.in'
|
||||
option port '9001'
|
||||
option https '1'
|
||||
option webserver_hostname ''
|
||||
|
||||
Loading…
Reference in New Issue
Block a user