From a1bad31807045e252adc51c7460b3df4502ffa76 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sun, 25 Jan 2026 17:22:52 +0100 Subject: [PATCH] fix(multi): Exposure fixes, MagicMirror2 port, Tor Shield health card Exposure Manager: - Fix RPCD subshell issues in status and ssl_list methods - Fix JS views to handle both array and object API responses MagicMirror2: - Change default port from 8082 to 8085 (avoid CyberFeed conflict) - Update mm2ctl, RPCD, settings.js, dashboard.js, config Tor Shield: - Add restart method to RPCD and API - Add health status minicard (Service, Bootstrap, DNS, Kill Switch) Portal: - Add 'active-ports' section for detected services - Separate portal apps (Services) from detected ports (Active Ports) Service Detection: - Prioritize port-based identification over process name Co-Authored-By: Claude Opus 4.5 --- .../resources/view/exposure/overview.js | 4 +- .../resources/view/exposure/services.js | 6 +- .../resources/view/exposure/ssl.js | 6 +- .../resources/view/exposure/tor.js | 6 +- .../root/usr/libexec/rpcd/luci.exposure | 44 +++++++---- .../resources/view/magicmirror2/dashboard.js | 2 +- .../resources/view/magicmirror2/settings.js | 2 +- .../root/usr/libexec/rpcd/luci.magicmirror2 | 6 +- .../resources/secubox-portal/portal.js | 7 ++ .../resources/view/secubox-portal/index.js | 21 +++-- .../luci-static/resources/tor-shield/api.js | 7 ++ .../resources/view/tor-shield/overview.js | 74 ++++++++++++++++++ .../root/usr/libexec/rpcd/luci.tor-shield | 17 +++- .../secubox/secubox-app-magicmirror2/Makefile | 2 +- .../files/etc/config/magicmirror2 | 2 +- .../files/usr/sbin/mm2ctl | 6 +- .../root/usr/libexec/rpcd/luci.secubox | 78 +++++++++---------- 17 files changed, 212 insertions(+), 78 deletions(-) diff --git a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/overview.js b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/overview.js index 2154b1a2..388e4d4f 100644 --- a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/overview.js +++ b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/overview.js @@ -14,8 +14,10 @@ return view.extend({ render: function(data) { var status = data[0] || {}; - var conflicts = data[1] || []; + var conflictsResult = data[1] || {}; + // Handle both direct array and wrapped object responses + var conflicts = Array.isArray(conflictsResult) ? conflictsResult : (conflictsResult.conflicts || []); var services = status.services || {}; var tor = status.tor || {}; var ssl = status.ssl || {}; diff --git a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/services.js b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/services.js index b8fa1e9f..bc55a793 100644 --- a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/services.js +++ b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/services.js @@ -13,8 +13,10 @@ return view.extend({ }, render: function(data) { - var services = data[0] || []; - var config = data[1] || []; + var scanResult = data[0] || {}; + var configResult = data[1] || {}; + var services = Array.isArray(scanResult) ? scanResult : (scanResult.services || []); + var config = Array.isArray(configResult) ? configResult : (configResult.known_services || []); var self = this; // Inject CSS diff --git a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/ssl.js b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/ssl.js index eda8af0d..6c752d54 100644 --- a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/ssl.js +++ b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/ssl.js @@ -13,8 +13,10 @@ return view.extend({ }, render: function(data) { - var sslBackends = data[0] || []; - var allServices = data[1] || []; + var sslResult = data[0] || {}; + var scanResult = data[1] || {}; + var sslBackends = Array.isArray(sslResult) ? sslResult : (sslResult.backends || []); + var allServices = Array.isArray(scanResult) ? scanResult : (scanResult.services || []); var self = this; // Inject CSS diff --git a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/tor.js b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/tor.js index 1f8a3bde..686d70b2 100644 --- a/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/tor.js +++ b/package/secubox/luci-app-exposure/htdocs/luci-static/resources/view/exposure/tor.js @@ -13,8 +13,10 @@ return view.extend({ }, render: function(data) { - var torServices = data[0] || []; - var allServices = data[1] || []; + var torResult = data[0] || {}; + var scanResult = data[1] || {}; + var torServices = Array.isArray(torResult) ? torResult : (torResult.services || []); + var allServices = Array.isArray(scanResult) ? scanResult : (scanResult.services || []); var self = this; // Inject CSS diff --git a/package/secubox/luci-app-exposure/root/usr/libexec/rpcd/luci.exposure b/package/secubox/luci-app-exposure/root/usr/libexec/rpcd/luci.exposure index 1b3d5b74..d828fed1 100755 --- a/package/secubox/luci-app-exposure/root/usr/libexec/rpcd/luci.exposure +++ b/package/secubox/luci-app-exposure/root/usr/libexec/rpcd/luci.exposure @@ -137,23 +137,32 @@ case "$1" in json_close_array json_close_object - # HAProxy SSL backends + # HAProxy SSL backends - use temp file to avoid subshell HAPROXY_CONFIG="/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg" ssl_count=0 [ -f "$HAPROXY_CONFIG" ] && ssl_count=$(grep -c "^backend.*_backend$" "$HAPROXY_CONFIG" 2>/dev/null || echo 0) + TMP_SSL="/tmp/exposure_ssl_$$" + if [ -f "$HAPROXY_CONFIG" ]; then + grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" 2>/dev/null | while read line; do + backend=$(echo "$line" | awk '{print $2}' | sed 's/_backend$//') + domain=$(grep "acl host_${backend} " "$HAPROXY_CONFIG" 2>/dev/null | awk '{print $NF}') + echo "$backend ${domain:-N/A}" + done > "$TMP_SSL" + fi + json_add_object "ssl" json_add_int "count" "$ssl_count" json_add_array "backends" - if [ -f "$HAPROXY_CONFIG" ]; then - grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" 2>/dev/null | while read line; do - backend=$(echo "$line" | awk '{print $2}' | sed 's/_backend$//') - domain=$(grep "acl host_${backend} " "$HAPROXY_CONFIG" 2>/dev/null | awk '{print $NF}') + if [ -f "$TMP_SSL" ]; then + while read backend domain; do + [ -z "$backend" ] && continue json_add_object "" json_add_string "service" "$backend" - json_add_string "domain" "${domain:-N/A}" + json_add_string "domain" "$domain" json_close_object - done + done < "$TMP_SSL" + rm -f "$TMP_SSL" fi json_close_array json_close_object @@ -195,23 +204,32 @@ case "$1" in ssl_list) HAPROXY_CONFIG="/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg" + TMP_SSLLIST="/tmp/exposure_ssllist_$$" - json_init - json_add_array "backends" - + # Extract backend info to temp file to avoid subshell issues if [ -f "$HAPROXY_CONFIG" ]; then grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" 2>/dev/null | while read line; do backend=$(echo "$line" | awk '{print $2}') service=$(echo "$backend" | sed 's/_backend$//') domain=$(grep "acl host_${service} " "$HAPROXY_CONFIG" 2>/dev/null | awk '{print $NF}') server=$(grep -A5 "backend $backend" "$HAPROXY_CONFIG" 2>/dev/null | grep "server " | awk '{print $3}') + echo "$service|${domain:-N/A}|${server:-N/A}" + done > "$TMP_SSLLIST" + fi + json_init + json_add_array "backends" + + if [ -f "$TMP_SSLLIST" ]; then + while IFS='|' read service domain server; do + [ -z "$service" ] && continue json_add_object "" json_add_string "service" "$service" - json_add_string "domain" "${domain:-N/A}" - json_add_string "backend" "${server:-N/A}" + json_add_string "domain" "$domain" + json_add_string "backend" "$server" json_close_object - done + done < "$TMP_SSLLIST" + rm -f "$TMP_SSLLIST" fi json_close_array diff --git a/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/dashboard.js b/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/dashboard.js index 737b170a..f72324a2 100644 --- a/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/dashboard.js +++ b/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/dashboard.js @@ -126,7 +126,7 @@ return view.extend({ ]), E('div', { 'class': 'mm2-card' }, [ E('div', { 'class': 'mm2-stat' }, [ - E('div', { 'class': 'mm2-stat-value' }, ':' + (config.port || 8082)), + E('div', { 'class': 'mm2-stat-value' }, ':' + (config.port || 8085)), E('div', { 'class': 'mm2-stat-label' }, _('Web Port')) ]) ]), diff --git a/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/settings.js b/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/settings.js index 49dd44e3..24d27aab 100644 --- a/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/settings.js +++ b/package/secubox/luci-app-magicmirror2/htdocs/luci-static/resources/view/magicmirror2/settings.js @@ -67,7 +67,7 @@ return view.extend({ o = s.option(form.Value, 'port', _('Web Port')); o.datatype = 'port'; - o.default = '8082'; + o.default = '8085'; o.rmempty = false; o = s.option(form.Value, 'address', _('Listen Address')); diff --git a/package/secubox/luci-app-magicmirror2/root/usr/libexec/rpcd/luci.magicmirror2 b/package/secubox/luci-app-magicmirror2/root/usr/libexec/rpcd/luci.magicmirror2 index 6f307ef2..efe2bfdd 100644 --- a/package/secubox/luci-app-magicmirror2/root/usr/libexec/rpcd/luci.magicmirror2 +++ b/package/secubox/luci-app-magicmirror2/root/usr/libexec/rpcd/luci.magicmirror2 @@ -26,7 +26,7 @@ get_status() { fi local enabled=$(uci -q get magicmirror2.main.enabled || echo "0") - local port=$(uci -q get magicmirror2.main.port || echo "8082") + local port=$(uci -q get magicmirror2.main.port || echo "8085") local router_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1") [ "$running" = "1" ] && web_url="http://${router_ip}:${port}" @@ -54,7 +54,7 @@ EOF # Get main configuration get_config() { local enabled=$(uci -q get magicmirror2.main.enabled || echo "0") - local port=$(uci -q get magicmirror2.main.port || echo "8082") + local port=$(uci -q get magicmirror2.main.port || echo "8085") local address=$(uci -q get magicmirror2.main.address || echo "0.0.0.0") local data_path=$(uci -q get magicmirror2.main.data_path || echo "/srv/magicmirror2") local memory_limit=$(uci -q get magicmirror2.main.memory_limit || echo "512M") @@ -327,7 +327,7 @@ set_config() { # Get web URL for iframe get_web_url() { local router_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1") - local port=$(uci -q get magicmirror2.main.port || echo "8082") + local port=$(uci -q get magicmirror2.main.port || echo "8085") cat <= 100 ? '#10b981' : status.bootstrap > 0 ? '#f59e0b' : '#6b7280') + '; box-shadow: 0 0 8px ' + (status.bootstrap >= 100 ? '#10b981' : status.bootstrap > 0 ? '#f59e0b' : 'transparent') + ';' + }), + E('div', {}, [ + E('div', { 'style': 'font-size: 14px; font-weight: 600; color: var(--tor-text, #fff);' }, _('Bootstrap')), + E('div', { 'style': 'font-size: 12px; color: var(--tor-text-muted, #a0a0b0);' }, + status.bootstrap >= 100 ? _('Complete') : status.bootstrap + '%') + ]) + ]), + E('div', { 'class': 'tor-health-item', 'style': 'display: flex; align-items: center; gap: 12px; padding: 16px; background: var(--tor-bg-card, #1a1a24); border-radius: 12px; border: 1px solid rgba(255,255,255,0.05);' }, [ + E('div', { + 'class': 'tor-health-indicator', + 'style': 'width: 12px; height: 12px; border-radius: 50%; background: ' + (status.dns_over_tor ? '#10b981' : '#f59e0b') + '; box-shadow: 0 0 8px ' + (status.dns_over_tor ? '#10b981' : '#f59e0b') + ';' + }), + E('div', {}, [ + E('div', { 'style': 'font-size: 14px; font-weight: 600; color: var(--tor-text, #fff);' }, _('DNS')), + E('div', { 'style': 'font-size: 12px; color: var(--tor-text-muted, #a0a0b0);' }, + status.dns_over_tor ? _('Protected') : _('Exposed')) + ]) + ]), + E('div', { 'class': 'tor-health-item', 'style': 'display: flex; align-items: center; gap: 12px; padding: 16px; background: var(--tor-bg-card, #1a1a24); border-radius: 12px; border: 1px solid rgba(255,255,255,0.05);' }, [ + E('div', { + 'class': 'tor-health-indicator', + 'style': 'width: 12px; height: 12px; border-radius: 50%; background: ' + (status.kill_switch ? '#10b981' : '#6b7280') + '; box-shadow: 0 0 8px ' + (status.kill_switch ? '#10b981' : 'transparent') + ';' + }), + E('div', {}, [ + E('div', { 'style': 'font-size: 14px; font-weight: 600; color: var(--tor-text, #fff);' }, _('Kill Switch')), + E('div', { 'style': 'font-size: 12px; color: var(--tor-text-muted, #a0a0b0);' }, + status.kill_switch ? _('Active') : _('Disabled')) + ]) + ]) + ]), + // Actions Card E('div', { 'class': 'tor-card' }, [ E('div', { 'class': 'tor-card-header' }, [ @@ -393,6 +462,11 @@ return view.extend({ 'click': L.bind(this.handleLeakTest, this), 'disabled': !isActive }, ['\uD83D\uDD0D ', _('Leak Test')]), + E('button', { + 'class': 'tor-btn tor-btn-warning', + 'click': L.bind(this.handleRestart, this), + 'disabled': !status.enabled + }, ['\u21BB ', _('Restart')]), E('a', { 'class': 'tor-btn', 'href': L.url('admin', 'services', 'tor-shield', 'circuits') diff --git a/package/secubox/luci-app-tor-shield/root/usr/libexec/rpcd/luci.tor-shield b/package/secubox/luci-app-tor-shield/root/usr/libexec/rpcd/luci.tor-shield index 2c49bbd3..55c207c6 100644 --- a/package/secubox/luci-app-tor-shield/root/usr/libexec/rpcd/luci.tor-shield +++ b/package/secubox/luci-app-tor-shield/root/usr/libexec/rpcd/luci.tor-shield @@ -707,9 +707,21 @@ save_settings() { } # Main dispatcher +# Restart Tor Shield service +do_restart() { + json_init + + /etc/init.d/tor-shield restart >/dev/null 2>&1 & + + json_add_boolean "success" 1 + json_add_string "message" "Tor Shield restarting" + + json_dump +} + case "$1" in list) - echo '{"status":{},"enable":{"preset":"str"},"disable":{},"circuits":{},"new_identity":{},"check_leaks":{},"hidden_services":{},"add_hidden_service":{"name":"str","local_port":"int","virtual_port":"int"},"remove_hidden_service":{"name":"str"},"exit_ip":{},"bandwidth":{},"presets":{},"bridges":{},"set_bridges":{"enabled":"bool","type":"str"},"settings":{},"save_settings":{"mode":"str","dns_over_tor":"bool","kill_switch":"bool","socks_port":"int","trans_port":"int","dns_port":"int","exit_nodes":"str","exclude_exit_nodes":"str","strict_nodes":"bool"}}' + echo '{"status":{},"enable":{"preset":"str"},"disable":{},"restart":{},"circuits":{},"new_identity":{},"check_leaks":{},"hidden_services":{},"add_hidden_service":{"name":"str","local_port":"int","virtual_port":"int"},"remove_hidden_service":{"name":"str"},"exit_ip":{},"bandwidth":{},"presets":{},"bridges":{},"set_bridges":{"enabled":"bool","type":"str"},"settings":{},"save_settings":{"mode":"str","dns_over_tor":"bool","kill_switch":"bool","socks_port":"int","trans_port":"int","dns_port":"int","exit_nodes":"str","exclude_exit_nodes":"str","strict_nodes":"bool"}}' ;; call) case "$2" in @@ -722,6 +734,9 @@ case "$1" in disable) do_disable ;; + restart) + do_restart + ;; circuits) get_circuits ;; diff --git a/package/secubox/secubox-app-magicmirror2/Makefile b/package/secubox/secubox-app-magicmirror2/Makefile index def56b65..11c1debd 100644 --- a/package/secubox/secubox-app-magicmirror2/Makefile +++ b/package/secubox/secubox-app-magicmirror2/Makefile @@ -60,7 +60,7 @@ define Package/secubox-app-magicmirror2/postinst echo " mm2ctl install" echo " /etc/init.d/magicmirror2 start" echo "" - echo "Web interface: http://:8082" + echo "Web interface: http://:8085" echo "" echo "To manage modules:" echo " mm2ctl module list" diff --git a/package/secubox/secubox-app-magicmirror2/files/etc/config/magicmirror2 b/package/secubox/secubox-app-magicmirror2/files/etc/config/magicmirror2 index e42cdf9b..6073e2a6 100644 --- a/package/secubox/secubox-app-magicmirror2/files/etc/config/magicmirror2 +++ b/package/secubox/secubox-app-magicmirror2/files/etc/config/magicmirror2 @@ -2,7 +2,7 @@ config magicmirror2 'main' option enabled '0' - option port '8082' + option port '8085' option address '0.0.0.0' option data_path '/srv/magicmirror2' option memory_limit '512M' diff --git a/package/secubox/secubox-app-magicmirror2/files/usr/sbin/mm2ctl b/package/secubox/secubox-app-magicmirror2/files/usr/sbin/mm2ctl index 5127ebfc..0d46d91d 100644 --- a/package/secubox/secubox-app-magicmirror2/files/usr/sbin/mm2ctl +++ b/package/secubox/secubox-app-magicmirror2/files/usr/sbin/mm2ctl @@ -51,7 +51,7 @@ Examples: mm2ctl module list mm2ctl config -Web Interface: http://:8082 +Web Interface: http://:8085 EOF } @@ -66,7 +66,7 @@ uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; } # Load configuration with defaults load_config() { - port="$(uci_get main.port || echo 8082)" + port="$(uci_get main.port || echo 8085)" address="$(uci_get main.address || echo 0.0.0.0)" data_path="$(uci_get main.data_path || echo /srv/magicmirror2)" memory_limit="$(uci_get main.memory_limit || echo 512M)" @@ -255,7 +255,7 @@ lxc_create_docker_rootfs() { cat >> "$rootfs/opt/start-mm2.sh" << 'START' export PATH="/usr/local/bin:/usr/bin:/bin:$PATH" export NODE_ENV=production -export MM_PORT="${MM2_PORT:-8082}" +export MM_PORT="${MM2_PORT:-8085}" export MM_ADDRESS="${MM2_ADDRESS:-0.0.0.0}" MM_DIR="/opt/magic_mirror" diff --git a/package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox b/package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox index 190ae535..69a375d2 100755 --- a/package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox +++ b/package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox @@ -1200,49 +1200,47 @@ case "$1" in addr=$(echo "$local" | sed 's/:[^:]*$//') name=""; icon=""; category="other"; path="" - # First: identify by process name (most accurate) - case "$proc" in - sshd|dropbear) name="SSH"; icon="lock"; category="system" ;; - dnsmasq|named|unbound) name="DNS"; icon="globe"; category="system" ;; - haproxy) name="HAProxy"; icon="arrow"; category="proxy" ;; - nginx|uhttpd) name="Web Server"; icon="settings"; category="system" ;; - gitea) name="Gitea"; icon="git"; path=":$port"; category="app" ;; - hexo|node) [ "$port" = "4000" ] && { name="HexoJS"; icon="blog"; path=":$port"; category="app"; } ;; - crowdsec|lapi) name="CrowdSec"; icon="security"; category="security" ;; - netifyd) name="Netifyd"; icon="chart"; path=":$port"; category="monitoring" ;; - streamlit|python*) - [ "$port" = "8501" ] && { name="Streamlit"; icon="app"; path=":$port"; category="app"; } - ;; - slimserver|squeezeboxserver) name="Lyrion"; icon="music"; path=":$port"; category="media" ;; - tor) name="Tor"; icon="onion"; category="privacy" ;; - cyberfeed*) name="CyberFeed"; icon="feed"; path=":$port"; category="app" ;; - metabolizer*) name="Metabolizer"; icon="blog"; path=":$port"; category="app" ;; - magicmirror*|electron) name="MagicMirror"; icon="app"; path=":$port"; category="app" ;; - picobrew*) name="PicoBrew"; icon="app"; path=":$port"; category="app" ;; + # First: identify by well-known port (most reliable for multi-service ports) + case "$port" in + 22) name="SSH"; icon="lock"; category="system" ;; + 53) name="DNS"; icon="globe"; category="system" ;; + 80) name="HTTP"; icon="arrow"; path="/"; category="proxy" ;; + 443) name="HTTPS"; icon="shield"; path="/"; category="proxy" ;; + 2222) name="Gitea SSH"; icon="git"; category="app" ;; + 3000) name="Gitea"; icon="git"; path=":3000"; category="app" ;; + 3483) name="Squeezebox"; icon="music"; category="media" ;; + 4000) name="HexoJS"; icon="blog"; path=":4000"; category="app" ;; + 6060) name="CrowdSec LAPI"; icon="security"; category="security" ;; + 8081) name="LuCI"; icon="settings"; path=":8081"; category="system" ;; + 8085) name="MagicMirror2"; icon="app"; path=":8085"; category="app" ;; + 8086) name="Netifyd"; icon="chart"; path=":8086"; category="monitoring" ;; + 8404) name="HAProxy Stats"; icon="stats"; path=":8404/stats"; category="monitoring" ;; + 8444) name="LuCI HTTPS"; icon="admin"; path=":8444"; category="system" ;; + 8501) name="Streamlit"; icon="app"; path=":8501"; category="app" ;; + 9000) name="Lyrion"; icon="music"; path=":9000"; category="media" ;; + 9050) name="Tor SOCKS"; icon="onion"; category="privacy" ;; + 9090) name="Lyrion CLI"; icon="music"; category="media" ;; esac - # Fallback: identify by port number if process didn't match + # Fallback: identify by process name if port didn't match if [ -z "$name" ]; then - case "$port" in - 22) name="SSH"; icon="lock"; category="system" ;; - 53) name="DNS"; icon="globe"; category="system" ;; - 80) name="HTTP"; icon="arrow"; path="/"; category="proxy" ;; - 443) name="HTTPS"; icon="shield"; path="/"; category="proxy" ;; - 3000) name="Gitea"; icon="git"; path=":3000"; category="app" ;; - 4000) name="HexoJS"; icon="blog"; path=":4000"; category="app" ;; - 6060) name="CrowdSec LAPI"; icon="security"; category="security" ;; - 8080) name="Web App"; icon="app"; path=":8080"; category="app" ;; - 8081) name="LuCI"; icon="settings"; path=":8081"; category="system" ;; - 8082) name="CyberFeed"; icon="feed"; path=":8082"; category="app" ;; - 8086) name="Netifyd"; icon="chart"; path=":8086"; category="monitoring" ;; - 8404) name="HAProxy Stats"; icon="stats"; path=":8404/stats"; category="monitoring" ;; - 8444) name="LuCI HTTPS"; icon="admin"; path=":8444"; category="system" ;; - 8501) name="Streamlit"; icon="app"; path=":8501"; category="app" ;; - 9000) name="Lyrion"; icon="music"; path=":9000"; category="media" ;; - 9050) name="Tor SOCKS"; icon="onion"; category="privacy" ;; - 9090) name="Lyrion CLI"; icon="music"; category="media" ;; - 2222) name="Gitea SSH"; icon="git"; category="app" ;; - 3483) name="Squeezebox"; icon="music"; category="media" ;; + case "$proc" in + sshd|dropbear) name="SSH"; icon="lock"; category="system" ;; + dnsmasq|named|unbound) name="DNS"; icon="globe"; category="system" ;; + haproxy) name="HAProxy"; icon="arrow"; category="proxy" ;; + nginx|uhttpd) name="Web Server"; icon="settings"; category="system" ;; + gitea) name="Gitea"; icon="git"; path=":$port"; category="app" ;; + hexo|node) name="HexoJS"; icon="blog"; path=":$port"; category="app" ;; + crowdsec|lapi) name="CrowdSec"; icon="security"; category="security" ;; + netifyd) name="Netifyd"; icon="chart"; path=":$port"; category="monitoring" ;; + slimserver|squeezeboxserver) name="Lyrion"; icon="music"; path=":$port"; category="media" ;; + tor) name="Tor"; icon="onion"; category="privacy" ;; + cyberfeed*) name="CyberFeed"; icon="feed"; path=":$port"; category="app" ;; + metabolizer*) name="Metabolizer"; icon="blog"; path=":$port"; category="app" ;; + magicmirror*|electron) name="MagicMirror"; icon="app"; path=":$port"; category="app" ;; + picobrew*) name="PicoBrew"; icon="app"; path=":$port"; category="app" ;; + streamlit) name="Streamlit"; icon="app"; path=":$port"; category="app" ;; + python*) name="Python App"; icon="app"; path=":$port"; category="app" ;; *) name="$proc"; icon=""; category="other"; path=":$port" ;; esac fi