secubox-openwrt/luci-app-secubox/root/usr/libexec/rpcd/luci.secubox
CyberMind-FR b00d13b84a fix(secubox): rename .appstore to appstore to fix package build
Renamed .appstore directory to appstore (without dot prefix) to ensure
proper inclusion in OpenWrt package builds. Hidden directories (starting
with .) can be problematic during tarball creation and package installation.

Changes:
- Renamed .appstore/ to appstore/
- Updated Makefile install path references
- Updated RPCD script APPSTORE_JSON path
- Fixed file permissions to 644 for apps.json

This fixes the issue where appstore appears empty on fresh firmware
installations.

New path: /usr/share/secubox/appstore/apps.json
Old path: /usr/share/secubox/.appstore/apps.json

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 12:38:47 +01:00

2081 lines
58 KiB
Bash
Executable File

#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# SecuBox Master RPCD Backend
# Module discovery and unified status
# Copyright (C) 2025 CyberMind.fr
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
get_pkg_version() {
local version=""
# OpenWrt 25.12+ uses apk packages
if command -v apk >/dev/null 2>&1; then
local apk_line=$(apk info -v luci-app-secubox 2>/dev/null | grep -E '^luci-app-secubox-[0-9]' | head -n1)
if [ -n "$apk_line" ]; then
version=$(echo "$apk_line" | sed 's/^luci-app-secubox-//' | awk '{print $1}' | sed 's/-r[0-9]*$//')
else
version=$(apk info luci-app-secubox 2>/dev/null | awk -F': ' '/^Version/ {print $2; exit}')
fi
if [ -n "$version" ]; then
echo "$version"
return
fi
fi
# Legacy opkg metadata
local ctrl="/usr/lib/opkg/info/luci-app-secubox.control"
if [ -f "$ctrl" ]; then
version=$(awk -F': ' '/^Version/ { print $2; exit }' "$ctrl")
if [ -n "$version" ]; then
echo "$version"
return
fi
fi
if command -v opkg >/dev/null 2>&1; then
version=$(opkg list-installed luci-app-secubox 2>/dev/null | awk '{print $3}' | sed 's/-r[0-9]*$//' | head -n1)
if [ -n "$version" ]; then
echo "$version"
return
fi
fi
local cfg_version="$(uci -q get secubox.main.version)"
if [ -n "$cfg_version" ]; then
echo "$cfg_version"
elif [ -f "/usr/share/secubox/VERSION" ]; then
head -n1 /usr/share/secubox/VERSION
else
echo "unknown"
fi
}
PKG_VERSION="$(get_pkg_version)"
PLUGIN_DIR="/usr/share/secubox/plugins"
PROFILE_DIR="/usr/share/secubox/profiles"
PROFILE_BACKUP_DIR="/etc/secubox-profiles/backups"
DEFAULT_STORAGE_PATH="/srv/secubox"
SECOBOX_APP="/usr/sbin/secubox-app"
OPKG_UPDATED=0
manifest_files() {
if [ -d "$PLUGIN_DIR/catalog" ]; then
for file in "$PLUGIN_DIR"/catalog/*.json; do
[ -f "$file" ] || continue
echo "$file"
done
fi
for file in "$PLUGIN_DIR"/*/manifest.json; do
[ -f "$file" ] || continue
echo "$file"
done
}
manifest_file_for_id() {
local id="$1"
local catalog="$PLUGIN_DIR/catalog/$id.json"
local legacy="$PLUGIN_DIR/$id/manifest.json"
if [ -f "$catalog" ]; then
printf '%s' "$catalog"
return 0
fi
if [ -f "$legacy" ]; then
printf '%s' "$legacy"
return 0
fi
return 1
}
validate_manifest_json() {
local manifest="$1"
local id name runtime packages category
id=$(jsonfilter -s "$manifest" -e '@.id' 2>/dev/null)
name=$(jsonfilter -s "$manifest" -e '@.name' 2>/dev/null)
category=$(jsonfilter -s "$manifest" -e '@.category' 2>/dev/null)
runtime=$(jsonfilter -s "$manifest" -e '@.runtime' 2>/dev/null)
[ -n "$runtime" ] || runtime=$(jsonfilter -s "$manifest" -e '@.type' 2>/dev/null)
packages=$(jsonfilter -s "$manifest" -e '@.packages[*]' 2>/dev/null)
[ -n "$id" ] && [ -n "$name" ] && [ -n "$runtime" ] && [ -n "$category" ] && [ -n "$packages" ]
}
# Module registry - auto-detected from /usr/libexec/rpcd/
detect_modules() {
local modules=""
local scripts="luci.crowdsec-dashboard luci.netdata-dashboard luci.netifyd-dashboard luci.wireguard-dashboard luci.network-modes luci.client-guardian luci.system-hub luci.bandwidth-manager luci.auth-guardian luci.media-flow luci.vhost-manager luci.cdn-cache luci.traffic-shaper luci.ksm-manager"
for script in $scripts; do
if [ -x "/usr/libexec/rpcd/$script" ]; then
# Remove luci. prefix, remove -dashboard suffix, convert dashes to underscores
local module_id=$(echo "$script" | sed 's/^luci\.//' | sed 's/-dashboard$//' | sed 's/-/_/g')
modules="$modules $module_id"
fi
done
echo "$modules"
}
MODULES=$(detect_modules)
is_root_password_set() {
local hash
hash=$(grep '^root:' /etc/shadow 2>/dev/null | cut -d: -f2)
[ -n "$hash" ] && [ "$hash" != "!" ] && [ "$hash" != "*" ]
}
get_storage_path() {
local path
path=$(uci -q get secubox.main.storage_path)
if [ -z "$path" ]; then
path="$DEFAULT_STORAGE_PATH"
fi
echo "$path"
}
package_installed() {
local pkg="$1"
if command -v opkg >/dev/null 2>&1; then
opkg status "$pkg" >/dev/null 2>&1 && return 0
elif command -v apk >/dev/null 2>&1; then
apk info -e "$pkg" >/dev/null 2>&1 && return 0
fi
return 1
}
packages_state() {
local manifest="$1"
local total=0 installed=0 missing=0 pkg
for pkg in $(jsonfilter -s "$manifest" -e '@.packages[*]' 2>/dev/null); do
total=$((total + 1))
if package_installed "$pkg"; then
installed=$((installed + 1))
else
missing=$((missing + 1))
fi
done
if [ "$total" -eq 0 ]; then
echo "n/a"
elif [ "$missing" -eq 0 ]; then
echo "installed"
elif [ "$installed" -eq 0 ]; then
echo "missing"
else
echo "partial"
fi
}
ensure_directory() {
local dir="$1"
[ -d "$dir" ] || mkdir -p "$dir"
}
apply_network_mode() {
local mode="$1"
[ -n "$mode" ] || return
if command -v ubus >/dev/null 2>&1; then
if ubus call luci.network-modes set_mode "{\"mode\":\"$mode\"}" >/dev/null 2>&1; then
ubus call luci.network-modes apply_mode >/dev/null 2>&1
fi
fi
}
ensure_opkg_updated() {
[ "$OPKG_UPDATED" -eq 1 ] && return
if command -v opkg >/dev/null 2>&1; then
opkg update >/dev/null 2>&1 && OPKG_UPDATED=1
fi
}
install_package_if_missing() {
local pkg="$1"
package_installed "$pkg" && return
if command -v opkg >/dev/null 2>&1; then
ensure_opkg_updated
opkg install "$pkg" >/dev/null 2>&1
elif command -v apk >/dev/null 2>&1; then
apk add "$pkg" >/dev/null 2>&1
fi
}
# Check if a module is installed (supports both opkg and apk)
check_module_installed() {
local module="$1"
local package config
config_get package "$module" package ""
config_get config "$module" config ""
# Check if package is installed via apk (OpenWrt 25.12+) or opkg (24.10 and earlier)
if [ -x "/usr/bin/apk" ]; then
# OpenWrt 25.12+ uses apk
if apk list --installed 2>/dev/null | grep -q "^${package}-"; then
echo "1"
return
fi
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
# OpenWrt 24.10 and earlier uses opkg
if opkg list-installed 2>/dev/null | grep -q "^${package} "; then
echo "1"
return
fi
fi
# Fallback: check if config file exists
if [ -f "/etc/config/${config}" ]; then
echo "1"
else
echo "0"
fi
}
# Check if a module is enabled (UCI config)
check_module_enabled() {
local module="$1"
local enabled
config_load secubox
config_get enabled "$module" enabled "1"
echo "$enabled"
}
# Check if a module service is running
check_module_running() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
case "$module" in
crowdsec)
pgrep -f crowdsec > /dev/null 2>&1 && echo "1" || echo "0"
;;
netdata)
pgrep -f netdata > /dev/null 2>&1 && echo "1" || echo "0"
;;
netifyd)
pgrep -f netifyd > /dev/null 2>&1 && echo "1" || echo "0"
;;
wireguard)
[ -n "$(wg show 2>/dev/null)" ] && echo "1" || echo "0"
;;
cdn_cache)
pgrep -f "nginx.*cdn-cache" > /dev/null 2>&1 && echo "1" || echo "0"
;;
*)
# Generic check via init.d
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} running > /dev/null 2>&1 && echo "1" || echo "0"
else
echo "0"
fi
;;
esac
}
# Determine module status based on enabled + running
get_module_status() {
local enabled="$1"
local running="$2"
if [ "$enabled" = "1" ] && [ "$running" = "1" ]; then
echo "active"
elif [ "$enabled" = "1" ] && [ "$running" = "0" ]; then
echo "error"
elif [ "$enabled" = "0" ] && [ "$running" = "0" ]; then
echo "disabled"
else
echo "unknown"
fi
}
# Get overall system status
get_status() {
local total=0
local installed=0
local running=0
json_init
json_add_string "version" "$PKG_VERSION"
json_add_string "hostname" "$(uci -q get system.@system[0].hostname || echo 'SecuBox')"
json_add_boolean "enabled" "$(uci -q get secubox.main.enabled || echo 1)"
# System info
local uptime=$(cat /proc/uptime | cut -d. -f1)
local load=$(cat /proc/loadavg | cut -d' ' -f1-3)
local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
local mem_used=$((mem_total - mem_free))
local mem_pct=$((mem_used * 100 / mem_total))
json_add_int "uptime" "$uptime"
json_add_string "load" "$load"
json_add_int "memory_total" "$mem_total"
json_add_int "memory_used" "$mem_used"
json_add_int "memory_percent" "$mem_pct"
# Count modules from UCI config (same as get_modules)
config_load secubox 2>/dev/null || true
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
for module in $module_sections; do
total=$((total + 1))
local is_installed=$(check_module_installed "$module")
local is_running=$(check_module_running "$module")
if [ "$is_installed" = "1" ]; then
installed=$((installed + 1))
fi
if [ "$is_running" = "1" ]; then
running=$((running + 1))
fi
done
json_add_int "modules_total" "$total"
json_add_int "modules_installed" "$installed"
json_add_int "modules_running" "$running"
json_dump
}
# Detect real installed luci-app packages (supports both opkg and apk)
detect_real_modules() {
if [ -x "/usr/bin/apk" ]; then
# OpenWrt 25.12+ uses apk
apk list --installed 2>/dev/null | grep "^luci-app-" | grep -v "^luci-app-secubox-" | while IFS=- read -r pkg rest; do
# Parse apk output: luci-app-network-modes-0.3.1-r1 noarch {...} (license) [installed]
local package=$(echo "$pkg-$rest" | awk '{print $1}')
local version=$(echo "$package" | sed 's/^luci-app-[^-]*-//' | sed 's/-r[0-9]*$//')
local pkg_name=$(echo "$package" | sed 's/-[0-9].*//')
local module_id=$(echo "$pkg_name" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/_/g')
echo "$module_id:$pkg_name:$version"
done
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
# OpenWrt 24.10 and earlier uses opkg
opkg list-installed 2>/dev/null | grep "^luci-app-" | grep -v "^luci-app-secubox " | while read package version; do
# Extract module ID from package name
local module_id=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/_/g')
echo "$module_id:$package:$version"
done
fi
}
# Get detailed modules list (UCI + auto-detected)
get_modules() {
json_init
json_add_array "modules"
config_load secubox 2>/dev/null || true
# Create associative array to track processed modules
local processed_modules=""
# First, add modules from UCI config
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
for module in $module_sections; do
local name desc category icon color package config version
config_get name "$module" name "$module"
config_get desc "$module" description ""
config_get category "$module" category "other"
config_get icon "$module" icon "📦"
config_get color "$module" color "#64748b"
config_get package "$module" package ""
config_get config "$module" config ""
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
local status=$(get_module_status "$is_enabled" "$is_running")
# Get real version from package manager if installed
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
if [ -x "/usr/bin/apk" ]; then
# OpenWrt 25.12+ uses apk
# apk output: luci-app-network-modes-0.3.1-r1 noarch {...}
local real_version=$(apk list --installed "$package" 2>/dev/null | head -1 | awk '{print $1}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
[ -n "$real_version" ] && version="$real_version"
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
# OpenWrt 24.10 and earlier uses opkg
local real_version=$(opkg list-installed "$package" 2>/dev/null | awk '{print $3}' | sed 's/-.*$//')
[ -n "$real_version" ] && version="$real_version"
fi
fi
json_add_object ""
json_add_string "id" "$module"
json_add_string "name" "$name"
json_add_string "description" "$desc"
json_add_string "category" "$category"
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_string "package" "$package"
json_add_string "config" "$config"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "1"
json_close_object
processed_modules="$processed_modules $module"
done
# Second, add auto-detected modules not in UCI
detect_real_modules | while IFS=: read module_id package version; do
# Skip if already processed
echo "$processed_modules" | grep -q " $module_id " && continue
# Auto-detect properties based on package name
local name=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1)) substr($i,2)}}1')
local category="other"
local icon="📦"
local color="#64748b"
# Auto-categorize
case "$package" in
*crowdsec*|*guardian*|*auth*)
category="security"; icon="🛡️"; color="#22c55e"
;;
*netdata*|*monitoring*)
category="monitoring"; icon="📊"; color="#00ab44"
;;
*network*|*wireguard*|*bandwidth*|*traffic*)
category="network"; icon="🌐"; color="#3b82f6"
;;
*system*|*hub*)
category="system"; icon="⚙️"; color="#6366f1"
;;
esac
local clean_module=$(echo "$module_id" | sed 's/_/-/g')
local is_enabled=$(check_module_enabled "$module_id")
local is_running=$(check_module_running "$module_id")
local status=$(get_module_status "$is_enabled" "$is_running")
json_add_object ""
json_add_string "id" "$module_id"
json_add_string "name" "$name"
json_add_string "description" "Auto-detected module"
json_add_string "category" "$category"
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_string "package" "$package"
json_add_string "config" "$clean_module"
json_add_string "version" "$version"
json_add_boolean "installed" "1"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "0"
json_close_object
done
json_close_array
json_dump
}
# Get modules by category
get_modules_by_category() {
local category="$1"
json_init
json_add_array "modules"
config_load secubox 2>/dev/null || true
local processed_modules=""
# List all module sections from UCI config
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
# First pass: UCI-defined modules in this category
for module in $module_sections; do
local mod_category
config_get mod_category "$module" category "other"
if [ "$mod_category" = "$category" ]; then
local name desc icon color package config version
config_get name "$module" name "$module"
config_get desc "$module" description ""
config_get icon "$module" icon "box"
config_get color "$module" color "#64748b"
config_get package "$module" package ""
config_get config "$module" config ""
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
local status=$(get_module_status "$is_enabled" "$is_running")
# Get real version from opkg if installed
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
local real_version=$(opkg list-installed "$package" 2>/dev/null | awk '{print $3}' | sed 's/-.*$//')
[ -n "$real_version" ] && version="$real_version"
fi
json_add_object ""
json_add_string "id" "$module"
json_add_string "name" "$name"
json_add_string "description" "$desc"
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_string "version" "$version"
json_add_string "package" "$package"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "1"
json_close_object
processed_modules="$processed_modules $module"
fi
done
# Second pass: Auto-detected modules not in UCI
detect_real_modules | while IFS=: read module_id package version; do
# Skip if already processed from UCI
echo "$processed_modules" | grep -q " $module_id " && continue
# Auto-categorize
local mod_category icon color name
case "$package" in
*crowdsec*|*guardian*|*auth*)
mod_category="security"; icon="🛡️"; color="#22c55e"
;;
*netdata*|*monitoring*)
mod_category="monitoring"; icon="📊"; color="#00ab44"
;;
*network*|*wireguard*|*bandwidth*|*traffic*|*cdn*)
mod_category="network"; icon="🌐"; color="#3b82f6"
;;
*system*|*hub*|*vhost*)
mod_category="system"; icon="⚙️"; color="#6366f1"
;;
*)
mod_category="other"; icon="📦"; color="#64748b"
;;
esac
# Only include if matches requested category
if [ "$mod_category" = "$category" ]; then
# Generate nice name from package
name=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
local is_installed="1" # Must be installed if detected
local is_enabled=$(check_module_enabled "$module_id")
local is_running=$(check_module_running "$module_id")
local status=$(get_module_status "$is_enabled" "$is_running")
json_add_object ""
json_add_string "id" "$module_id"
json_add_string "name" "$name"
json_add_string "description" "Auto-detected module"
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_string "version" "$version"
json_add_string "package" "$package"
json_add_string "category" "$mod_category"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "0"
json_close_object
fi
done
json_close_array
json_dump
}
# Get single module info
get_module_info() {
local module="$1"
config_load secubox
local name desc category icon color package config version
config_get name "$module" name "$module"
config_get desc "$module" description ""
config_get category "$module" category "other"
config_get icon "$module" icon "box"
config_get color "$module" color "#64748b"
config_get package "$module" package ""
config_get config "$module" config ""
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
local status=$(get_module_status "$is_enabled" "$is_running")
json_init
json_add_string "id" "$module"
json_add_string "name" "$name"
json_add_string "description" "$desc"
json_add_string "category" "$category"
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_string "package" "$package"
json_add_string "config" "$config"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_dump
}
# Start a module
start_module() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} start
json_init
json_add_boolean "success" 1
json_add_string "message" "Module started"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "message" "Init script not found"
json_dump
fi
}
# Stop a module
stop_module() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} stop
json_init
json_add_boolean "success" 1
json_add_string "message" "Module stopped"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "message" "Init script not found"
json_dump
fi
}
# Restart a module (DEPRECATED - use disable/enable instead)
restart_module() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} restart
json_init
json_add_boolean "success" 1
json_add_string "message" "Module restarted"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "message" "Init script not found"
json_dump
fi
}
# Enable a module (NEW v0.3.1)
enable_module() {
local module="$1"
# Set enabled flag in UCI (v0.3.1)
# This only activates the module in configuration
# Use start_module() to actually start the service
uci set secubox.${module}.enabled='1'
uci commit secubox
json_init
json_add_boolean "success" 1
json_add_string "message" "Module activé dans la configuration"
json_add_string "note" "Utilisez 'Démarrer' pour lancer le service"
json_dump
}
# Disable a module (NEW v0.3.1)
disable_module() {
local module="$1"
# Set disabled flag in UCI (v0.3.1)
# This only deactivates the module in configuration
# Use stop_module() to actually stop the service
uci set secubox.${module}.enabled='0'
uci commit secubox
json_init
json_add_boolean "success" 1
json_add_string "message" "Module désactivé dans la configuration"
json_add_string "note" "Utilisez 'Arrêter' pour stopper le service"
json_dump
}
# Get health report
get_health() {
json_init
json_add_array "checks"
config_load secubox 2>/dev/null || true
# List all module sections from UCI config
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
# Check each installed module
for module in $module_sections; do
local is_installed=$(check_module_installed "$module")
if [ "$is_installed" = "1" ]; then
local is_running=$(check_module_running "$module")
local name
config_get name "$module" name "$module"
local status="ok"
local message="Running normally"
if [ "$is_running" != "1" ]; then
status="warning"
message="Service not running"
fi
json_add_object ""
json_add_string "module" "$module"
json_add_string "name" "$name"
json_add_string "status" "$status"
json_add_string "message" "$message"
json_close_object
fi
done
json_close_array
# Overall health
local overall="healthy"
# Could add more sophisticated health checks here
json_add_string "overall" "$overall"
json_add_int "timestamp" "$(date +%s)"
json_dump
}
# Generate diagnostics bundle
get_diagnostics() {
json_init
# System info
json_add_object "system"
json_add_string "hostname" "$(uci -q get system.@system[0].hostname)"
json_add_string "model" "$(cat /tmp/sysinfo/model 2>/dev/null || echo 'Unknown')"
json_add_string "openwrt_version" "$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d= -f2 | tr -d \"\')"
json_add_string "kernel" "$(uname -r)"
json_add_int "uptime" "$(cat /proc/uptime | cut -d. -f1)"
json_close_object
# Modules status
json_add_array "modules"
config_load secubox 2>/dev/null || true
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
for module in $module_sections; do
local is_installed=$(check_module_installed "$module")
local is_running=$(check_module_running "$module")
json_add_object ""
json_add_string "id" "$module"
json_add_boolean "installed" "$is_installed"
json_add_boolean "running" "$is_running"
json_close_object
done
json_close_array
# Network interfaces
json_add_array "interfaces"
for iface in $(ls /sys/class/net/); do
local mac=$(cat /sys/class/net/$iface/address 2>/dev/null)
local state=$(cat /sys/class/net/$iface/operstate 2>/dev/null)
json_add_object ""
json_add_string "name" "$iface"
json_add_string "mac" "$mac"
json_add_string "state" "$state"
json_close_object
done
json_close_array
json_add_int "generated" "$(date +%s)"
json_dump
}
# Get system health metrics
get_system_health() {
json_init
# CPU usage (based on load average)
local load=$(cat /proc/loadavg | cut -d' ' -f1)
local cpu_cores=$(grep -c processor /proc/cpuinfo)
local cpu_pct=$(awk "BEGIN {printf \"%.0f\", ($load / $cpu_cores) * 100}")
# Memory
local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
local mem_avail=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
local mem_used=$((mem_total - mem_avail))
local mem_pct=$((mem_used * 100 / mem_total))
# Disk usage (root filesystem)
local disk_info=$(df / | tail -1)
local disk_total=$(echo "$disk_info" | awk '{print $2}')
local disk_used=$(echo "$disk_info" | awk '{print $3}')
local disk_pct=$(echo "$disk_info" | awk '{print $5}' | tr -d '%')
# Network stats (sum all interfaces)
local net_rx=0
local net_tx=0
for iface in $(ls /sys/class/net/ | grep -v lo); do
if [ -f "/sys/class/net/$iface/statistics/rx_bytes" ]; then
local rx=$(cat /sys/class/net/$iface/statistics/rx_bytes 2>/dev/null || echo 0)
local tx=$(cat /sys/class/net/$iface/statistics/tx_bytes 2>/dev/null || echo 0)
net_rx=$((net_rx + rx))
net_tx=$((net_tx + tx))
fi
done
# Uptime
local uptime=$(cat /proc/uptime | cut -d. -f1)
# Load averages
local load_1min=$(cat /proc/loadavg | cut -d' ' -f1)
local load_5min=$(cat /proc/loadavg | cut -d' ' -f2)
local load_15min=$(cat /proc/loadavg | cut -d' ' -f3)
json_add_object "cpu"
json_add_int "percent" "$cpu_pct"
json_add_int "cores" "$cpu_cores"
json_add_string "load_1min" "$load_1min"
json_add_string "load_5min" "$load_5min"
json_add_string "load_15min" "$load_15min"
json_close_object
json_add_object "memory"
json_add_int "percent" "$mem_pct"
json_add_int "total_kb" "$mem_total"
json_add_int "used_kb" "$mem_used"
json_add_int "available_kb" "$mem_avail"
json_close_object
json_add_object "disk"
json_add_int "percent" "$disk_pct"
json_add_int "total_kb" "$disk_total"
json_add_int "used_kb" "$disk_used"
json_close_object
json_add_object "network"
json_add_int "rx_bytes" "$net_rx"
json_add_int "tx_bytes" "$net_tx"
json_close_object
json_add_int "uptime" "$uptime"
json_add_int "timestamp" "$(date +%s)"
json_dump
}
# Get aggregated alerts from all modules
get_alerts() {
json_init
json_add_array "alerts"
local alert_count=0
# Check each installed module for alerts
config_load secubox 2>/dev/null || true
# List all module sections from UCI config
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
for module in $module_sections; do
local is_installed=$(check_module_installed "$module")
if [ "$is_installed" = "1" ]; then
# Check if module service is not running
local is_running=$(check_module_running "$module")
if [ "$is_running" != "1" ]; then
local name
config_get name "$module" name "$module"
json_add_object ""
json_add_string "module" "$module"
json_add_string "message" "$name service is not running"
json_add_string "severity" "warning"
json_add_int "timestamp" "$(date +%s)"
json_close_object
alert_count=$((alert_count + 1))
fi
fi
done
json_close_array
json_add_int "count" "$alert_count"
json_add_int "timestamp" "$(date +%s)"
json_dump
}
# Execute quick actions
quick_action() {
local action="$1"
json_init
case "$action" in
restart_rpcd)
/etc/init.d/rpcd restart >/dev/null 2>&1
json_add_boolean "success" 1
json_add_string "message" "RPCD service restarted"
;;
restart_uhttpd)
/etc/init.d/uhttpd restart >/dev/null 2>&1
json_add_boolean "success" 1
json_add_string "message" "uHTTPd service restarted"
;;
clear_cache)
sync
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null
json_add_boolean "success" 1
json_add_string "message" "System cache cleared"
;;
backup_config)
local backup_file="/tmp/backup-$(date +%Y%m%d-%H%M%S).tar.gz"
sysupgrade -b "$backup_file" >/dev/null 2>&1
if [ -f "$backup_file" ]; then
json_add_boolean "success" 1
json_add_string "message" "Configuration backup created"
json_add_string "file" "$backup_file"
else
json_add_boolean "success" 0
json_add_string "message" "Backup failed"
fi
;;
restart_network)
/etc/init.d/network restart >/dev/null 2>&1
json_add_boolean "success" 1
json_add_string "message" "Network services restarted"
;;
restart_firewall)
/etc/init.d/firewall restart >/dev/null 2>&1
json_add_boolean "success" 1
json_add_string "message" "Firewall restarted"
;;
*)
json_add_boolean "success" 0
json_add_string "message" "Unknown action: $action"
;;
esac
json_add_int "timestamp" "$(date +%s)"
json_dump
}
# Get all dashboard data in one call
get_dashboard_data() {
json_init
# Get status info
local uptime=$(cat /proc/uptime | cut -d. -f1)
local load=$(cat /proc/loadavg | cut -d' ' -f1-3)
local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
local mem_used=$((mem_total - mem_free))
local mem_pct=$((mem_used * 100 / mem_total))
json_add_object "status"
json_add_string "version" "$PKG_VERSION"
json_add_string "hostname" "$(uci -q get system.@system[0].hostname || echo 'SecuBox')"
json_add_int "uptime" "$uptime"
json_add_string "load" "$load"
json_add_int "memory_total" "$mem_total"
json_add_int "memory_used" "$mem_used"
json_add_int "memory_percent" "$mem_pct"
json_close_object
# Load config once for all modules
config_load secubox 2>/dev/null || true
# Get modules list
json_add_array "modules"
local total=0 installed=0 running=0
# List all module sections from UCI config
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
for module in $module_sections; do
local name desc category icon color package version
config_get name "$module" name "$module"
config_get desc "$module" description ""
config_get category "$module" category "other"
config_get icon "$module" icon "box"
config_get color "$module" color "#64748b"
config_get package "$module" package ""
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled="0"
local is_running="0"
local status="disabled"
total=$((total + 1))
if [ "$is_installed" = "1" ]; then
is_enabled=$(check_module_enabled "$module")
is_running=$(check_module_running "$module")
status=$(get_module_status "$is_enabled" "$is_running")
installed=$((installed + 1))
[ "$is_running" = "1" ] && running=$((running + 1))
# Get real version from package manager if installed
if [ -n "$package" ]; then
if [ -x "/usr/bin/apk" ]; then
# OpenWrt 25.12+ uses apk
local real_version=$(apk list --installed "$package" 2>/dev/null | head -1 | awk '{print $1}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
[ -n "$real_version" ] && version="$real_version"
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
# OpenWrt 24.10 and earlier uses opkg
local real_version=$(opkg list-installed "$package" 2>/dev/null | awk '{print $3}' | sed 's/-.*$//')
[ -n "$real_version" ] && version="$real_version"
fi
fi
fi
json_add_object ""
json_add_string "id" "$module"
json_add_string "name" "$name"
json_add_string "description" "$desc"
json_add_string "category" "$category"
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_close_object
done
json_close_array
json_add_object "counts"
json_add_int "total" "$total"
json_add_int "installed" "$installed"
json_add_int "running" "$running"
json_close_object
# Get system health
local cpu_cores=$(grep -c processor /proc/cpuinfo)
local load_val=$(echo "$load" | cut -d' ' -f1)
local cpu_pct=$(awk "BEGIN {printf \"%.0f\", ($load_val / $cpu_cores) * 100}" 2>/dev/null || echo "0")
local disk_pct=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
json_add_object "health"
json_add_int "cpu_percent" "$cpu_pct"
json_add_int "memory_percent" "$mem_pct"
json_add_int "disk_percent" "$disk_pct"
json_close_object
# Empty alerts array
json_add_array "alerts"
json_close_array
json_add_int "timestamp" "$(date +%s)"
json_dump
}
# Get theme setting
get_theme() {
local theme="dark"
# Load secubox config
if [ -f "/etc/config/secubox" ]; then
config_load secubox
config_get theme main theme "dark"
fi
json_init
json_add_string "theme" "$theme"
json_dump
}
# Persist theme setting
set_theme() {
local input theme="dark"
read -r input
[ -n "$input" ] && json_load "$input"
json_get_var theme theme "dark"
case "$theme" in
dark|light|system|cyberpunk)
;;
*)
json_init
json_add_boolean "success" 0
json_add_string "message" "Invalid theme"
json_dump
return 1
;;
esac
uci -q set secubox.main.theme="$theme"
uci -q commit secubox
json_init
json_add_boolean "success" 1
json_add_string "theme" "$theme"
json_add_string "message" "Theme updated"
json_dump
}
# Dismiss a specific alert
dismiss_alert() {
local alert_id="$1"
# Store dismissed alerts in tmp file
local dismissed_file="/tmp/secubox_dismissed_alerts"
# Append alert_id to dismissed list
echo "$alert_id" >> "$dismissed_file"
json_init
json_add_boolean "success" 1
json_add_string "message" "Alert dismissed"
json_dump
}
# Clear all alerts
clear_alerts() {
# Clear dismissed alerts file
local dismissed_file="/tmp/secubox_dismissed_alerts"
rm -f "$dismissed_file"
json_init
json_add_boolean "success" 1
json_add_string "message" "All alerts cleared"
json_dump
}
# Fix permissions (v0.3.1)
fix_permissions() {
local fix_script="/usr/libexec/secubox/fix-permissions.sh"
if [ ! -x "$fix_script" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Fix script not found or not executable"
json_dump
return 1
fi
# Run the fix script and capture output
local output=$($fix_script 2>&1)
local exit_code=$?
json_init
if [ $exit_code -eq 0 ]; then
json_add_boolean "success" 1
json_add_string "message" "Permissions fixed successfully"
else
json_add_boolean "success" 0
json_add_string "message" "Failed to fix permissions"
fi
json_add_string "output" "$output"
json_dump
}
# Main dispatcher
first_run_status() {
local timezone storage network_json mode_name mode_id storage_path
storage_path=$(get_storage_path)
local storage_ready=0
[ -d "$storage_path" ] && storage_ready=1
local password_state=0
if is_root_password_set; then
password_state=1
fi
timezone=$(uci -q get system.@system[0].timezone || echo "UTC")
mode_id="unknown"
mode_name="N/A"
if command -v ubus >/dev/null 2>&1; then
network_json=$(ubus call luci.network-modes status 2>/dev/null || printf '')
if [ -n "$network_json" ]; then
mode_id=$(jsonfilter -s "$network_json" -e '@.current_mode' 2>/dev/null || echo "$mode_id")
mode_name=$(jsonfilter -s "$network_json" -e '@.mode_name' 2>/dev/null || echo "$mode_name")
fi
fi
json_init
json_add_boolean "password_set" "$password_state"
json_add_string "timezone" "$timezone"
json_add_string "storage_path" "$storage_path"
json_add_boolean "storage_ready" "$storage_ready"
json_add_string "network_mode" "$mode_id"
json_add_string "network_mode_name" "$mode_name"
json_add_array "recommended_modes"
json_add_string "" "router"
json_add_string "" "dmz"
json_close_array
json_dump
}
apply_first_run() {
local input
read input
json_load "$input"
json_get_var timezone timezone
json_get_var storage_path storage_path
json_get_var network_mode network_mode
json_init
json_add_array "messages"
if [ -n "$timezone" ]; then
uci set system.@system[0].timezone="$timezone"
uci set system.@system[0].zonename="$timezone"
uci commit system
if [ -f "/usr/share/zoneinfo/$timezone" ]; then
ln -sf "/usr/share/zoneinfo/$timezone" /etc/localtime
fi
json_add_string "" "Timezone updated"
fi
if [ -n "$storage_path" ]; then
mkdir -p "$storage_path"
chmod 755 "$storage_path"
uci set secubox.main.storage_path="$storage_path"
uci commit secubox
json_add_string "" "Storage prepared at $storage_path"
fi
if [ -n "$network_mode" ]; then
if command -v ubus >/dev/null 2>&1; then
if ubus call luci.network-modes set_mode "{\"mode\":\"$network_mode\"}" >/dev/null 2>&1; then
if ubus call luci.network-modes apply_mode >/dev/null 2>&1; then
json_add_string "" "Network mode $network_mode applied"
else
json_add_string "" "Failed to apply network mode"
fi
else
json_add_string "" "Mode $network_mode unsupported"
fi
else
json_add_string "" "ubus unavailable; cannot change network mode"
fi
fi
json_close_array
json_add_boolean "success" 1
json_dump
}
list_apps() {
ensure_directory "$PLUGIN_DIR"
json_init
json_add_array "apps"
local manifest_file manifest id name runtime type version description state wizard_field category maturity seen_ids=""
while IFS= read -r manifest_file; do
[ -f "$manifest_file" ] || continue
manifest=$(cat "$manifest_file")
id=$(jsonfilter -s "$manifest" -e '@.id' 2>/dev/null)
[ -n "$id" ] || continue
case " $seen_ids " in
*" $id "*) continue ;;
esac
if ! validate_manifest_json "$manifest"; then
continue
fi
seen_ids="$seen_ids $id"
name=$(jsonfilter -s "$manifest" -e '@.name' 2>/dev/null)
type=$(jsonfilter -s "$manifest" -e '@.type' 2>/dev/null)
runtime=$(jsonfilter -s "$manifest" -e '@.runtime' 2>/dev/null)
[ -n "$type" ] || type="$runtime"
version=$(jsonfilter -s "$manifest" -e '@.version' 2>/dev/null)
description=$(jsonfilter -s "$manifest" -e '@.description' 2>/dev/null)
category=$(jsonfilter -s "$manifest" -e '@.category' 2>/dev/null)
maturity=$(jsonfilter -s "$manifest" -e '@.maturity' 2>/dev/null)
state=$(packages_state "$manifest")
wizard_field=$(jsonfilter -s "$manifest" -e '@.wizard.fields[0].id' 2>/dev/null)
json_add_object
json_add_string "id" "$id"
json_add_string "name" "$name"
json_add_string "runtime" "$runtime"
json_add_string "type" "$type"
json_add_string "version" "$version"
json_add_string "description" "$description"
json_add_string "category" "$category"
json_add_string "maturity" "$maturity"
json_add_string "state" "$state"
json_add_boolean "has_wizard" "$([ -n "$wizard_field" ] && echo 1 || echo 0)"
json_close_object
done <<EOF
$(manifest_files | sort)
EOF
json_close_array
json_dump
}
get_app_manifest() {
local input app_id file manifest state
read input
app_id=$(jsonfilter -s "$input" -e '@.app_id' 2>/dev/null)
if [ -z "$app_id" ]; then
json_init
json_add_boolean "success" 0
json_add_string "error" "app_id required"
json_dump
return
fi
file=$(manifest_file_for_id "$app_id")
if [ -z "$file" ] || [ ! -f "$file" ]; then
json_init
json_add_boolean "success" 0
json_add_string "error" "Manifest not found"
json_dump
return
fi
manifest=$(cat "$file")
state=$(packages_state "$manifest")
json_load "$manifest"
json_add_string "state" "$state"
json_dump
}
apply_app_wizard() {
local input app_id file manifest config section fields field option value
read input
app_id=$(jsonfilter -s "$input" -e '@.app_id' 2>/dev/null)
if [ -z "$app_id" ]; then
json_init
json_add_boolean "success" 0
json_add_string "error" "app_id required"
json_dump
return
fi
file=$(manifest_file_for_id "$app_id")
if [ -z "$file" ] || [ ! -f "$file" ]; then
json_init
json_add_boolean "success" 0
json_add_string "error" "Manifest not found"
json_dump
return
fi
manifest=$(cat "$file")
config=$(jsonfilter -s "$manifest" -e '@.wizard.uci.config' 2>/dev/null)
section=$(jsonfilter -s "$manifest" -e '@.wizard.uci.section' 2>/dev/null)
[ -n "$section" ] || section="main"
if [ -z "$config" ]; then
json_init
json_add_boolean "success" 0
json_add_string "error" "Wizard missing UCI config"
json_dump
return
fi
fields=$(jsonfilter -s "$manifest" -e '@.wizard.fields[*].id' 2>/dev/null)
for field in $fields; do
option=$(jsonfilter -s "$manifest" -e "@.wizard.fields[@.id='$field'].uci_option" 2>/dev/null)
[ -n "$option" ] || continue
value=$(jsonfilter -s "$input" -e "@.values.$field" 2>/dev/null)
[ -n "$value" ] || continue
uci set ${config}.${section}.${option}="$value"
done
uci commit "$config"
json_init
json_add_boolean "success" 1
json_dump
}
list_profiles() {
ensure_directory "$PROFILE_DIR"
json_init
json_add_array "profiles"
local profile_file
for profile_file in "$PROFILE_DIR"/*.json; do
[ -f "$profile_file" ] || continue
local profile_json id name description network_mode apps state
profile_json=$(cat "$profile_file")
id=$(jsonfilter -s "$profile_json" -e '@.id' 2>/dev/null)
name=$(jsonfilter -s "$profile_json" -e '@.name' 2>/dev/null)
description=$(jsonfilter -s "$profile_json" -e '@.description' 2>/dev/null)
network_mode=$(jsonfilter -s "$profile_json" -e '@.network_mode' 2>/dev/null)
apps=$(jsonfilter -s "$profile_json" -e '@.apps[*]' 2>/dev/null)
state=$(packages_state "$profile_json")
[ -n "$id" ] || continue
json_add_object
json_add_string "id" "$id"
json_add_string "name" "$name"
json_add_string "description" "$description"
json_add_string "network_mode" "$network_mode"
json_add_string "state" "$state"
json_add_array "apps"
for app in $apps; do
json_add_string "" "$app"
done
json_close_array
json_close_object
done
json_close_array
json_dump
}
apply_profile() {
local input profile_id profile_file profile_json network_mode packages apps backup_file notes=""
read input
json_load "$input"
json_get_var profile_id profile_id
json_cleanup
[ -n "$profile_id" ] || {
json_init; json_add_boolean "success" 0; json_add_string "error" "profile_id required"; json_dump; return;
}
profile_file="$PROFILE_DIR/$profile_id.json"
if [ ! -f "$profile_file" ]; then
json_init; json_add_boolean "success" 0; json_add_string "error" "Profile not found"; json_dump; return;
fi
profile_json=$(cat "$profile_file")
ensure_directory "$PROFILE_BACKUP_DIR"
local timestamp=$(date +%Y%m%d_%H%M%S)
backup_file="$PROFILE_BACKUP_DIR/profile_${profile_id}_${timestamp}.tar.gz"
tar -czf "$backup_file" /etc/config 2>/dev/null
notes="${notes}Backup saved to $backup_file\n"
uci set secubox.profile.last_backup="$backup_file"
uci set secubox.profile.last_profile="$profile_id"
uci commit secubox
network_mode=$(jsonfilter -s "$profile_json" -e '@.network_mode' 2>/dev/null)
if [ -n "$network_mode" ]; then
notes="${notes}Switched network mode to $network_mode\n"
apply_network_mode "$network_mode"
fi
packages=$(jsonfilter -s "$profile_json" -e '@.packages[*]' 2>/dev/null)
for pkg in $packages; do
install_package_if_missing "$pkg"
notes="${notes}Ensured package $pkg\n"
done
apps=$(jsonfilter -s "$profile_json" -e '@.apps[*]' 2>/dev/null)
for app in $apps; do
if [ -x "$SECOBOX_APP" ]; then
$SECOBOX_APP install "$app" >/dev/null 2>&1 && notes="${notes}Installed app $app\n"
fi
done
json_load "$profile_json"
local uci_configs=""
if json_select uci >/dev/null 2>&1; then
local entries
json_get_keys entries
for entry in $entries; do
json_select "$entry"
local cfg section option value
json_get_var cfg config
json_get_var section section
json_get_var option option
json_get_var value value
section=${section:-main}
if [ -n "$cfg" ] && [ -n "$option" ]; then
uci set ${cfg}.${section}.${option}="$value"
uci_configs="${uci_configs} $cfg"
notes="${notes}Set ${cfg}.${section}.${option}=${value}\n"
fi
json_select ..
done
json_select ..
fi
json_cleanup
local committed=""
for cfg in $uci_configs; do
case " $committed " in
*" $cfg "*) continue;;
esac
uci commit "$cfg"
committed="$committed $cfg"
done
json_init
json_add_boolean "success" 1
json_add_array "messages"
printf '%b' "$notes" | while IFS= read -r line; do
[ -n "$line" ] && json_add_string "" "$line"
done
json_close_array
json_add_string "backup" "$backup_file"
json_dump
}
rollback_profile() {
local latest_backup
latest_backup=$(ls -t "$PROFILE_BACKUP_DIR"/profile_*.tar.gz 2>/dev/null | head -1)
if [ -z "$latest_backup" ]; then
json_init; json_add_boolean "success" 0; json_add_string "error" "No backups available"; json_dump; return;
fi
cd /
tar -xzf "$latest_backup" 2>/dev/null
/etc/init.d/network reload >/dev/null 2>&1
/etc/init.d/firewall reload >/dev/null 2>&1
/etc/init.d/dnsmasq reload >/dev/null 2>&1
json_init
json_add_boolean "success" 1
json_add_string "message" "Restored $latest_backup"
json_dump
}
# ============================================================================
# App Store Functions
# ============================================================================
APPSTORE_JSON="/usr/share/secubox/appstore/apps.json"
# Get all apps from app store catalog
get_appstore_apps() {
if [ ! -f "$APPSTORE_JSON" ]; then
json_init
json_add_array "apps"
json_close_array
json_add_object "categories"
json_close_object
json_dump
return
fi
local appstore_data=$(cat "$APPSTORE_JSON")
local app_count=$(jsonfilter -s "$appstore_data" -e '@.apps[*]' | wc -l)
json_init
json_add_array "apps"
local i=0
while [ $i -lt $app_count ]; do
local app_id=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].id")
local app_name=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].name")
local app_version=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].version")
local app_category=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].category")
local app_description=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].description")
local app_icon=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].icon")
local app_status=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].status")
local app_luci=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].luci_app")
local app_notes=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].notes")
# Check if app is installed
local is_installed=0
if command -v opkg >/dev/null 2>&1; then
opkg list-installed "$app_id" 2>/dev/null | grep -q "^$app_id " && is_installed=1
elif command -v apk >/dev/null 2>&1; then
apk info -e "$app_id" >/dev/null 2>&1 && is_installed=1
fi
json_add_object
json_add_string "id" "$app_id"
json_add_string "name" "$app_name"
json_add_string "version" "$app_version"
json_add_string "category" "$app_category"
json_add_string "description" "$app_description"
json_add_string "icon" "$app_icon"
json_add_string "status" "$app_status"
json_add_boolean "installed" "$is_installed"
[ -n "$app_luci" ] && json_add_string "luci_app" "$app_luci"
[ -n "$app_notes" ] && json_add_string "notes" "$app_notes"
json_close_object
i=$((i + 1))
done
json_close_array
# Add categories
json_add_object "categories"
local cat_keys=$(jsonfilter -s "$appstore_data" -e '@.categories' | jsonfilter -e '@[@]')
for cat in security network iot media; do
local cat_name=$(jsonfilter -s "$appstore_data" -e "@.categories.$cat.name")
local cat_icon=$(jsonfilter -s "$appstore_data" -e "@.categories.$cat.icon")
local cat_desc=$(jsonfilter -s "$appstore_data" -e "@.categories.$cat.description")
if [ -n "$cat_name" ]; then
json_add_object "$cat"
json_add_string "name" "$cat_name"
json_add_string "icon" "$cat_icon"
json_add_string "description" "$cat_desc"
json_close_object
fi
done
json_close_object
json_dump
}
# Get single app from app store
get_appstore_app() {
local input app_id
read input
json_load "$input"
json_get_var app_id app_id
json_cleanup
if [ ! -f "$APPSTORE_JSON" ]; then
json_init
json_add_boolean "success" 0
json_add_string "error" "App store catalog not found"
json_dump
return
fi
local appstore_data=$(cat "$APPSTORE_JSON")
local app_count=$(jsonfilter -s "$appstore_data" -e '@.apps[*]' | wc -l)
local i=0
while [ $i -lt $app_count ]; do
local current_id=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].id")
if [ "$current_id" = "$app_id" ]; then
# Found the app
json_init
json_add_string "id" "$current_id"
json_add_string "name" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].name")"
json_add_string "version" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].version")"
json_add_string "category" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].category")"
json_add_string "description" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].description")"
json_add_string "icon" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].icon")"
json_add_string "author" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].author")"
json_add_string "license" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].license")"
json_add_string "url" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].url")"
json_add_string "status" "$(jsonfilter -s "$appstore_data" -e "@.apps[$i].status")"
local app_luci=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].luci_app")
[ -n "$app_luci" ] && [ "$app_luci" != "null" ] && json_add_string "luci_app" "$app_luci"
local app_notes=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].notes")
[ -n "$app_notes" ] && [ "$app_notes" != "null" ] && json_add_string "notes" "$app_notes"
# Add dependencies array
json_add_array "dependencies"
local dep_count=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].dependencies[*]" | wc -l)
local j=0
while [ $j -lt $dep_count ]; do
local dep=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].dependencies[$j]")
json_add_string "" "$dep"
j=$((j + 1))
done
json_close_array
# Add tags array
json_add_array "tags"
local tag_count=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].tags[*]" | wc -l)
j=0
while [ $j -lt $tag_count ]; do
local tag=$(jsonfilter -s "$appstore_data" -e "@.apps[$i].tags[$j]")
json_add_string "" "$tag"
j=$((j + 1))
done
json_close_array
# Check installation status
local is_installed=0
if command -v opkg >/dev/null 2>&1; then
opkg list-installed "$app_id" 2>/dev/null | grep -q "^$app_id " && is_installed=1
elif command -v apk >/dev/null 2>&1; then
apk info -e "$app_id" >/dev/null 2>&1 && is_installed=1
fi
json_add_boolean "installed" "$is_installed"
json_dump
return
fi
i=$((i + 1))
done
# App not found
json_init
json_add_boolean "success" 0
json_add_string "error" "App not found"
json_dump
}
# Install app from app store
install_appstore_app() {
local input app_id
read input
json_load "$input"
json_get_var app_id app_id
json_cleanup
[ -z "$app_id" ] && {
json_init
json_add_boolean "success" 0
json_add_string "error" "app_id required"
json_dump
return
}
# Update package lists if not done in this session
if [ "$OPKG_UPDATED" -eq 0 ]; then
if command -v opkg >/dev/null 2>&1; then
opkg update >/dev/null 2>&1
elif command -v apk >/dev/null 2>&1; then
apk update >/dev/null 2>&1
fi
OPKG_UPDATED=1
fi
# Install the package
local install_output
if command -v opkg >/dev/null 2>&1; then
install_output=$(opkg install "$app_id" 2>&1)
local ret=$?
if [ $ret -eq 0 ]; then
json_init
json_add_boolean "success" 1
json_add_string "message" "App installed successfully"
json_add_string "app_id" "$app_id"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "error" "Installation failed"
json_add_string "details" "$install_output"
json_dump
fi
elif command -v apk >/dev/null 2>&1; then
install_output=$(apk add "$app_id" 2>&1)
local ret=$?
if [ $ret -eq 0 ]; then
json_init
json_add_boolean "success" 1
json_add_string "message" "App installed successfully"
json_add_string "app_id" "$app_id"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "error" "Installation failed"
json_add_string "details" "$install_output"
json_dump
fi
else
json_init
json_add_boolean "success" 0
json_add_string "error" "No package manager found"
json_dump
fi
}
# Remove app from app store
remove_appstore_app() {
local input app_id
read input
json_load "$input"
json_get_var app_id app_id
json_cleanup
[ -z "$app_id" ] && {
json_init
json_add_boolean "success" 0
json_add_string "error" "app_id required"
json_dump
return
}
# Remove the package
local remove_output
if command -v opkg >/dev/null 2>&1; then
remove_output=$(opkg remove "$app_id" 2>&1)
local ret=$?
if [ $ret -eq 0 ]; then
json_init
json_add_boolean "success" 1
json_add_string "message" "App removed successfully"
json_add_string "app_id" "$app_id"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "error" "Removal failed"
json_add_string "details" "$remove_output"
json_dump
fi
elif command -v apk >/dev/null 2>&1; then
remove_output=$(apk del "$app_id" 2>&1)
local ret=$?
if [ $ret -eq 0 ]; then
json_init
json_add_boolean "success" 1
json_add_string "message" "App removed successfully"
json_add_string "app_id" "$app_id"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "error" "Removal failed"
json_add_string "details" "$remove_output"
json_dump
fi
else
json_init
json_add_boolean "success" 0
json_add_string "error" "No package manager found"
json_dump
fi
}
case "$1" in
list)
json_init
json_add_object "status"
json_close_object
json_add_object "modules"
json_close_object
json_add_object "modules_by_category"
json_add_string "category" "string"
json_close_object
json_add_object "module_info"
json_add_string "module" "string"
json_close_object
json_add_object "start_module"
json_add_string "module" "string"
json_close_object
json_add_object "stop_module"
json_add_string "module" "string"
json_close_object
json_add_object "restart_module"
json_add_string "module" "string"
json_close_object
json_add_object "enable_module"
json_add_string "module" "string"
json_close_object
json_add_object "disable_module"
json_add_string "module" "string"
json_close_object
json_add_object "check_module_enabled"
json_add_string "module" "string"
json_close_object
json_add_object "health"
json_close_object
json_add_object "diagnostics"
json_close_object
json_add_object "get_system_health"
json_close_object
json_add_object "get_alerts"
json_close_object
json_add_object "quick_action"
json_add_string "action" "string"
json_close_object
json_add_object "get_dashboard_data"
json_close_object
json_add_object "get_theme"
json_close_object
json_add_object "set_theme"
json_add_string "theme" "string"
json_close_object
json_add_object "dismiss_alert"
json_add_string "alert_id" "string"
json_close_object
json_add_object "clear_alerts"
json_close_object
json_add_object "fix_permissions"
json_close_object
json_add_object "first_run_status"
json_close_object
json_add_object "apply_first_run"
json_close_object
json_add_object "list_apps"
json_close_object
json_add_object "get_app_manifest"
json_add_string "app_id" "string"
json_close_object
json_add_object "apply_app_wizard"
json_add_string "app_id" "string"
json_close_object
json_add_object "list_profiles"
json_close_object
json_add_object "apply_profile"
json_add_string "profile_id" "string"
json_close_object
json_add_object "rollback_profile"
json_close_object
json_add_object "get_appstore_apps"
json_close_object
json_add_object "get_appstore_app"
json_add_string "app_id" "string"
json_close_object
json_add_object "install_appstore_app"
json_add_string "app_id" "string"
json_close_object
json_add_object "remove_appstore_app"
json_add_string "app_id" "string"
json_close_object
json_add_object "first_run_status"
json_close_object
json_add_object "apply_first_run"
json_close_object
json_add_object "list_apps"
json_close_object
json_add_object "get_app_manifest"
json_add_string "app_id" "string"
json_close_object
json_add_object "apply_app_wizard"
json_add_string "app_id" "string"
json_close_object
json_dump
;;
call)
case "$2" in
status)
get_status
;;
modules)
get_modules
;;
modules_by_category)
read -r input
json_load "$input"
json_get_var category category "security"
get_modules_by_category "$category"
;;
module_info)
read -r input
json_load "$input"
json_get_var module module ""
get_module_info "$module"
;;
start_module)
read -r input
json_load "$input"
json_get_var module module ""
start_module "$module"
;;
stop_module)
read -r input
json_load "$input"
json_get_var module module ""
stop_module "$module"
;;
restart_module)
read -r input
json_load "$input"
json_get_var module module ""
restart_module "$module"
;;
enable_module)
read -r input
json_load "$input"
json_get_var module module ""
enable_module "$module"
;;
disable_module)
read -r input
json_load "$input"
json_get_var module module ""
disable_module "$module"
;;
check_module_enabled)
read -r input
json_load "$input"
json_get_var module module ""
local enabled=$(check_module_enabled "$module")
json_init
json_add_boolean "enabled" "$enabled"
json_dump
;;
health)
get_health
;;
diagnostics)
get_diagnostics
;;
get_system_health)
get_system_health
;;
get_alerts)
get_alerts
;;
quick_action)
read -r input
json_load "$input"
json_get_var action action ""
quick_action "$action"
;;
get_dashboard_data)
get_dashboard_data
;;
get_theme)
get_theme
;;
set_theme)
set_theme
;;
dismiss_alert)
read -r input
json_load "$input"
json_get_var alert_id alert_id ""
dismiss_alert "$alert_id"
;;
clear_alerts)
clear_alerts
;;
fix_permissions)
fix_permissions
;;
first_run_status)
first_run_status
;;
apply_first_run)
apply_first_run
;;
list_apps)
list_apps
;;
get_app_manifest)
get_app_manifest
;;
apply_app_wizard)
apply_app_wizard
;;
list_profiles)
list_profiles
;;
apply_profile)
apply_profile
;;
rollback_profile)
rollback_profile
;;
get_appstore_apps)
get_appstore_apps
;;
get_appstore_app)
get_appstore_app
;;
install_appstore_app)
install_appstore_app
;;
remove_appstore_app)
remove_appstore_app
;;
*)
echo '{"error":"Unknown method"}'
;;
esac
;;
*)
echo '{"error":"Unknown command"}'
;;
esac