fix(luci): Performance and UX improvements for exposure and portal

- Optimize exposure RPCD: O(n) single-pass awk parsing for vhost_list
  and ssl_list (fixes XHR timeout on 200+ vhosts)
- Fix portal tree URLs: Use get_menu_path() to read actual LuCI menu
  paths from JSON instead of hardcoded paths
- Add Downloads category to portal tree (torrent, droplet patterns)
- Add new apps to System category (config-vault, reporter, smtp-relay,
  rtty, dpi-dual, metacatalog)
- Enhance KISS theme menu: Add Downloads, Monitoring categories
- Fix Lyrion URL: Use HTTPS vhost instead of dynamic port URL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-03-16 07:35:18 +01:00
parent b08e71fa7f
commit d0cd42e2a1
4 changed files with 290 additions and 138 deletions

View File

@ -270,44 +270,52 @@ case "$1" in
;; ;;
ssl_list) ssl_list)
TMP_SSLLIST="/tmp/exposure_ssllist_$$" # OPTIMIZED: Single-pass awk parsing
> "$TMP_SSLLIST" uci show haproxy 2>/dev/null | awk '
BEGIN {
# Read from HAProxy UCI config (vhosts with their backends) printf "{\"backends\":["
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do first = 1
domain=$(uci -q get "haproxy.${vhost}.domain") }
backend=$(uci -q get "haproxy.${vhost}.backend") /=vhost$/ {
enabled=$(uci -q get "haproxy.${vhost}.enabled") if (vh_domain != "" && vh_enabled == "1") {
if (first == 0) printf ","
[ "$enabled" != "1" ] && continue first = 0
[ -z "$domain" ] && continue printf "{\"service\":\"%s\",\"domain\":\"%s\",\"backend\":\"%s\"}",
(vh_backend != "" ? vh_backend : vh_id), vh_domain, "N/A"
# Get server address from backend config }
server="" gsub(/^haproxy\./, "", $0)
if [ -n "$backend" ]; then gsub(/=vhost$/, "", $0)
server=$(uci -q get "haproxy.${backend}.server" 2>/dev/null | head -1 | awk '{print $2}') vh_id = $0
fi vh_domain = ""
vh_backend = ""
echo "${backend:-$vhost}|${domain}|${server:-N/A}" >> "$TMP_SSLLIST" vh_enabled = "0"
done in_vhost = 1
}
json_init /=backend$/ || /=server$/ { in_vhost = 0 }
json_add_array "backends" /\.domain=/ && in_vhost {
gsub(/.*\.domain=/, "", $0)
if [ -s "$TMP_SSLLIST" ]; then gsub(/'\''/, "", $0)
while IFS='|' read service domain server; do vh_domain = $0
[ -z "$service" ] && continue }
json_add_object "" /\.backend=/ && in_vhost {
json_add_string "service" "$service" gsub(/.*\.backend=/, "", $0)
json_add_string "domain" "$domain" gsub(/'\''/, "", $0)
json_add_string "backend" "$server" vh_backend = $0
json_close_object }
done < "$TMP_SSLLIST" /\.enabled=/ && in_vhost {
fi gsub(/.*\.enabled=/, "", $0)
rm -f "$TMP_SSLLIST" gsub(/'\''/, "", $0)
vh_enabled = $0
json_close_array }
json_dump END {
if (vh_domain != "" && vh_enabled == "1") {
if (first == 0) printf ","
printf "{\"service\":\"%s\",\"domain\":\"%s\",\"backend\":\"%s\"}",
(vh_backend != "" ? vh_backend : vh_id), vh_domain, "N/A"
}
printf "]}"
}
'
;; ;;
get_config) get_config)
@ -479,80 +487,171 @@ case "$1" in
;; ;;
vhost_list) vhost_list)
json_init # OPTIMIZED: Single-pass awk parsing instead of O(n²) uci calls
uci show haproxy 2>/dev/null | awk '
BEGIN {
printf "{\"haproxy\":["
first_vh = 1
}
# HAProxy vhosts (domain -> backend with resolved port) # Collect server backend->port mappings
json_add_array "haproxy" /\.backend=/ && /=server$/ == 0 {
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do # This is a vhost or backend .backend= line, skip for server collection
domain=$(uci -q get "haproxy.${vhost}.domain") }
backend=$(uci -q get "haproxy.${vhost}.backend") /=server$/ {
enabled=$(uci -q get "haproxy.${vhost}.enabled") gsub(/^haproxy\./, "", $0)
ssl=$(uci -q get "haproxy.${vhost}.ssl") gsub(/=server$/, "", $0)
acme=$(uci -q get "haproxy.${vhost}.acme") current_srv = $0
}
/^haproxy\.[^.]+\.backend=/ && prev_type == "server" {
gsub(/^haproxy\.[^.]+\.backend=/, "", $0)
gsub(/'\''/, "", $0)
srv_backends[current_srv] = $0
}
/^haproxy\.[^.]+\.port=/ && prev_type == "server" {
gsub(/^haproxy\.[^.]+\.port=/, "", $0)
gsub(/'\''/, "", $0)
srv_ports[current_srv] = $0
}
[ -z "$domain" ] && continue # Track section type
/=vhost$/ { prev_type = "vhost" }
/=server$/ { prev_type = "server" }
/=backend$/ { prev_type = "backend" }
# Check for original_backend (when mitmproxy is intercepting) # Process vhosts
original_backend=$(uci -q get "haproxy.${vhost}.original_backend") /=vhost$/ {
resolve_backend="${original_backend:-$backend}" # Output previous vhost
if (vh_id != "" && vh_domain != "") {
if (first_vh == 0) printf ","
first_vh = 0
# Resolve backend port
resolve_be = (vh_orig_be != "") ? vh_orig_be : vh_backend
port = 0
for (s in srv_backends) {
if (srv_backends[s] == resolve_be && srv_ports[s] != "") {
port = srv_ports[s]
break
}
}
printf "{\"id\":\"%s\",\"domain\":\"%s\",\"backend\":\"%s\",\"backend_port\":%d,\"ssl\":%s,\"acme\":%s,\"enabled\":%s}",
vh_id, vh_domain, resolve_be, port,
(vh_ssl == "1" ? "true" : "false"),
(vh_acme == "1" ? "true" : "false"),
(vh_enabled == "1" ? "true" : "false")
}
# Start new vhost
gsub(/^haproxy\./, "", $0)
gsub(/=vhost$/, "", $0)
vh_id = $0
vh_domain = ""
vh_backend = ""
vh_orig_be = ""
vh_ssl = "0"
vh_acme = "0"
vh_enabled = "0"
}
/\.domain=/ && prev_type == "vhost" {
gsub(/.*\.domain=/, "", $0)
gsub(/'\''/, "", $0)
vh_domain = $0
}
/\.backend=/ && prev_type == "vhost" {
gsub(/.*\.backend=/, "", $0)
gsub(/'\''/, "", $0)
vh_backend = $0
}
/\.original_backend=/ && prev_type == "vhost" {
gsub(/.*\.original_backend=/, "", $0)
gsub(/'\''/, "", $0)
vh_orig_be = $0
}
/\.ssl=/ && prev_type == "vhost" {
gsub(/.*\.ssl=/, "", $0)
gsub(/'\''/, "", $0)
vh_ssl = $0
}
/\.acme=/ && prev_type == "vhost" {
gsub(/.*\.acme=/, "", $0)
gsub(/'\''/, "", $0)
vh_acme = $0
}
/\.enabled=/ && prev_type == "vhost" {
gsub(/.*\.enabled=/, "", $0)
gsub(/'\''/, "", $0)
vh_enabled = $0
}
# Resolve backend port from the target backend END {
backend_port="" # Output last vhost
if [ -n "$resolve_backend" ]; then if (vh_id != "" && vh_domain != "") {
# Try inline server option: 'name IP:PORT check' if (first_vh == 0) printf ","
server_line=$(uci -q get "haproxy.${resolve_backend}.server" 2>/dev/null) resolve_be = (vh_orig_be != "") ? vh_orig_be : vh_backend
if [ -n "$server_line" ]; then port = 0
backend_port=$(echo "$server_line" | awk '{print $2}' | grep -o ':[0-9]*' | tr -d ':') for (s in srv_backends) {
fi if (srv_backends[s] == resolve_be && srv_ports[s] != "") {
# Try server sections referencing this backend port = srv_ports[s]
if [ -z "$backend_port" ]; then break
for srv in $(uci show haproxy 2>/dev/null | grep "=server$" | cut -d'.' -f2 | cut -d'=' -f1); do }
srv_backend=$(uci -q get "haproxy.${srv}.backend") }
if [ "$srv_backend" = "$resolve_backend" ]; then printf "{\"id\":\"%s\",\"domain\":\"%s\",\"backend\":\"%s\",\"backend_port\":%d,\"ssl\":%s,\"acme\":%s,\"enabled\":%s}",
backend_port=$(uci -q get "haproxy.${srv}.port") vh_id, vh_domain, resolve_be, port,
break (vh_ssl == "1" ? "true" : "false"),
fi (vh_acme == "1" ? "true" : "false"),
done (vh_enabled == "1" ? "true" : "false")
fi }
fi printf "],"
}
'
json_add_object "" # uhttpd vhosts - also optimized with awk
json_add_string "id" "$vhost" uci show uhttpd 2>/dev/null | awk '
json_add_string "domain" "$domain" BEGIN {
json_add_string "backend" "${resolve_backend:-${backend:-}}" printf "\"uhttpd\":["
json_add_int "backend_port" "${backend_port:-0}" first = 1
json_add_boolean "ssl" "${ssl:-0}" }
json_add_boolean "acme" "${acme:-0}" /=uhttpd$/ {
json_add_boolean "enabled" "${enabled:-0}" if (section != "" && section != "main" && section != "acme" && listen != "") {
json_close_object if (first == 0) printf ","
done first = 0
json_close_array # Extract port from listen
match(listen, /[0-9]+$/)
# uhttpd vhosts (non-main instances) port = substr(listen, RSTART, RLENGTH)
json_add_array "uhttpd" gsub(/^metablog_site_/, "", section)
for section in $(uci show uhttpd 2>/dev/null | grep "=uhttpd$" | cut -d'.' -f2 | cut -d'=' -f1); do gsub(/_/, " ", section)
[ "$section" = "main" ] && continue printf "{\"id\":\"%s\",\"port\":%s,\"name\":\"%s\",\"home\":\"%s\"}",
[ "$section" = "acme" ] && continue orig_section, (port != "" ? port : "0"), section, home
}
listen=$(uci -q get "uhttpd.${section}.listen_http") gsub(/^uhttpd\./, "", $0)
home=$(uci -q get "uhttpd.${section}.home") gsub(/=uhttpd$/, "", $0)
[ -z "$listen" ] && continue orig_section = $0
section = $0
port=$(echo "$listen" | grep -o '[0-9]*$') listen = ""
home = ""
# Derive friendly name from section id }
fname=$(echo "$section" | sed 's/^metablog_site_//' | sed 's/_/ /g') /\.listen_http=/ {
gsub(/.*\.listen_http=/, "", $0)
json_add_object "" gsub(/'\''/, "", $0)
json_add_string "id" "$section" listen = $0
json_add_int "port" "${port:-0}" }
json_add_string "name" "$fname" /\.home=/ {
json_add_string "home" "${home:-}" gsub(/.*\.home=/, "", $0)
json_close_object gsub(/'\''/, "", $0)
done home = $0
json_close_array }
END {
json_dump if (section != "" && section != "main" && section != "acme" && listen != "") {
if (first == 0) printf ","
match(listen, /[0-9]+$/)
port = substr(listen, RSTART, RLENGTH)
gsub(/^metablog_site_/, "", section)
gsub(/_/, " ", section)
printf "{\"id\":\"%s\",\"port\":%s,\"name\":\"%s\",\"home\":\"%s\"}",
orig_section, (port != "" ? port : "0"), section, home
}
printf "]}"
}
'
;; ;;
emancipate) emancipate)

