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';
|
'use strict';
|
||||||
'require view';
|
'require view';
|
||||||
'require dom';
|
'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({
|
return view.extend({
|
||||||
handleSaveApply: null,
|
handleSaveApply: null,
|
||||||
@ -9,180 +28,128 @@ return view.extend({
|
|||||||
handleReset: null,
|
handleReset: null,
|
||||||
|
|
||||||
load: function() {
|
load: function() {
|
||||||
return Promise.resolve();
|
return Promise.all([
|
||||||
|
callGetTree(),
|
||||||
|
callGetContainers(),
|
||||||
|
callGetVhosts()
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function(data) {
|
||||||
var TREE = [
|
var treeData = data[0] || {};
|
||||||
{ cat: 'SecuBox Core', items: [
|
var containersData = data[1] || {};
|
||||||
{ name: 'Dashboard', path: 'admin/secubox/dashboard' },
|
var vhostsData = data[2] || {};
|
||||||
{ name: 'App Store', path: 'admin/secubox/apps' },
|
|
||||||
{ name: 'Modules', path: 'admin/secubox/modules' },
|
var categories = treeData.categories || [];
|
||||||
{ name: 'Alerts', path: 'admin/secubox/alerts' },
|
var stats = treeData.stats || {};
|
||||||
{ name: 'Settings', path: 'admin/secubox/settings' }
|
var containers = containersData.containers || [];
|
||||||
]},
|
var vhosts = vhostsData.vhosts || [];
|
||||||
{ 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' }
|
|
||||||
]}
|
|
||||||
];
|
|
||||||
|
|
||||||
var totalLinks = 0;
|
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', {}, [
|
var style = E('style', {}, [
|
||||||
'.luci-tree-page { background: #111; min-height: 100vh; padding: 20px; font-family: monospace; }',
|
'.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 { text-align: center; margin-bottom: 30px; }',
|
||||||
'.luci-tree-header h1 { color: #0f0; font-size: 24px; margin: 0 0 10px 0; }',
|
'.luci-tree-header h1 { color: #0f0; font-size: 24px; margin: 0 0 10px 0; }',
|
||||||
'.luci-tree-header p { color: #888; margin: 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-stats { display: flex; justify-content: center; gap: 20px; margin: 20px 0; flex-wrap: wrap; }',
|
||||||
'.luci-tree-stat { text-align: center; padding: 10px 20px; background: #222; border-radius: 8px; }',
|
'.luci-tree-stat { text-align: center; padding: 10px 15px; background: #222; border-radius: 8px; min-width: 80px; }',
|
||||||
'.luci-tree-stat-value { font-size: 24px; color: #0ff; }',
|
'.luci-tree-stat-value { font-size: 20px; color: #0ff; }',
|
||||||
'.luci-tree-stat-label { font-size: 12px; color: #888; }',
|
'.luci-tree-stat-label { font-size: 11px; color: #888; }',
|
||||||
'.luci-tree-search { margin: 20px auto; max-width: 400px; }',
|
'.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 { 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-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 { 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-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-item { padding: 4px 0; }',
|
'.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 { color: #0ff; text-decoration: none; }',
|
||||||
'.luci-tree-item a:hover { color: #fff; text-decoration: underline; }',
|
'.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'));
|
].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' }, [
|
var header = E('div', { 'class': 'luci-tree-header' }, [
|
||||||
E('h1', {}, 'SecuBox LuCI Navigation Tree'),
|
E('h1', {}, ['SecuBox Navigation Tree ', refreshBtn]),
|
||||||
E('p', {}, 'Clickable map of all LuCI dashboards and modules')
|
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' }, [
|
||||||
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-label' }, 'Categories')
|
||||||
]),
|
]),
|
||||||
E('div', { 'class': 'luci-tree-stat' }, [
|
E('div', { 'class': 'luci-tree-stat' }, [
|
||||||
E('div', { 'class': 'luci-tree-stat-value' }, String(totalLinks)),
|
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' }, [
|
||||||
E('div', { 'class': 'luci-tree-stat-value' }, '60+'),
|
E('div', { 'class': 'luci-tree-stat-value' }, String(stats.packages || 0)),
|
||||||
E('div', { 'class': 'luci-tree-stat-label' }, 'LuCI Apps')
|
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', {
|
var searchInput = E('input', {
|
||||||
'type': 'text',
|
'type': 'text',
|
||||||
'placeholder': 'Search modules...',
|
'placeholder': 'Search modules...',
|
||||||
@ -206,22 +173,62 @@ return view.extend({
|
|||||||
|
|
||||||
var search = E('div', { 'class': 'luci-tree-search' }, [searchInput]);
|
var search = E('div', { 'class': 'luci-tree-search' }, [searchInput]);
|
||||||
|
|
||||||
|
// Panel 0: LuCI Apps Grid
|
||||||
var grid = E('div', { 'class': 'luci-tree-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' }, [
|
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) {
|
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)
|
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);
|
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"]
|
"/bin/sh": ["exec"]
|
||||||
},
|
},
|
||||||
"ubus": {
|
"ubus": {
|
||||||
|
"luci.secubox-portal": [
|
||||||
|
"get_tree",
|
||||||
|
"get_containers",
|
||||||
|
"get_vhosts"
|
||||||
|
],
|
||||||
"luci.secubox": [
|
"luci.secubox": [
|
||||||
"modules",
|
"modules",
|
||||||
"getModules",
|
"getModules",
|
||||||
@ -33,6 +38,11 @@
|
|||||||
"description": "Public access for SecuBox Portal",
|
"description": "Public access for SecuBox Portal",
|
||||||
"read": {
|
"read": {
|
||||||
"ubus": {
|
"ubus": {
|
||||||
|
"luci.secubox-portal": [
|
||||||
|
"get_tree",
|
||||||
|
"get_containers",
|
||||||
|
"get_vhosts"
|
||||||
|
],
|
||||||
"luci.secubox": [
|
"luci.secubox": [
|
||||||
"modules",
|
"modules",
|
||||||
"getModules",
|
"getModules",
|
||||||
|
|||||||
@ -6,7 +6,7 @@ config peertube 'main'
|
|||||||
option timezone 'Europe/Paris'
|
option timezone 'Europe/Paris'
|
||||||
|
|
||||||
config peertube 'server'
|
config peertube 'server'
|
||||||
option hostname 'peertube.local'
|
option hostname 'tube.gk2.secubox.in'
|
||||||
option port '9001'
|
option port '9001'
|
||||||
option https '1'
|
option https '1'
|
||||||
option webserver_hostname ''
|
option webserver_hostname ''
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user