diff --git a/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/luci-tree.js b/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/luci-tree.js index 0143658b..11120ecd 100644 --- a/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/luci-tree.js +++ b/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/luci-tree.js @@ -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]); } }); diff --git a/package/secubox/luci-app-secubox-portal/root/usr/libexec/rpcd/luci.secubox-portal b/package/secubox/luci-app-secubox-portal/root/usr/libexec/rpcd/luci.secubox-portal new file mode 100644 index 00000000..dffba732 --- /dev/null +++ b/package/secubox/luci-app-secubox-portal/root/usr/libexec/rpcd/luci.secubox-portal @@ -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 diff --git a/package/secubox/luci-app-secubox-portal/root/usr/share/rpcd/acl.d/luci-app-secubox-portal.json b/package/secubox/luci-app-secubox-portal/root/usr/share/rpcd/acl.d/luci-app-secubox-portal.json index 9c72ff23..812161ba 100644 --- a/package/secubox/luci-app-secubox-portal/root/usr/share/rpcd/acl.d/luci-app-secubox-portal.json +++ b/package/secubox/luci-app-secubox-portal/root/usr/share/rpcd/acl.d/luci-app-secubox-portal.json @@ -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", diff --git a/package/secubox/secubox-app-peertube/files/etc/config/peertube b/package/secubox/secubox-app-peertube/files/etc/config/peertube index 037bf011..fb1fd34f 100644 --- a/package/secubox/secubox-app-peertube/files/etc/config/peertube +++ b/package/secubox/secubox-app-peertube/files/etc/config/peertube @@ -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 ''