View File

@ -145,13 +145,13 @@ return view.extend({
E('h3', {}, _('Web Interface')), E('h3', {}, _('Web Interface')),
E('div', { 'style': 'margin-bottom:12px' }, [ E('div', { 'style': 'margin-bottom:12px' }, [
E('a', { E('a', {
'href': 'http://' + window.location.hostname + ':' + (s.port || 9000), 'href': 'https://lyrion.gk2.secubox.in/',
'target': '_blank', 'target': '_blank',
'class': 'cbi-button cbi-button-action', 'class': 'cbi-button cbi-button-action',
'style': 'margin-right:8px' 'style': 'margin-right:8px'
}, _('Open Lyrion Web UI')), }, _('Open Lyrion Web UI')),
E('span', { 'style': 'color:#888' }, E('span', { 'style': 'color:#888' },
'http://' + window.location.hostname + ':' + (s.port || 9000)) 'https://lyrion.gk2.secubox.in/')
]) ])
]), ]),

View File

@ -4,6 +4,20 @@
. /usr/share/libubox/jshn.sh . /usr/share/libubox/jshn.sh
# Get actual menu path for a luci-app package
get_menu_path() {
local pkg="$1"
local menu_file="/usr/share/luci/menu.d/${pkg}.json"
if [ -f "$menu_file" ]; then
local path=$(grep -o '"admin/[^"]*"' "$menu_file" | head -1 | tr -d '"')
if [ -n "$path" ]; then
echo "$path"
return
fi
fi
echo "admin/services/$(echo "$pkg" | sed 's/luci-app-//')"
}
# Discover LuCI menu entries from menu.d JSON files # Discover LuCI menu entries from menu.d JSON files
discover_luci_menus() { discover_luci_menus() {
local menus="" local menus=""
@ -90,11 +104,11 @@ build_tree() {
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in case "$app" in
luci-app-crowdsec*|luci-app-mitmproxy*|luci-app-guardian*|luci-app-dnsguard*|luci-app-threat*|luci-app-wazuh*|luci-app-vortex*) luci-app-crowdsec*|luci-app-mitmproxy*|luci-app-guardian*|luci-app-dnsguard*|luci-app-threat*|luci-app-wazuh*|luci-app-vortex*|luci-app-firewall*|luci-app-device-intel*|luci-app-security*)
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -109,11 +123,11 @@ build_tree() {
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in case "$app" in
luci-app-jellyfin*|luci-app-lyrion*|luci-app-streamlit*|luci-app-peertube*|luci-app-magicmirror*) luci-app-jellyfin*|luci-app-lyrion*|luci-app-streamlit*|luci-app-peertube*|luci-app-magicmirror*|luci-app-media-flow*|luci-app-icecast*|luci-app-webradio*|luci-app-mmpm*)
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -128,11 +142,11 @@ build_tree() {
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in case "$app" in
luci-app-haproxy*|luci-app-wireguard*|luci-app-tor*|luci-app-cdn*|luci-app-exposure*|luci-app-dns-provider*) luci-app-haproxy*|luci-app-wireguard*|luci-app-tor*|luci-app-cdn*|luci-app-exposure*|luci-app-dns-provider*|luci-app-vhost*|luci-app-bandwidth*|luci-app-traffic*|luci-app-network-modes*|luci-app-ksmbd*)
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -151,7 +165,7 @@ build_tree() {
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -166,11 +180,11 @@ build_tree() {
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in case "$app" in
luci-app-domoticz*|luci-app-zigbee*|luci-app-iot*|luci-app-mqtt*) luci-app-domoticz*|luci-app-zigbee*|luci-app-iot*|luci-app-mqtt*|luci-app-picobrew*)
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -185,11 +199,11 @@ build_tree() {
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in case "$app" in
luci-app-localai*|luci-app-ollama*|luci-app-simplex*|luci-app-gotosocial*|luci-app-ai-*|luci-app-voip*|luci-app-jabber*|luci-app-jitsi*|luci-app-mail*|luci-app-nextcloud*|luci-app-webradio*) luci-app-localai*|luci-app-ollama*|luci-app-simplex*|luci-app-gotosocial*|luci-app-ai-*|luci-app-voip*|luci-app-jabber*|luci-app-jitsi*|luci-app-mailserver*|luci-app-nextcloud*|luci-app-matrix*|luci-app-cyberfeed*)
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -204,11 +218,11 @@ build_tree() {
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in case "$app" in
luci-app-cloner*|luci-app-backup*|luci-app-system*|luci-app-config-advisor*|luci-app-service-registry*) luci-app-cloner*|luci-app-backup*|luci-app-system*|luci-app-config-advisor*|luci-app-service-registry*|luci-app-watchdog*|luci-app-glances*|luci-app-netdata*|luci-app-package-manager*|luci-app-master-link*|luci-app-uhttpd*|luci-app-config-vault*|luci-app-reporter*|luci-app-smtp-relay*|luci-app-rtty*|luci-app-dpi-dual*|luci-app-metacatalog*)
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') 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_object ""
json_add_string "name" "$name" json_add_string "name" "$name"
json_add_string "path" "admin/services/$(echo "$app" | sed 's/luci-app-//')" json_add_string "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;
@ -217,24 +231,36 @@ build_tree() {
json_close_array json_close_array
json_close_object json_close_object
# Other SecuBox Apps (catch-all) # Downloads Apps
json_add_object "" json_add_object ""
json_add_string "cat" "Other SecuBox Apps" json_add_string "cat" "Downloads"
json_add_array "items"
for app in $apps; do
case "$app" in
luci-app-torrent*|luci-app-droplet*|luci-app-aria*|luci-app-transmission*|luci-app-nzb*|luci-app-sabnzbd*)
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" "$(get_menu_path "$app")"
json_add_string "package" "$app"
json_close_object
;;
esac
done
json_close_array
json_close_object
# SecuBox Core Apps
json_add_object ""
json_add_string "cat" "SecuBox Core"
json_add_array "items" json_add_array "items"
for app in $apps; do for app in $apps; do
case "$app" in 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-*|luci-app-voip*|luci-app-jabber*|luci-app-jitsi*|luci-app-mail*|luci-app-nextcloud*|luci-app-webradio*) 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*) 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') 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_object ""
json_add_string "name" "$name" 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 "path" "$(get_menu_path "$app")"
json_add_string "package" "$app" json_add_string "package" "$app"
json_close_object json_close_object
;; ;;

View File

@ -96,7 +96,7 @@ var KissThemeClass = baseclass.extend({
]}, ]},
{ icon: '📶', name: 'Traffic Shaper', path: 'admin/secubox/network/traffic-shaper' }, { icon: '📶', name: 'Traffic Shaper', path: 'admin/secubox/network/traffic-shaper' },
{ icon: '📡', name: 'Bandwidth', path: 'admin/secubox/network/bandwidth-manager' }, { icon: '📡', name: 'Bandwidth', path: 'admin/secubox/network/bandwidth-manager' },
{ icon: '🌐', name: 'Network Modes', path: 'admin/secubox/network/network-modes' }, { icon: '🌐', name: 'Network Modes', path: 'admin/secubox/network/modes' },
{ icon: '🔌', name: 'Interfaces', path: 'admin/network/network' }, { icon: '🔌', name: 'Interfaces', path: 'admin/network/network' },
{ icon: '🔧', name: 'Net Diagnostics', path: 'admin/services/network-diagnostics' } { icon: '🔧', name: 'Net Diagnostics', path: 'admin/services/network-diagnostics' }
]}, ]},
@ -119,6 +119,7 @@ var KissThemeClass = baseclass.extend({
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
{ cat: 'Communication', icon: '💬', collapsed: true, items: [ { cat: 'Communication', icon: '💬', collapsed: true, items: [
{ icon: '✉️', name: 'Mail Server', path: 'admin/services/mailserver' }, { icon: '✉️', name: 'Mail Server', path: 'admin/services/mailserver' },
{ icon: '📧', name: 'SMTP Relay', path: 'admin/secubox/system/smtp-relay' },
{ icon: '💬', name: 'Jabber/XMPP', path: 'admin/services/jabber' }, { icon: '💬', name: 'Jabber/XMPP', path: 'admin/services/jabber' },
{ icon: '🔐', name: 'Matrix', path: 'admin/services/matrix' }, { icon: '🔐', name: 'Matrix', path: 'admin/services/matrix' },
{ icon: '🔒', name: 'SimpleX', path: 'admin/services/simplex' }, { icon: '🔒', name: 'SimpleX', path: 'admin/services/simplex' },
@ -145,7 +146,10 @@ var KissThemeClass = baseclass.extend({
{ name: 'Settings', path: 'admin/services/metablogizer/settings' } { name: 'Settings', path: 'admin/services/metablogizer/settings' }
]}, ]},
{ icon: '🎯', name: 'Streamlit', path: 'admin/services/streamlit' }, { icon: '🎯', name: 'Streamlit', path: 'admin/services/streamlit' },
{ icon: '🔧', name: 'Streamlit Forge', path: 'admin/services/streamlit-forge' },
{ icon: '📰', name: 'CyberFeed', path: 'admin/services/cyberfeed' }, { icon: '📰', name: 'CyberFeed', path: 'admin/services/cyberfeed' },
{ icon: '📚', name: 'Meta Catalog', path: 'admin/secubox/metacatalog' },
{ icon: '🎭', name: 'Avatar Tap', path: 'admin/services/avatar-tap' },
{ icon: '🏠', name: 'Domoticz', path: 'admin/services/domoticz' }, { icon: '🏠', name: 'Domoticz', path: 'admin/services/domoticz' },
{ icon: '🍺', name: 'PicoBrew', path: 'admin/services/picobrew' } { icon: '🍺', name: 'PicoBrew', path: 'admin/services/picobrew' }
]}, ]},
@ -168,13 +172,12 @@ var KissThemeClass = baseclass.extend({
// P2P & MESH - Distributed networking // P2P & MESH - Distributed networking
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
{ cat: 'P2P & Mesh', icon: '🔗', collapsed: true, items: [ { cat: 'P2P & Mesh', icon: '🔗', collapsed: true, items: [
{ icon: '🔗', name: 'Master Link', path: 'admin/secubox/master-link' }, { icon: '🔗', name: 'Master Link', path: 'admin/services/secubox-mesh' },
{ icon: '🌐', name: 'P2P Network', path: 'admin/services/secubox-p2p' }, { icon: '🌐', name: 'P2P Network', path: 'admin/services/secubox-p2p' },
{ icon: '🔗', name: 'Mesh Network', path: 'admin/services/secubox-mesh' }, { icon: '📡', name: 'Exposure', path: 'admin/secubox/network/exposure' },
{ icon: '📡', name: 'Exposure', path: 'admin/services/exposure' },
{ icon: '📋', name: 'Service Registry', path: 'admin/services/service-registry' }, { icon: '📋', name: 'Service Registry', path: 'admin/services/service-registry' },
{ icon: '☁️', name: 'SaaS Relay', path: 'admin/services/saas-relay' }, { icon: '☁️', name: 'SaaS Relay', path: 'admin/services/saas-relay' },
{ icon: '🌳', name: 'Netifyd', path: 'admin/services/secubox-netifyd' } { icon: '🌳', name: 'Netifyd', path: 'admin/secubox/netifyd' }
]}, ]},
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
@ -187,7 +190,31 @@ var KissThemeClass = baseclass.extend({
{ icon: '📁', name: 'File Sharing', path: 'admin/services/ksmbd' }, { icon: '📁', name: 'File Sharing', path: 'admin/services/ksmbd' },
{ icon: '🌳', name: 'LuCI Menu', path: 'admin/secubox/luci-tree' }, { icon: '🌳', name: 'LuCI Menu', path: 'admin/secubox/luci-tree' },
{ icon: '🔧', name: 'Software', path: 'admin/system/opkg' }, { icon: '🔧', name: 'Software', path: 'admin/system/opkg' },
{ icon: '🖥️', name: 'uhttpd', path: 'admin/services/uhttpd' } { icon: '🖥️', name: 'uhttpd', path: 'admin/services/uhttpd' },
{ icon: '🔐', name: 'Config Vault', path: 'admin/secubox/system/config-vault' },
{ icon: '📋', name: 'Reporter', path: 'admin/secubox/system/reporter' },
{ icon: '🐕', name: 'Watchdog', path: 'admin/secubox/system/watchdog' },
{ icon: '🖥️', name: 'Remote RTTY', path: 'admin/secubox/system/system-hub/rtty-remote' }
]},
// ═══════════════════════════════════════════════════════════════
// DOWNLOADS - Torrent and Usenet clients
// ═══════════════════════════════════════════════════════════════
{ cat: 'Downloads', icon: '📥', collapsed: true, items: [
{ icon: '🧲', name: 'Torrent', path: 'admin/services/torrent' },
{ icon: '💧', name: 'Droplet', path: 'admin/services/droplet' },
{ icon: '🌊', name: 'WebTorrent', path: 'admin/services/webtorrent' }
]},
// ═══════════════════════════════════════════════════════════════
// MONITORING - System and network monitoring
// ═══════════════════════════════════════════════════════════════
{ cat: 'Monitoring', icon: '📈', collapsed: true, items: [
{ icon: '👁️', name: 'Glances', path: 'admin/secubox/monitoring/glances' },
{ icon: '📊', name: 'Netdata', path: 'admin/secubox/monitoring/netdata' },
{ icon: '🔍', name: 'Device Intel', path: 'admin/secubox/device-intel' },
{ icon: '📡', name: 'Threat Analyst', path: 'admin/services/threat-analyst' },
{ icon: '🔬', name: 'DPI Dual', path: 'admin/secubox/dpi-dual' }
]} ]}
], ],