diff --git a/package/secubox/secubox-core/Makefile b/package/secubox/secubox-core/Makefile index acdcd01b..b230c5c9 100644 --- a/package/secubox/secubox-core/Makefile +++ b/package/secubox/secubox-core/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=secubox-core PKG_VERSION:=0.10.0 -PKG_RELEASE:=14 +PKG_RELEASE:=15 PKG_ARCH:=all PKG_LICENSE:=GPL-2.0 PKG_MAINTAINER:=SecuBox Team @@ -103,6 +103,10 @@ define Package/secubox-core/install $(INSTALL_DIR) $(1)/usr/lib/secubox $(INSTALL_DATA) ./root/usr/lib/secubox/tftp-mesh.sh $(1)/usr/lib/secubox/ + # RPCD method modules + $(INSTALL_DIR) $(1)/usr/lib/secubox/rpcd.d + $(INSTALL_DATA) ./root/usr/lib/secubox/rpcd.d/*.sh $(1)/usr/lib/secubox/rpcd.d/ + # WAN Access hotplug for interface events $(INSTALL_DIR) $(1)/etc/hotplug.d/iface $(INSTALL_BIN) ./root/etc/hotplug.d/iface/99-secubox-wan $(1)/etc/hotplug.d/iface/ diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/_common.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/_common.sh new file mode 100644 index 00000000..d94a73b7 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/_common.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# +# SecuBox RPCD Common Utilities +# Shared functions for all RPCD method modules +# + +# Standard JSON success response +json_success() { + local msg="${1:-Success}" + json_init + json_add_boolean "success" 1 + json_add_string "message" "$msg" + json_dump +} + +# Standard JSON error response +json_error() { + local msg="${1:-Error}" + local details="$2" + json_init + json_add_boolean "success" 0 + json_add_string "error" "$msg" + [ -n "$details" ] && json_add_string "details" "$details" + json_dump +} + +# Check if a service/process is running +# Usage: check_service_running +# Returns: 0 if running, 1 if not +check_service_running() { + local name="$1" + pgrep -f "$name" >/dev/null 2>&1 +} + +# Check if a port is listening +# Usage: check_port_listening +# Returns: 0 if listening, 1 if not +check_port_listening() { + local port="$1" + # Try netstat first (most common on OpenWrt) + if command -v netstat >/dev/null 2>&1; then + netstat -tln 2>/dev/null | grep -q ":${port} " + return $? + fi + # Fallback to /proc/net/tcp (always available) + # Convert port to hex + local hex_port=$(printf '%04X' "$port") + grep -qi ":${hex_port} " /proc/net/tcp 2>/dev/null +} + +# Read and parse JSON input from stdin +# Sets global variable: INPUT_JSON +read_input_json() { + INPUT_JSON="" + read -r INPUT_JSON +} + +# Get a value from INPUT_JSON +# Usage: get_input +get_input() { + local field="$1" + echo "$INPUT_JSON" | jsonfilter -e "@.$field" 2>/dev/null +} + +# Get boolean from INPUT_JSON (returns 1 or 0) +get_input_bool() { + local field="$1" + local val=$(get_input "$field") + case "$val" in + true|1|yes) echo 1 ;; + *) echo 0 ;; + esac +} + +# Get integer from INPUT_JSON with default +get_input_int() { + local field="$1" + local default="${2:-0}" + local val=$(get_input "$field") + [ -n "$val" ] && [ "$val" -eq "$val" ] 2>/dev/null && echo "$val" || echo "$default" +} + +# Check if init.d service exists and is enabled +# Usage: check_init_service +# Returns: running|stopped|disabled|not_installed +check_init_service() { + local svc="$1" + if [ ! -f "/etc/init.d/$svc" ]; then + echo "not_installed" + return + fi + if ! /etc/init.d/$svc enabled 2>/dev/null; then + echo "disabled" + return + fi + if /etc/init.d/$svc running 2>/dev/null; then + echo "running" + else + echo "stopped" + fi +} + +# Check LXC container status +# Usage: check_lxc_status +# Returns: running|stopped|not_installed +check_lxc_status() { + local name="$1" + if ! command -v lxc-info >/dev/null 2>&1; then + echo "not_installed" + return + fi + if lxc-info -n "$name" -s 2>/dev/null | grep -q "RUNNING"; then + echo "running" + elif lxc-info -n "$name" 2>/dev/null | grep -q "State"; then + echo "stopped" + else + echo "not_installed" + fi +} + +# Get current timestamp in ISO format +get_timestamp() { + date -Iseconds 2>/dev/null || date -u +%Y-%m-%dT%H:%M:%SZ +} + +# Get system uptime in seconds +get_uptime_seconds() { + cut -d. -f1 /proc/uptime +} + +# Get load average (1 5 15 minute) +get_load_avg() { + cut -d' ' -f1-3 /proc/loadavg +} + +# Get memory usage percentage +get_memory_percent() { + local mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo) + local mem_avail=$(awk '/MemAvailable/ {print $2}' /proc/meminfo) + mem_avail=${mem_avail:-0} + [ "$mem_total" -gt 0 ] && echo $(( (mem_total - mem_avail) * 100 / mem_total )) || echo 0 +} + +# Get disk usage percentage for root +get_disk_percent() { + df / 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%' +} + +# +# Method registration helpers +# Each module should call these in its list_methods_() function +# + +# Add a method with no parameters +add_method() { + local name="$1" + json_add_object "$name" + json_close_object +} + +# Add a method with string parameter +add_method_str() { + local name="$1" + local param="$2" + json_add_object "$name" + json_add_string "$param" "string" + json_close_object +} + +# Add a method with multiple string parameters +add_method_strs() { + local name="$1" + shift + json_add_object "$name" + for param in "$@"; do + json_add_string "$param" "string" + done + json_close_object +} + +# Add a method with boolean parameter +add_method_bool() { + local name="$1" + local param="$2" + json_add_object "$name" + json_add_boolean "$param" "boolean" + json_close_object +} + +# Add a method with integer parameter +add_method_int() { + local name="$1" + local param="$2" + json_add_object "$name" + json_add_int "$param" "integer" + json_close_object +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/appstore.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/appstore.sh new file mode 100644 index 00000000..97f1589f --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/appstore.sh @@ -0,0 +1,399 @@ +#!/bin/sh +# +# SecuBox RPCD - AppStore Operations +# Catalog, apps, versions, widgets +# + +# Register methods +list_methods_appstore() { + add_method "get_appstore_apps" + add_method "list_apps" + add_method_str "get_appstore_app" "app_id" + add_method_str "install_appstore_app" "app_id" + add_method_str "remove_appstore_app" "app_id" + add_method "get_catalog_sources" + add_method_str "set_catalog_source" "source" + add_method_str "sync_catalog" "source" + add_method "check_updates" + add_method_str "get_app_versions" "app_id" + json_add_object "get_changelog" + json_add_string "app_id" "string" + json_add_string "from_version" "string" + json_add_string "to_version" "string" + json_close_object + add_method_str "get_widget_data" "app_id" + add_method_str "get_app_manifest" "app_id" +} + +# Handle method calls +handle_appstore() { + local method="$1" + case "$method" in + get_appstore_apps) + _do_get_appstore_apps + ;; + list_apps) + _do_list_apps + ;; + get_appstore_app) + read_input_json + local app_id=$(get_input "app_id") + _do_get_appstore_app "$app_id" + ;; + install_appstore_app) + read_input_json + local app_id=$(get_input "app_id") + if /usr/sbin/secubox-appstore install "$app_id" >/dev/null 2>&1; then + json_success "App installed successfully" + else + json_error "Installation failed" "Check system logs for more information" + fi + ;; + remove_appstore_app) + read_input_json + local app_id=$(get_input "app_id") + if /usr/sbin/secubox-appstore remove "$app_id" >/dev/null 2>&1; then + json_success "App removed successfully" + else + json_error "Removal failed" "Check system logs for more information" + fi + ;; + get_catalog_sources) + _do_get_catalog_sources + ;; + set_catalog_source) + read_input_json + local source=$(get_input "source") + _do_set_catalog_source "$source" + ;; + sync_catalog) + read_input_json + local source=$(get_input "source") + if /usr/sbin/secubox-appstore sync ${source:+"$source"} 2>&1; then + json_init + json_add_boolean "success" 1 + json_add_string "message" "Catalog synced successfully" + [ -n "$source" ] && json_add_string "source" "$source" + json_dump + else + json_error "Sync failed" + fi + ;; + check_updates) + /usr/sbin/secubox-appstore check-updates --json + ;; + get_app_versions) + read_input_json + local app_id=$(get_input "app_id") + _do_get_app_versions "$app_id" + ;; + get_changelog) + read_input_json + local app_id=$(get_input "app_id") + local from_version=$(get_input "from_version") + local to_version=$(get_input "to_version") + /usr/sbin/secubox-appstore changelog "$app_id" ${from_version:+"$from_version"} ${to_version:+"$to_version"} + ;; + get_widget_data) + read_input_json + local app_id=$(get_input "app_id") + _do_get_widget_data "$app_id" + ;; + get_app_manifest) + read_input_json + local app_id=$(get_input "app_id") + local manifest="/usr/share/secubox/plugins/$app_id/manifest.json" + if [ -f "$manifest" ]; then + cat "$manifest" + else + json_error "Manifest not found" "$app_id" + fi + ;; + *) + return 1 + ;; + esac +} + +# Get apps from catalog with installation status +_do_get_appstore_apps() { + local CATALOG_FILE="/usr/share/secubox/catalog.json" + + if [ -f "$CATALOG_FILE" ]; then + local MODULES_JSON=$(/usr/sbin/secubox-appstore list --json 2>/dev/null) + + if [ -n "$MODULES_JSON" ]; then + jq --argjson modules "$MODULES_JSON" ' + { + apps: [.plugins[] | . as $app | + ($modules.modules // [] | map(select(.id == $app.id or .name == $app.id)) | first) as $mod | + $app + { + installed: (if $mod then ($mod.installed // false) else false end), + enabled: (if $mod then ($mod.enabled // false) else false end), + status: (if $mod then ($mod.status // "unknown") else "not_installed" end) + } + ], + categories: .categories + } + ' "$CATALOG_FILE" + else + jq '{apps: .plugins, categories: .categories}' "$CATALOG_FILE" + fi + else + echo '{"apps":[],"categories":{}}' + fi +} + +# List apps with wizard detection +_do_list_apps() { + local CATALOG_FILE="/usr/share/secubox/catalog.json" + local PLUGINS_DIR="/usr/share/secubox/plugins" + local APPS_JSON='[]' + + if [ -f "$CATALOG_FILE" ]; then + APPS_JSON=$(jq '.plugins' "$CATALOG_FILE") + fi + + for plugin_dir in "$PLUGINS_DIR"/*; do + [ -d "$plugin_dir" ] || continue + local manifest="$plugin_dir/manifest.json" + [ -f "$manifest" ] || continue + + local app_id=$(jq -r '.id // empty' "$manifest" 2>/dev/null) + [ -n "$app_id" ] || continue + + local has_wizard=$(jq -e '.wizard.fields | length > 0' "$manifest" >/dev/null 2>&1 && echo "true" || echo "false") + + if [ "$has_wizard" = "true" ]; then + local app_exists=$(echo "$APPS_JSON" | jq --arg id "$app_id" 'map(select(.id == $id)) | length') + if [ "$app_exists" -gt 0 ]; then + APPS_JSON=$(echo "$APPS_JSON" | jq --arg id "$app_id" \ + 'map(if .id == $id then . + {has_wizard: true} else . end)') + else + local app_data=$(jq '{ + id: .id, + name: .name, + description: .description, + version: .version, + icon: "box", + has_wizard: true, + state: "available" + }' "$manifest") + APPS_JSON=$(echo "$APPS_JSON" | jq --argjson app "$app_data" '. + [$app]') + fi + fi + done + + if [ -f "$CATALOG_FILE" ]; then + jq -n --argjson apps "$APPS_JSON" --argjson cats "$(jq '.categories // {}' "$CATALOG_FILE")" \ + '{apps: $apps, categories: $cats}' + else + jq -n --argjson apps "$APPS_JSON" '{apps: $apps, categories: {}}' + fi +} + +# Get single app details +_do_get_appstore_app() { + local app_id="$1" + local CATALOG_DIR="/usr/share/secubox/plugins/catalog" + local CATALOG_FILE="$CATALOG_DIR/${app_id}.json" + + if [ -f "$CATALOG_FILE" ]; then + json_init + cat "$CATALOG_FILE" | jsonfilter -e '@' + local pkg=$(jsonfilter -i "$CATALOG_FILE" -e '@.packages.required[0]') + if [ -n "$pkg" ] && opkg list-installed | grep -q "^$pkg "; then + echo ',"installed":true' + else + echo ',"installed":false' + fi + else + json_error "App not found" "$app_id" + fi +} + +# Get catalog sources +_do_get_catalog_sources() { + local CONFIG_NAME="secubox-appstore" + local METADATA_FILE="/var/lib/secubox/catalog-metadata.json" + + _add_default_src() { + local name="$1" type="$2" url="$3" path="$4" priority="$5" + json_add_object "" + json_add_string "name" "$name" + json_add_boolean "enabled" 1 + json_add_string "type" "$type" + [ -n "$url" ] && json_add_string "url" "$url" + [ -n "$path" ] && json_add_string "path" "$path" + json_add_int "priority" "$priority" + [ "$name" = "embedded" ] && json_add_boolean "active" 1 || json_add_boolean "active" 0 + json_add_string "status" "default" + json_add_string "last_success" "" + json_close_object + } + + if [ ! -f "/etc/config/$CONFIG_NAME" ]; then + json_init + json_add_array "sources" + _add_default_src "github" "remote" "https://raw.githubusercontent.com/CyberMind-FR/secubox-openwrt/refs/heads/master/package/secubox/secubox-core/root/usr/share/secubox/catalog.json" "" 1 + _add_default_src "embedded" "embedded" "" "/usr/share/secubox/catalog.json" 999 + json_close_array + json_add_boolean "defaults" true + json_add_string "message" "Catalog config missing, using built-in defaults" + json_dump + return + fi + + json_init + json_add_array "sources" + + . /lib/functions.sh + config_load "$CONFIG_NAME" + + local active_source="" + [ -f "$METADATA_FILE" ] && active_source=$(jsonfilter -i "$METADATA_FILE" -e '@.active_source' 2>/dev/null || echo "") + + local metadata_content="" + [ -f "$METADATA_FILE" ] && metadata_content=$(cat "$METADATA_FILE" 2>/dev/null || echo "{}") + + local sources_count=0 + + _add_src_info() { + local section="$1" + local enabled type url path priority + config_get_bool enabled "$section" enabled 0 + config_get type "$section" type + config_get url "$section" url + config_get path "$section" path + config_get priority "$section" priority 999 + + json_add_object "" + json_add_string "name" "$section" + json_add_boolean "enabled" "$enabled" + json_add_string "type" "$type" + [ -n "$url" ] && json_add_string "url" "$url" + [ -n "$path" ] && json_add_string "path" "$path" + json_add_int "priority" "$priority" + json_add_boolean "active" "$([ "$section" = "$active_source" ] && echo 1 || echo 0)" + + if [ -n "$metadata_content" ]; then + local status=$(echo "$metadata_content" | jsonfilter -e "@.sources['$section'].status" 2>/dev/null || echo "") + local last_success=$(echo "$metadata_content" | jsonfilter -e "@.sources['$section'].last_success" 2>/dev/null || echo "") + [ -n "$status" ] && json_add_string "status" "$status" + [ -n "$last_success" ] && json_add_string "last_success" "$last_success" + fi + json_close_object + sources_count=$((sources_count + 1)) + } + + config_foreach _add_src_info source + + if [ "$sources_count" -eq 0 ]; then + _add_default_src "github" "remote" "https://raw.githubusercontent.com/CyberMind-FR/secubox-openwrt/refs/heads/master/package/secubox/secubox-core/root/usr/share/secubox/catalog.json" "" 1 + _add_default_src "embedded" "embedded" "" "/usr/share/secubox/catalog.json" 999 + json_close_array + json_add_boolean "defaults" true + json_add_string "message" "Catalog config empty, using built-in defaults" + else + json_close_array + fi + + json_dump +} + +# Set catalog source +_do_set_catalog_source() { + local source="$1" + local CONFIG_NAME="secubox-appstore" + local SECTION_NAME=$(uci -q show "$CONFIG_NAME" | grep "=settings" | head -n1 | cut -d'.' -f2 | cut -d'=' -f1) + [ -z "$SECTION_NAME" ] && SECTION_NAME="main" + + if [ -n "$source" ]; then + if uci set "${CONFIG_NAME}.${SECTION_NAME}.force_source=$source" >/dev/null 2>&1 && \ + uci commit "$CONFIG_NAME" >/dev/null 2>&1; then + json_init + json_add_boolean "success" 1 + json_add_string "message" "Catalog source set to: $source" + json_dump + else + json_error "Failed to update UCI config" + fi + else + json_error "No source specified" + fi +} + +# Get app versions +_do_get_app_versions() { + local app_id="$1" + local CATALOG_FILE="/usr/share/secubox/catalog.json" + local METADATA_FILE="/var/lib/secubox/catalog-metadata.json" + + json_init + + if [ -f "$CATALOG_FILE" ]; then + local pkg_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].pkg_version" 2>/dev/null) + local app_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].app_version" 2>/dev/null) + [ -n "$pkg_version" ] && json_add_string "catalog_pkg_version" "$pkg_version" + [ -n "$app_version" ] && json_add_string "catalog_app_version" "$app_version" + fi + + local pkg_name=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].packages.required[0]" 2>/dev/null) + if [ -n "$pkg_name" ]; then + local installed_version=$(opkg list-installed | grep "^$pkg_name " | awk '{print $3}') + [ -n "$installed_version" ] && json_add_string "installed_version" "$installed_version" + fi + + if [ -f "$METADATA_FILE" ]; then + local update_available=$(jsonfilter -i "$METADATA_FILE" -e "@.installed_apps['$app_id'].update_available" 2>/dev/null) + [ -n "$update_available" ] && json_add_boolean "update_available" "$update_available" + fi + + json_add_string "app_id" "$app_id" + json_dump +} + +# Get widget data +_do_get_widget_data() { + local app_id="$1" + local CATALOG_FILE="/usr/share/secubox/catalog.json" + + json_init + json_add_string "app_id" "$app_id" + json_add_int "timestamp" "$(date +%s)" + + if [ -f "$CATALOG_FILE" ]; then + local widget_enabled=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].widget.enabled" 2>/dev/null) + + if [ "$widget_enabled" = "true" ]; then + json_add_boolean "widget_enabled" true + + local catalog_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].version" 2>/dev/null) + local pkg_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].pkg_version" 2>/dev/null) + [ -n "$catalog_version" ] && json_add_string "catalog_version" "$catalog_version" + [ -n "$pkg_version" ] && json_add_string "pkg_version" "$pkg_version" + + local installed_version="" + if [ -n "$pkg_version" ]; then + local package_name=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].packages.required[0]" 2>/dev/null) + if [ -n "$package_name" ]; then + installed_version=$(opkg info "$package_name" 2>/dev/null | awk '/^Version:/ {print $2}') + fi + fi + [ -n "$installed_version" ] && json_add_string "installed_version" "$installed_version" + + json_add_boolean "installed" false + json_add_boolean "running" false + json_add_string "status" "unknown" + + json_add_array "metrics" + json_close_array + else + json_add_boolean "widget_enabled" false + fi + else + json_add_boolean "widget_enabled" false + fi + + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/core.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/core.sh new file mode 100644 index 00000000..738d6d7e --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/core.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# +# SecuBox RPCD - Core Methods +# Status, version, reload +# + +# Register methods +list_methods_core() { + add_method "getStatus" + add_method "getVersion" + add_method "reload" +} + +# Handle method calls +handle_core() { + local method="$1" + case "$method" in + getStatus) + /usr/sbin/secubox-core status + ;; + getVersion) + json_init + json_add_string "version" "0.8.0" + json_add_string "core" "secubox-core" + json_add_string "build_date" "$(date -u +%Y-%m-%d)" + json_dump + ;; + reload) + /usr/sbin/secubox-core reload + json_init + json_add_boolean "success" 1 + json_dump + ;; + *) + return 1 + ;; + esac +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/dashboard.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/dashboard.sh new file mode 100644 index 00000000..e38f51de --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/dashboard.sh @@ -0,0 +1,219 @@ +#!/bin/sh +# +# SecuBox RPCD - Dashboard Data +# Dashboard summary, public IPs, quick actions, logs +# + +# Register methods +list_methods_dashboard() { + add_method "get_dashboard_data" + add_method "get_public_ips" + add_method "refresh_public_ips" + add_method_str "quick_action" "action" + json_add_object "getLogs" + json_add_string "service" "string" + json_add_int "lines" "integer" + json_close_object +} + +# Handle method calls +handle_dashboard() { + local method="$1" + case "$method" in + get_dashboard_data) + _do_dashboard_data + ;; + get_public_ips) + _do_public_ips + ;; + refresh_public_ips) + _do_refresh_public_ips + ;; + quick_action) + read_input_json + local action=$(get_input "action") + _do_quick_action "$action" + ;; + getLogs) + read_input_json + local service=$(get_input "service") + local lines=$(get_input_int "lines" 100) + _do_get_logs "$service" "$lines" + ;; + *) + return 1 + ;; + esac +} + +# Dashboard summary data (optimized - no slow appstore call) +_do_dashboard_data() { + json_init + + # Fast module counting + local total_modules=0 + local running_modules=0 + local CATALOG_FILE="/usr/share/secubox/catalog.json" + if [ -f "$CATALOG_FILE" ]; then + total_modules=$(jsonfilter -i "$CATALOG_FILE" -e '@.plugins[*].id' 2>/dev/null | wc -l) + fi + [ -z "$total_modules" ] || [ "$total_modules" -eq 0 ] && total_modules=0 + + # Count running LXC containers + local lxc_running=$(lxc-ls --running 2>/dev/null | wc -w) + lxc_running=${lxc_running:-0} + + # Count running init services that are SecuBox-related + local svc_running=0 + for svc in crowdsec tor haproxy netifyd syslog-ng; do + if pgrep -f "$svc" >/dev/null 2>&1; then + svc_running=$((svc_running + 1)) + fi + done + + running_modules=$((lxc_running + svc_running)) + + # Get system info + local uptime_seconds=$(get_uptime_seconds) + local load_avg=$(get_load_avg) + + json_add_object "status" + json_add_string "version" "0.8.0" + json_add_int "uptime" "$uptime_seconds" + json_add_string "load" "$load_avg" + json_close_object + + json_add_object "counts" + json_add_int "total" "$total_modules" + json_add_int "running" "$running_modules" + json_add_int "lxc_running" "$lxc_running" + json_add_int "services_running" "$svc_running" + json_close_object + + json_dump +} + +# Public IPs (cached) +_do_public_ips() { + json_init + + local cache_v4="/tmp/secubox_public_ipv4" + local cache_v6="/tmp/secubox_public_ipv6" + local ipv4="" ipv6="" + + [ -f "$cache_v4" ] && ipv4=$(cat "$cache_v4" 2>/dev/null) + [ -f "$cache_v6" ] && ipv6=$(cat "$cache_v6" 2>/dev/null) + + # Fallback: get from interface + if [ -z "$ipv4" ]; then + ipv4=$(ip -4 addr show scope global 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) + fi + if [ -z "$ipv6" ]; then + ipv6=$(ip -6 addr show scope global 2>/dev/null | grep -oE '[0-9a-f:]+::[0-9a-f:]+' | head -1) + fi + + json_add_string "ipv4" "${ipv4:-N/A}" + json_add_string "ipv6" "${ipv6:-N/A}" + json_add_boolean "ipv4_available" "$([ -n "$ipv4" ] && [ "$ipv4" != "N/A" ] && echo 1 || echo 0)" + json_add_boolean "ipv6_available" "$([ -n "$ipv6" ] && [ "$ipv6" != "N/A" ] && echo 1 || echo 0)" + + json_dump +} + +# Refresh public IPs in background +_do_refresh_public_ips() { + json_init + ( + for svc in "https://api.ipify.org" "https://ipv4.icanhazip.com"; do + local ipv4=$(curl -4 -s --connect-timeout 2 --max-time 3 "$svc" 2>/dev/null | tr -d '\n') + [ -n "$ipv4" ] && { echo "$ipv4" > /tmp/secubox_public_ipv4; break; } + done + for svc in "https://api64.ipify.org" "https://ipv6.icanhazip.com"; do + local ipv6=$(curl -6 -s --connect-timeout 2 --max-time 3 "$svc" 2>/dev/null | tr -d '\n') + [ -n "$ipv6" ] && { echo "$ipv6" > /tmp/secubox_public_ipv6; break; } + done + ) & + json_add_boolean "success" 1 + json_dump +} + +# Quick actions +_do_quick_action() { + local action="$1" + json_init + + case "$action" in + restart_services) + for svc in haproxy crowdsec tor netifyd; do + if [ -x "/etc/init.d/$svc" ]; then + /etc/init.d/$svc restart 2>/dev/null & + elif [ -x "/usr/sbin/${svc}ctl" ]; then + /usr/sbin/${svc}ctl restart 2>/dev/null & + fi + done + json_add_boolean "success" 1 + json_add_string "message" "Services restart initiated" + ;; + restart_*) + local svc_name="${action#restart_}" + if [ -x "/etc/init.d/$svc_name" ]; then + /etc/init.d/$svc_name restart 2>/dev/null + json_add_boolean "success" 1 + json_add_string "message" "Service $svc_name restarted" + elif [ -x "/usr/sbin/${svc_name}ctl" ]; then + /usr/sbin/${svc_name}ctl restart 2>/dev/null + json_add_boolean "success" 1 + json_add_string "message" "Service $svc_name restarted" + else + json_add_boolean "success" 0 + json_add_string "message" "Service $svc_name not found" + fi + ;; + update_packages) + opkg update 2>/dev/null & + json_add_boolean "success" 1 + json_add_string "message" "Package update initiated" + ;; + view_logs) + json_add_boolean "success" 1 + json_add_string "redirect" "/cgi-bin/luci/admin/status/syslog" + ;; + export_config) + if [ -x "/usr/sbin/secubox-recovery" ]; then + local snapshot_name="export-$(date +%Y%m%d-%H%M%S)" + /usr/sbin/secubox-recovery snapshot "$snapshot_name" 2>/dev/null + json_add_boolean "success" 1 + json_add_string "message" "Configuration exported as $snapshot_name" + else + json_add_boolean "success" 0 + json_add_string "message" "Recovery system not available" + fi + ;; + *) + json_add_boolean "success" 0 + json_add_string "message" "Unknown action: $action" + ;; + esac + + json_dump +} + +# Get logs +_do_get_logs() { + local service="$1" + local lines="$2" + + json_init + json_add_array "logs" + if [ -n "$service" ]; then + logread -e "$service" | tail -n "$lines" | while read -r line; do + json_add_string "" "$line" + done + else + logread | tail -n "$lines" | while read -r line; do + json_add_string "" "$line" + done + fi + json_close_array + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/feedback.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/feedback.sh new file mode 100644 index 00000000..5d9dd513 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/feedback.sh @@ -0,0 +1,158 @@ +#!/bin/sh +# +# SecuBox RPCD - Feedback & Issue Management +# Report, resolve, search issues +# + +# Register methods +list_methods_feedback() { + json_add_object "report_issue" + json_add_string "app_id" "string" + json_add_string "type" "string" + json_add_string "summary" "string" + json_add_string "details" "string" + json_close_object + json_add_object "resolve_issue" + json_add_string "issue_id" "string" + json_add_string "description" "string" + json_close_object + add_method_str "search_resolutions" "keyword" + add_method_str "list_issues" "filter" +} + +# Handle method calls +handle_feedback() { + local method="$1" + case "$method" in + report_issue) + read_input_json + local app_id=$(get_input "app_id") + local type=$(get_input "type") + local summary=$(get_input "summary") + local details=$(get_input "details") + local result=$(/usr/sbin/secubox-feedback report "$app_id" --type "${type:-bug}" --summary "$summary" --details "$details" 2>&1) + if [ $? -eq 0 ]; then + local issue_num=$(echo "$result" | grep -oE 'Issue #[0-9]+' | grep -oE '[0-9]+') + json_init + json_add_boolean "success" 1 + json_add_string "message" "Issue reported" + [ -n "$issue_num" ] && json_add_int "issue_number" "$issue_num" + json_dump + else + json_error "$result" + fi + ;; + resolve_issue) + read_input_json + local issue_id=$(get_input "issue_id") + local description=$(get_input "description") + local result=$(/usr/sbin/secubox-feedback resolve "$issue_id" --description "$description" 2>&1) + if [ $? -eq 0 ]; then + json_success "Resolution added" + else + json_error "$result" + fi + ;; + search_resolutions) + read_input_json + local keyword=$(get_input "keyword") + _do_search_resolutions "$keyword" + ;; + list_issues) + read_input_json + local filter=$(get_input "filter") + _do_list_issues "$filter" + ;; + *) + return 1 + ;; + esac +} + +# Search resolutions +_do_search_resolutions() { + local keyword="$1" + local RESOLUTIONS_FILE="/var/lib/secubox/feedback/resolutions.json" + + json_init + json_add_string "keyword" "$keyword" + json_add_array "results" + + if [ -f "$RESOLUTIONS_FILE" ]; then + local idx=0 + while true; do + local res_id=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].id" 2>/dev/null) + [ -z "$res_id" ] && break + + local description=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].description" 2>/dev/null) + local issue_summary=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].issue_summary" 2>/dev/null) + local app_id=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].app_id" 2>/dev/null) + + if echo "$description $issue_summary $app_id" | grep -qi "$keyword"; then + local issue_num=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].issue_number" 2>/dev/null) + local upvotes=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].upvotes" 2>/dev/null) + local verified=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].verified" 2>/dev/null) + + json_add_object "" + json_add_string "id" "$res_id" + json_add_int "issue_number" "$issue_num" + json_add_string "app_id" "$app_id" + json_add_string "issue_summary" "$issue_summary" + json_add_string "description" "$description" + json_add_int "upvotes" "${upvotes:-0}" + json_add_boolean "verified" "$([ "$verified" = "true" ] && echo 1 || echo 0)" + json_close_object + fi + + idx=$((idx + 1)) + done + fi + + json_close_array + json_dump +} + +# List issues +_do_list_issues() { + local filter="$1" + local ISSUES_FILE="/var/lib/secubox/feedback/issues.json" + + json_init + json_add_array "issues" + + if [ -f "$ISSUES_FILE" ]; then + local idx=0 + while true; do + local issue_id=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].id" 2>/dev/null) + [ -z "$issue_id" ] && break + + local status=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].status" 2>/dev/null) + + if [ -n "$filter" ] && [ "$filter" != "all" ] && [ "$filter" != "$status" ]; then + idx=$((idx + 1)) + continue + fi + + local issue_num=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].number" 2>/dev/null) + local app_id=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].app_id" 2>/dev/null) + local summary=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].summary" 2>/dev/null) + local issue_type=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].type" 2>/dev/null) + local created=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].created_at" 2>/dev/null) + + json_add_object "" + json_add_string "id" "$issue_id" + json_add_int "number" "$issue_num" + json_add_string "app_id" "$app_id" + json_add_string "summary" "$summary" + json_add_string "type" "$issue_type" + json_add_string "status" "$status" + json_add_string "created_at" "$created" + json_close_object + + idx=$((idx + 1)) + done + fi + + json_close_array + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/feeds.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/feeds.sh new file mode 100644 index 00000000..390d3221 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/feeds.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# +# SecuBox RPCD - Feed Management +# List, add, remove, share, import feeds +# + +# Register methods +list_methods_feeds() { + add_method "list_feeds" + json_add_object "add_feed" + json_add_string "name" "string" + json_add_string "url" "string" + json_add_string "feed_type" "string" + json_add_string "description" "string" + json_close_object + add_method_str "remove_feed" "name" + add_method_str "share_feed" "name" + add_method_str "import_feed" "url" +} + +# Handle method calls +handle_feeds() { + local method="$1" + case "$method" in + list_feeds) + /usr/sbin/secubox-feed-manager list --json + ;; + add_feed) + read_input_json + local name=$(get_input "name") + local url=$(get_input "url") + local feed_type=$(get_input "feed_type") + local description=$(get_input "description") + local result=$(/usr/sbin/secubox-feed-manager add "$name" "$url" --type "${feed_type:-unpublished}" --description "$description" 2>&1) + if [ $? -eq 0 ]; then + json_init + json_add_boolean "success" 1 + json_add_string "message" "Feed added successfully" + json_add_string "name" "$name" + json_dump + else + json_error "$result" + fi + ;; + remove_feed) + read_input_json + local name=$(get_input "name") + local result=$(/usr/sbin/secubox-feed-manager remove "$name" 2>&1) + if [ $? -eq 0 ]; then + json_success "Feed removed" + else + json_error "$result" + fi + ;; + share_feed) + read_input_json + local name=$(get_input "name") + /usr/sbin/secubox-feed-manager share "$name" + ;; + import_feed) + read_input_json + local url=$(get_input "url") + local result=$(/usr/sbin/secubox-feed-manager import "$url" 2>&1) + if [ $? -eq 0 ]; then + json_success "Feed imported" + else + json_error "$result" + fi + ;; + *) + return 1 + ;; + esac +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/health.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/health.sh new file mode 100644 index 00000000..3b0c2673 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/health.sh @@ -0,0 +1,432 @@ +#!/bin/sh +# +# SecuBox RPCD - Health & Diagnostics +# Network health, vital services, full health report, alerts +# + +# Register methods +list_methods_health() { + json_add_object "runDiagnostics" + json_add_string "target" "string" + json_close_object + add_method "getHealth" + add_method "get_network_health" + add_method "get_vital_services" + add_method "get_full_health_report" + add_method "get_system_health" + add_method "get_alerts" +} + +# Handle method calls +handle_health() { + local method="$1" + case "$method" in + runDiagnostics) + read_input_json + local target=$(get_input "target") + /usr/sbin/secubox-diagnostics run "${target:-all}" + ;; + getHealth) + /usr/sbin/secubox-core health + ;; + get_network_health) + _do_network_health + ;; + get_vital_services) + _do_vital_services + ;; + get_full_health_report) + _do_full_health_report + ;; + get_system_health) + _do_system_health + ;; + get_alerts) + _do_get_alerts + ;; + *) + return 1 + ;; + esac +} + +# Network health monitoring - detects CRC errors, link flapping +_do_network_health() { + local DMESG_LINES=500 + local FLAP_THRESHOLD=5 + local CRC_THRESHOLD=10 + + json_init + json_add_string "timestamp" "$(get_timestamp)" + json_add_object "interfaces" + + local overall="healthy" + local critical_count=0 + local warning_count=0 + + for iface_path in /sys/class/net/eth* /sys/class/net/wan* /sys/class/net/lan*; do + [ -d "$iface_path" ] || continue + [ -d "$iface_path/device" ] || continue + local iface=$(basename "$iface_path") + + local current_state=$(cat "$iface_path/operstate" 2>/dev/null || echo "unknown") + local crc_count=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface.*crc error" 2>/dev/null) + crc_count=${crc_count:-0} + local link_up=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Up" 2>/dev/null) + link_up=${link_up:-0} + local link_down=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Down" 2>/dev/null) + link_down=${link_down:-0} + local link_changes=$((link_up + link_down)) + + local status="ok" + local issues="" + + if [ "$crc_count" -ge "$CRC_THRESHOLD" ]; then + status="critical" + issues="CRC errors ($crc_count)" + critical_count=$((critical_count + 1)) + fi + + if [ "$link_changes" -ge "$FLAP_THRESHOLD" ]; then + [ "$status" = "ok" ] && status="warning" + [ -n "$issues" ] && issues="$issues; " + issues="${issues}Link flapping ($link_changes changes)" + warning_count=$((warning_count + 1)) + fi + + local rx_errors=$(cat "$iface_path/statistics/rx_errors" 2>/dev/null || echo 0) + local tx_errors=$(cat "$iface_path/statistics/tx_errors" 2>/dev/null || echo 0) + + json_add_object "$iface" + json_add_string "status" "$status" + json_add_string "state" "$current_state" + json_add_int "crc_errors" "$crc_count" + json_add_int "link_changes" "$link_changes" + json_add_int "rx_errors" "$rx_errors" + json_add_int "tx_errors" "$tx_errors" + json_add_string "issues" "$issues" + json_close_object + done + + json_close_object + + if [ "$critical_count" -gt 0 ]; then + overall="critical" + elif [ "$warning_count" -gt 0 ]; then + overall="warning" + fi + + json_add_string "overall" "$overall" + json_add_int "critical_interfaces" "$critical_count" + json_add_int "warning_interfaces" "$warning_count" + + if [ "$overall" != "healthy" ]; then + json_add_array "recommendations" + [ "$critical_count" -gt 0 ] && json_add_string "" "Check/replace Ethernet cables" + [ "$critical_count" -gt 0 ] && json_add_string "" "Try different port on switch/modem" + [ "$warning_count" -gt 0 ] && json_add_string "" "Monitor link stability" + json_close_array + fi + + json_dump +} + +# Vital services monitoring +_do_vital_services() { + json_init + json_add_string "timestamp" "$(get_timestamp)" + + _check_svc() { + local name="$1" category="$2" check_type="$3" check_value="$4" description="$5" critical="$6" + local status="unknown" details="" + + case "$check_type" in + process) + if pgrep -f "$check_value" >/dev/null 2>&1; then + status="running" + else + status="stopped" + fi + ;; + port) + if check_port_listening "$check_value"; then + status="running" + details="Port $check_value listening" + else + status="stopped" + details="Port $check_value not listening" + fi + ;; + init) + status=$(check_init_service "$check_value") + ;; + lxc) + status=$(check_lxc_status "$check_value") + ;; + esac + + json_add_object "" + json_add_string "name" "$name" + json_add_string "category" "$category" + json_add_string "status" "$status" + json_add_string "description" "$description" + json_add_boolean "critical" "${critical:-0}" + [ -n "$details" ] && json_add_string "details" "$details" + json_close_object + } + + # Core Infrastructure Services + json_add_array "core" + _check_svc "SSH" "remote" "port" "22" "Remote shell access" 1 + _check_svc "HTTPS Admin" "remote" "port" "8444" "LuCI admin interface" 1 + _check_svc "DNS" "network" "port" "53" "Domain name resolution" 1 + _check_svc "DHCP" "network" "process" "dnsmasq" "IP address assignment" 1 + _check_svc "Firewall" "security" "process" "fw4" "Network firewall" 1 + json_close_array + + # Security Services + json_add_array "security" + _check_svc "CrowdSec" "security" "process" "crowdsec" "Intrusion prevention" 1 + _check_svc "CrowdSec Bouncer" "security" "process" "crowdsec-firewall-bouncer" "Firewall bouncer" 1 + _check_svc "Tor" "privacy" "init" "tor" "Anonymous routing" 0 + json_close_array + + # Web Publishing Services + json_add_array "publishers" + _check_svc "HAProxy" "proxy" "lxc" "haproxy" "Load balancer & reverse proxy" 1 + _check_svc "HexoJS" "cms" "lxc" "hexojs" "Static blog generator" 0 + _check_svc "Gitea" "devops" "lxc" "gitea" "Git repository hosting" 0 + _check_svc "Streamlit" "app" "lxc" "streamlit" "Python web apps" 0 + json_close_array + + # Media & App Services + json_add_array "apps" + _check_svc "Lyrion" "media" "lxc" "lyrion" "Music streaming server" 0 + _check_svc "MagicMirror" "display" "lxc" "magicmirror2" "Smart mirror display" 0 + _check_svc "PicoBrew" "app" "lxc" "picobrew" "Brewing automation" 0 + json_close_array + + # Monitoring Services + json_add_array "monitoring" + _check_svc "Netifyd" "monitoring" "process" "netifyd" "Network intelligence" 0 + _check_svc "Syslog-ng" "logging" "process" "syslog-ng" "System logging" 1 + json_close_array + + # Calculate summary + json_add_object "summary" + local total=0 + for svc in /etc/init.d/*; do + [ -x "$svc" ] || continue + total=$((total + 1)) + done + local lxc_running=$(lxc-ls --running 2>/dev/null | wc -w) + local lxc_total=$(lxc-ls 2>/dev/null | wc -w) + json_add_int "init_services" "$total" + json_add_int "lxc_running" "${lxc_running:-0}" + json_add_int "lxc_total" "${lxc_total:-0}" + json_close_object + + json_dump +} + +# Full health report +_do_full_health_report() { + json_init + json_add_string "timestamp" "$(get_timestamp)" + json_add_string "hostname" "$(uci get system.@system[0].hostname 2>/dev/null || hostname)" + + # System info + json_add_object "system" + json_add_int "uptime" "$(get_uptime_seconds)" + json_add_string "load" "$(get_load_avg)" + local mem_pct=$(get_memory_percent) + json_add_int "memory_percent" "$mem_pct" + local disk_pct=$(get_disk_percent) + json_add_int "disk_percent" "${disk_pct:-0}" + json_close_object + + # Network Health Summary + json_add_object "network" + local net_overall="healthy" + local net_issues=0 + for iface_path in /sys/class/net/eth* /sys/class/net/wan*; do + [ -d "$iface_path" ] || continue + [ -d "$iface_path/device" ] || continue + local iface=$(basename "$iface_path") + local crc=$(dmesg | tail -n 500 | grep -c "$iface.*crc error" 2>/dev/null) + crc=${crc:-0} + local flap=$(dmesg | tail -n 500 | grep -c "$iface: Link is" 2>/dev/null) + flap=${flap:-0} + if [ "$crc" -ge 10 ] || [ "$flap" -ge 10 ]; then + net_overall="critical" + net_issues=$((net_issues + 1)) + json_add_object "$iface" + json_add_string "status" "critical" + json_add_int "crc_errors" "$crc" + json_add_int "link_changes" "$flap" + json_close_object + fi + done + json_add_string "overall" "$net_overall" + json_add_int "issues" "$net_issues" + json_close_object + + # Critical Services Status + json_add_object "services" + local svc_ok=0 svc_down=0 + for svc in sshd dropbear dnsmasq haproxy crowdsec; do + if pgrep -x "$svc" >/dev/null 2>&1 || pgrep -f "$svc" >/dev/null 2>&1; then + svc_ok=$((svc_ok + 1)) + else + if [ -f "/etc/init.d/$svc" ] && /etc/init.d/$svc enabled 2>/dev/null; then + svc_down=$((svc_down + 1)) + fi + fi + done + local lxc_expected=$(lxc-ls 2>/dev/null | wc -w) + local lxc_running=$(lxc-ls --running 2>/dev/null | wc -w) + json_add_int "services_ok" "$svc_ok" + json_add_int "services_down" "$svc_down" + json_add_int "containers_running" "${lxc_running:-0}" + json_add_int "containers_total" "${lxc_expected:-0}" + if [ "$svc_down" -gt 0 ]; then + json_add_string "overall" "warning" + else + json_add_string "overall" "healthy" + fi + json_close_object + + # Overall health score + local health_score=100 + [ "$net_overall" = "critical" ] && health_score=$((health_score - 30)) + [ "$svc_down" -gt 0 ] && health_score=$((health_score - (svc_down * 10))) + [ "$mem_pct" -gt 90 ] && health_score=$((health_score - 10)) + [ "${disk_pct:-0}" -gt 90 ] && health_score=$((health_score - 10)) + json_add_int "health_score" "$health_score" + + if [ "$health_score" -ge 80 ]; then + json_add_string "overall_status" "healthy" + elif [ "$health_score" -ge 50 ]; then + json_add_string "overall_status" "warning" + else + json_add_string "overall_status" "critical" + fi + + # Alerts + json_add_array "alerts" + [ "$net_overall" = "critical" ] && { + json_add_object "" + json_add_string "level" "critical" + json_add_string "message" "Network interface issues detected - check cables" + json_close_object + } + [ "$svc_down" -gt 0 ] && { + json_add_object "" + json_add_string "level" "warning" + json_add_string "message" "$svc_down critical service(s) not running" + json_close_object + } + [ "$mem_pct" -gt 90 ] && { + json_add_object "" + json_add_string "level" "warning" + json_add_string "message" "High memory usage: ${mem_pct}%" + json_close_object + } + json_close_array + + json_dump +} + +# System health metrics +_do_system_health() { + json_init + + local uptime_seconds=$(get_uptime_seconds) + local load1=$(awk '{print $1}' /proc/loadavg) + local cpu_count=$(grep -c ^processor /proc/cpuinfo) + [ "$cpu_count" -le 0 ] && cpu_count=1 + local cpu_percent=$(awk -v load="$load1" -v cores="$cpu_count" 'BEGIN {printf "%.0f", (load / cores) * 100}') + + local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}') + local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}') + mem_free=${mem_free:-0} + [ "$mem_total" -le 0 ] && mem_total=1 + local mem_used=$((mem_total - mem_free)) + local mem_percent=$(awk -v used="$mem_used" -v total="$mem_total" 'BEGIN {printf "%.0f", (used / total) * 100}') + + local disk_info=$(df / | tail -1) + local disk_total=$(echo "$disk_info" | awk '{print $2}') + local disk_used=$(echo "$disk_info" | awk '{print $3}') + disk_used=${disk_used:-0} + [ -z "$disk_total" ] || [ "$disk_total" -le 0 ] && { disk_total=0; disk_used=0; } + local disk_free=$((disk_total - disk_used)) + local disk_percent=$(echo "$disk_info" | awk '{print $5}' | tr -d '%') + [ -n "$disk_percent" ] || disk_percent=0 + + local overall_score=$(awk -v c="$cpu_percent" -v m="$mem_percent" -v d="$disk_percent" 'BEGIN {printf "%.0f", 100 - ((c + m + d) / 3)}') + + json_add_int "uptime" "$uptime_seconds" + + local overall_status="healthy" + [ "$overall_score" -le 70 ] && overall_status="warning" + + json_add_object "overall" + json_add_int "score" "$overall_score" + json_add_string "status" "$overall_status" + json_close_object + + json_add_object "cpu" + json_add_int "usage_percent" "$cpu_percent" + json_add_string "load" "$(get_load_avg)" + json_add_int "count" "$cpu_count" + json_close_object + + json_add_object "memory" + json_add_int "total_kb" "$mem_total" + json_add_int "used_kb" "$mem_used" + json_add_int "free_kb" "$mem_free" + json_add_int "usage_percent" "$mem_percent" + json_close_object + + json_add_object "disk" + json_add_int "total_kb" "$disk_total" + json_add_int "used_kb" "$disk_used" + json_add_int "free_kb" "$disk_free" + json_add_int "usage_percent" "$disk_percent" + json_close_object + + json_dump +} + +# Get system alerts +_do_get_alerts() { + json_init + json_add_array "alerts" + + local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}') + local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}') + local mem_percent=$(awk "BEGIN {printf \"%.0f\", (($mem_total - $mem_free) / $mem_total) * 100}") + if [ "$mem_percent" -gt 90 ]; then + json_add_object "" + json_add_string "id" "high_memory" + json_add_string "level" "warning" + json_add_string "title" "High Memory Usage" + json_add_string "message" "Memory usage is at ${mem_percent}%" + json_add_int "timestamp" "$(date +%s)" + json_close_object + fi + + local disk_percent=$(df / | tail -1 | awk '{print $5}' | tr -d '%') + if [ "$disk_percent" -gt 85 ]; then + json_add_object "" + json_add_string "id" "high_disk" + json_add_string "level" "warning" + json_add_string "title" "High Disk Usage" + json_add_string "message" "Disk usage is at ${disk_percent}%" + json_add_int "timestamp" "$(date +%s)" + json_close_object + fi + + json_close_array + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/modules.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/modules.sh new file mode 100644 index 00000000..001db9ca --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/modules.sh @@ -0,0 +1,108 @@ +#!/bin/sh +# +# SecuBox RPCD - Module Management +# Install, remove, update, info +# + +# Register methods +list_methods_modules() { + add_method "getModules" + add_method_str "getModuleInfo" "module" + json_add_object "installModule" + json_add_string "module" "string" + json_add_boolean "dryrun" "boolean" + json_close_object + add_method_str "removeModule" "module" + add_method_str "updateModule" "module" + + # Module control + add_method_str "start_module" "module" + add_method_str "stop_module" "module" + add_method_str "restart_module" "module" + add_method_str "enable_module" "module" + add_method_str "disable_module" "module" +} + +# Handle method calls +handle_modules() { + local method="$1" + case "$method" in + getModules) + /usr/sbin/secubox-appstore list --json + ;; + getModuleInfo) + read_input_json + local module=$(get_input "module") + /usr/sbin/secubox-appstore info "$module" + ;; + installModule) + read_input_json + local module=$(get_input "module") + local dryrun=$(get_input "dryrun") + /usr/sbin/secubox-appstore install "$module" ${dryrun:+--dryrun} + ;; + removeModule) + read_input_json + local module=$(get_input "module") + /usr/sbin/secubox-appstore remove "$module" + ;; + updateModule) + read_input_json + local module=$(get_input "module") + /usr/sbin/secubox-appstore update "$module" + ;; + start_module) + read_input_json + local module=$(get_input "module") + if [ -x "/etc/init.d/$module" ]; then + /etc/init.d/$module start + json_success "Module $module started" + else + json_error "Module $module not found" + fi + ;; + stop_module) + read_input_json + local module=$(get_input "module") + if [ -x "/etc/init.d/$module" ]; then + /etc/init.d/$module stop + json_success "Module $module stopped" + else + json_error "Module $module not found" + fi + ;; + restart_module) + read_input_json + local module=$(get_input "module") + if [ -x "/etc/init.d/$module" ]; then + /etc/init.d/$module restart + json_success "Module $module restarted" + else + json_error "Module $module not found" + fi + ;; + enable_module) + read_input_json + local module=$(get_input "module") + if [ -x "/etc/init.d/$module" ]; then + /etc/init.d/$module enable + json_success "Module $module enabled" + else + json_error "Module $module not found" + fi + ;; + disable_module) + read_input_json + local module=$(get_input "module") + if [ -x "/etc/init.d/$module" ]; then + /etc/init.d/$module disable + json_success "Module $module disabled" + else + json_error "Module $module not found" + fi + ;; + *) + return 1 + ;; + esac +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/network.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/network.sh new file mode 100644 index 00000000..d885c31c --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/network.sh @@ -0,0 +1,267 @@ +#!/bin/sh +# +# SecuBox RPCD - Network & WAN Access +# WAN access, services discovery, proxy mode +# + +# Register methods +list_methods_network() { + add_method "get_wan_access" + json_add_object "set_wan_access" + json_add_boolean "enabled" "boolean" + json_add_boolean "https_enabled" "boolean" + json_add_int "https_port" "integer" + json_add_boolean "http_enabled" "boolean" + json_add_int "http_port" "integer" + json_add_boolean "ssh_enabled" "boolean" + json_add_int "ssh_port" "integer" + json_close_object + add_method "apply_wan_access" + add_method "get_services" + add_method "get_proxy_mode" + add_method_str "set_proxy_mode" "mode" +} + +# Handle method calls +handle_network() { + local method="$1" + case "$method" in + get_wan_access) + /usr/sbin/secubox-wan-access json + ;; + set_wan_access) + read_input_json + local enabled=$(get_input "enabled") + local https_enabled=$(get_input "https_enabled") + local https_port=$(get_input "https_port") + local http_enabled=$(get_input "http_enabled") + local http_port=$(get_input "http_port") + local ssh_enabled=$(get_input "ssh_enabled") + local ssh_port=$(get_input "ssh_port") + + [ -n "$enabled" ] && uci set secubox.remote.enabled="$enabled" + [ -n "$https_enabled" ] && uci set secubox.remote.https_enabled="$https_enabled" + [ -n "$https_port" ] && uci set secubox.remote.https_port="$https_port" + [ -n "$http_enabled" ] && uci set secubox.remote.http_enabled="$http_enabled" + [ -n "$http_port" ] && uci set secubox.remote.http_port="$http_port" + [ -n "$ssh_enabled" ] && uci set secubox.remote.ssh_enabled="$ssh_enabled" + [ -n "$ssh_port" ] && uci set secubox.remote.ssh_port="$ssh_port" + uci commit secubox + + json_success "WAN access settings updated" + ;; + apply_wan_access) + /usr/sbin/secubox-wan-access apply >/dev/null 2>&1 + json_success "WAN access rules applied" + ;; + get_services) + _do_get_services + ;; + get_proxy_mode) + _do_get_proxy_mode + ;; + set_proxy_mode) + read_input_json + local mode=$(get_input "mode") + _do_set_proxy_mode "$mode" + ;; + *) + return 1 + ;; + esac +} + +# Discover listening services +_do_get_services() { + local TMP_SERVICES="/tmp/services_$$" + netstat -tlnp 2>/dev/null | grep LISTEN | awk '{ + split($4, a, ":") + port = a[length(a)] + if (!seen[port]++) { + split($7, p, "/") + proc = p[2] + if (proc == "") proc = "unknown" + print port, $4, proc + } + }' | sort -n -u > "$TMP_SERVICES" + + json_init + json_add_array "services" + + while read port local proc; do + local addr=$(echo "$local" | sed 's/:[^:]*$//') + local name="" icon="" category="other" path="" + + 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 + + if [ -z "$name" ]; then + 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" ;; + 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 + + local external=0 + case "$addr" in 0.0.0.0|::) external=1 ;; 127.0.0.1|::1) ;; *) external=1 ;; esac + + json_add_object "" + json_add_int "port" "$port" + json_add_string "address" "$addr" + json_add_string "name" "$name" + json_add_string "icon" "$icon" + json_add_string "process" "$proc" + json_add_string "category" "$category" + json_add_boolean "external" "$external" + [ -n "$path" ] && [ "$external" = "1" ] && json_add_string "url" "$path" + json_close_object + done < "$TMP_SERVICES" + + rm -f "$TMP_SERVICES" + json_close_array + json_dump +} + +# Get proxy mode +_do_get_proxy_mode() { + json_init + local mode="direct" + local wpad_enabled=0 + + if [ -f "/www/wpad/wpad.dat" ]; then + wpad_enabled=1 + if grep -q "SOCKS5.*9050" /www/wpad/wpad.dat 2>/dev/null; then + mode="tor" + elif grep -q "PROXY.*3128" /www/wpad/wpad.dat 2>/dev/null; then + mode="cdn" + elif grep -q "PROXY.*8080" /www/wpad/wpad.dat 2>/dev/null; then + mode="mitmproxy" + fi + fi + + local dhcp_wpad=$(uci -q get dhcp.lan.dhcp_option | grep -c "252") + + json_add_string "mode" "$mode" + json_add_boolean "wpad_enabled" "$wpad_enabled" + json_add_boolean "dhcp_wpad" "$dhcp_wpad" + json_add_string "pac_url" "http://192.168.255.1/wpad/wpad.dat" + json_dump +} + +# Set proxy mode +_do_set_proxy_mode() { + local mode="$1" + json_init + + mkdir -p /www/wpad + + case "$mode" in + direct) + rm -f /www/wpad/wpad.dat + uci -q delete dhcp.lan.dhcp_option + uci commit dhcp + json_add_boolean "success" 1 + json_add_string "message" "Proxy disabled - direct connections" + ;; + cdn) + cat > /www/wpad/wpad.dat << 'PACEOF' +function FindProxyForURL(url, host) { + if (isPlainHostName(host) || shExpMatch(host, "*.local") || shExpMatch(host, "*.lan") || + isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || + isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || + isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || + isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) { + return "DIRECT"; + } + if (url.substring(0, 5) == "http:") { + return "PROXY 192.168.255.1:3128; DIRECT"; + } + return "DIRECT"; +} +PACEOF + uci set dhcp.lan.dhcp_option="252,http://192.168.255.1/wpad/wpad.dat" + uci commit dhcp + json_add_boolean "success" 1 + json_add_string "message" "CDN cache mode enabled - HTTP cached" + ;; + tor) + cat > /www/wpad/wpad.dat << 'PACEOF' +function FindProxyForURL(url, host) { + if (isPlainHostName(host) || shExpMatch(host, "*.local") || shExpMatch(host, "*.lan") || + isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || + isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || + isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || + isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) { + return "DIRECT"; + } + if (url.substring(0, 5) == "http:") { + return "PROXY 192.168.255.1:3128; DIRECT"; + } + if (url.substring(0, 6) == "https:") { + return "SOCKS5 192.168.255.1:9050; DIRECT"; + } + return "DIRECT"; +} +PACEOF + uci set dhcp.lan.dhcp_option="252,http://192.168.255.1/wpad/wpad.dat" + uci commit dhcp + json_add_boolean "success" 1 + json_add_string "message" "Tor bypass mode enabled - HTTPS through Tor" + ;; + mitmproxy) + cat > /www/wpad/wpad.dat << 'PACEOF' +function FindProxyForURL(url, host) { + if (isPlainHostName(host) || shExpMatch(host, "*.local") || shExpMatch(host, "*.lan") || + isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || + isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || + isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || + isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) { + return "DIRECT"; + } + return "PROXY 192.168.255.1:8080; DIRECT"; +} +PACEOF + uci set dhcp.lan.dhcp_option="252,http://192.168.255.1/wpad/wpad.dat" + uci commit dhcp + json_add_boolean "success" 1 + json_add_string "message" "mitmproxy mode enabled - all traffic inspectable" + ;; + *) + json_add_boolean "success" 0 + json_add_string "error" "Unknown mode: $mode" + ;; + esac + + /etc/init.d/dnsmasq restart >/dev/null 2>&1 & + + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/p2p.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/p2p.sh new file mode 100644 index 00000000..5f6929e3 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/p2p.sh @@ -0,0 +1,236 @@ +#!/bin/sh +# +# SecuBox RPCD - P2P Hub +# Peer management, catalog sharing +# + +P2P_DIR="/var/lib/secubox/p2p" +P2P_PEERS_FILE="$P2P_DIR/peers.json" +P2P_CONFIG="$P2P_DIR/config.json" + +# Register methods +list_methods_p2p() { + add_method "p2p_get_peers" + add_method "p2p_discover" + json_add_object "p2p_add_peer" + json_add_string "address" "string" + json_add_string "name" "string" + json_close_object + add_method_str "p2p_remove_peer" "peer_id" + add_method_str "p2p_get_peer_catalog" "peer_id" + add_method_bool "p2p_share_catalog" "enabled" + add_method "p2p_get_settings" + json_add_object "p2p_set_settings" + json_add_object "settings" + json_close_object + json_close_object +} + +# Handle method calls +handle_p2p() { + local method="$1" + mkdir -p "$P2P_DIR" + + case "$method" in + p2p_get_peers) + _do_p2p_get_peers + ;; + p2p_discover) + _do_p2p_discover + ;; + p2p_add_peer) + read_input_json + local address=$(get_input "address") + local name=$(get_input "name") + _do_p2p_add_peer "$address" "$name" + ;; + p2p_remove_peer) + read_input_json + local peer_id=$(get_input "peer_id") + _do_p2p_remove_peer "$peer_id" + ;; + p2p_get_peer_catalog) + read_input_json + local peer_id=$(get_input "peer_id") + _do_p2p_get_peer_catalog "$peer_id" + ;; + p2p_share_catalog) + read_input_json + local enabled=$(get_input_bool "enabled") + echo "{\"sharing_enabled\":$enabled}" > "$P2P_CONFIG" + json_init + json_add_boolean "success" 1 + json_add_boolean "sharing_enabled" "$enabled" + json_dump + ;; + p2p_get_settings) + json_init + if [ -f "$P2P_CONFIG" ]; then + local sharing=$(jsonfilter -i "$P2P_CONFIG" -e '@.sharing_enabled' 2>/dev/null) + json_add_boolean "sharing_enabled" "${sharing:-0}" + else + json_add_boolean "sharing_enabled" 0 + fi + json_add_string "hub_version" "1.0.0" + json_add_string "protocol" "http" + json_add_int "port" 8080 + json_dump + ;; + p2p_set_settings) + read_input_json + local sharing=$(echo "$INPUT_JSON" | jsonfilter -e '@.settings.sharing_enabled' 2>/dev/null) + echo "{\"sharing_enabled\":${sharing:-false}}" > "$P2P_CONFIG" + json_success "Settings updated" + ;; + *) + return 1 + ;; + esac +} + +# Get peers list +_do_p2p_get_peers() { + json_init + json_add_array "peers" + + if [ -f "$P2P_PEERS_FILE" ]; then + local idx=0 + while true; do + local peer_id=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].id" 2>/dev/null) + [ -z "$peer_id" ] && break + + local peer_name=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].name" 2>/dev/null) + local peer_addr=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].address" 2>/dev/null) + local peer_status=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].status" 2>/dev/null) + local last_seen=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].last_seen" 2>/dev/null) + + json_add_object "" + json_add_string "id" "$peer_id" + json_add_string "name" "${peer_name:-$peer_id}" + json_add_string "address" "$peer_addr" + json_add_string "status" "${peer_status:-unknown}" + json_add_string "last_seen" "$last_seen" + json_close_object + + idx=$((idx + 1)) + done + fi + + json_close_array + json_dump +} + +# mDNS discovery +_do_p2p_discover() { + local discovered=0 + json_init + json_add_array "peers" + + if command -v avahi-browse >/dev/null 2>&1; then + avahi-browse -t -r _secubox._tcp 2>/dev/null | grep -E "^\+" | while read -r line; do + local addr=$(echo "$line" | awk '{print $7}') + local name=$(echo "$line" | awk '{print $4}') + if [ -n "$addr" ]; then + json_add_object "" + json_add_string "address" "$addr" + json_add_string "name" "$name" + json_add_string "discovered" "mdns" + json_close_object + discovered=$((discovered + 1)) + fi + done + fi + + json_close_array + json_add_int "discovered" "$discovered" + json_dump +} + +# Add peer +_do_p2p_add_peer() { + local address="$1" + local name="$2" + + local peer_id="peer_$(echo "$address" | md5sum | cut -c1-8)" + + [ ! -f "$P2P_PEERS_FILE" ] && echo '{"peers":[]}' > "$P2P_PEERS_FILE" + + local timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) + local tmp_file="${P2P_PEERS_FILE}.tmp" + { + echo '{"peers":[' + if [ -f "$P2P_PEERS_FILE" ]; then + jsonfilter -i "$P2P_PEERS_FILE" -e '@.peers[*]' 2>/dev/null | while read -r p; do + echo "$p," + done + fi + echo "{\"id\":\"$peer_id\",\"name\":\"$name\",\"address\":\"$address\",\"status\":\"added\",\"last_seen\":\"$timestamp\"}" + echo ']}' + } > "$tmp_file" + mv "$tmp_file" "$P2P_PEERS_FILE" + + json_init + json_add_boolean "success" 1 + json_add_string "peer_id" "$peer_id" + json_dump +} + +# Remove peer +_do_p2p_remove_peer() { + local peer_id="$1" + + if [ -f "$P2P_PEERS_FILE" ] && [ -n "$peer_id" ]; then + local tmp_file="${P2P_PEERS_FILE}.tmp" + { + echo '{"peers":[' + local first=1 + local idx=0 + while true; do + local id=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].id" 2>/dev/null) + [ -z "$id" ] && break + if [ "$id" != "$peer_id" ]; then + [ "$first" -eq 0 ] && echo "," + jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx]" 2>/dev/null + first=0 + fi + idx=$((idx + 1)) + done + echo ']}' + } > "$tmp_file" + mv "$tmp_file" "$P2P_PEERS_FILE" + fi + + json_success "Peer removed" +} + +# Get peer catalog +_do_p2p_get_peer_catalog() { + local peer_id="$1" + + local idx=0 + local peer_addr="" + while true; do + local id=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].id" 2>/dev/null) + [ -z "$id" ] && break + if [ "$id" = "$peer_id" ]; then + peer_addr=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].address" 2>/dev/null) + break + fi + idx=$((idx + 1)) + done + + json_init + + if [ -n "$peer_addr" ]; then + local catalog=$(wget -q -O - "http://${peer_addr}:8080/api/catalog" 2>/dev/null) + if [ -n "$catalog" ]; then + echo "$catalog" + return + fi + fi + + json_add_array "apps" + json_close_array + json_add_string "error" "Could not fetch catalog from peer" + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/profiles.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/profiles.sh new file mode 100644 index 00000000..0e4f3fb3 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/profiles.sh @@ -0,0 +1,104 @@ +#!/bin/sh +# +# SecuBox RPCD - Profile Management +# List, get, apply, validate, export, import profiles +# + +# Register methods +list_methods_profiles() { + add_method "listProfiles" + add_method_str "getProfile" "profile" + json_add_object "applyProfile" + json_add_string "profile" "string" + json_add_boolean "dryrun" "boolean" + json_close_object + add_method "rollbackProfile" + add_method_str "validateProfile" "profile" + json_add_object "export_profile" + json_add_string "name" "string" + json_add_boolean "include_feeds" "boolean" + json_close_object + json_add_object "import_profile" + json_add_string "url" "string" + json_add_string "mode" "string" + json_close_object + add_method_str "share_profile" "profile" +} + +# Handle method calls +handle_profiles() { + local method="$1" + case "$method" in + listProfiles) + /usr/sbin/secubox-profile list --json + ;; + getProfile) + read_input_json + local profile=$(get_input "profile") + /usr/sbin/secubox-profile show "$profile" + ;; + applyProfile) + read_input_json + local profile=$(get_input "profile") + local dryrun=$(get_input "dryrun") + local result=$(/usr/sbin/secubox-profile apply "$profile" ${dryrun:+--dryrun} 2>&1) + if [ $? -eq 0 ]; then + echo '{"success":true,"message":"Profile applied successfully"}' + else + echo "{\"success\":false,\"message\":\"Failed to apply profile\",\"error\":\"$result\"}" + fi + ;; + rollbackProfile) + if [ -f /usr/sbin/secubox-recovery ]; then + local result=$(/usr/sbin/secubox-recovery restore last 2>&1) + if [ $? -eq 0 ]; then + echo '{"success":true,"message":"Rolled back to last snapshot"}' + else + echo "{\"success\":false,\"message\":\"Rollback failed\",\"error\":\"$result\"}" + fi + else + echo '{"success":false,"message":"Recovery system not available"}' + fi + ;; + validateProfile) + read_input_json + local profile=$(get_input "profile") + /usr/sbin/secubox-profile validate "$profile" + ;; + export_profile) + read_input_json + local name=$(get_input "name") + local include_feeds=$(get_input "include_feeds") + local args="" + [ -n "$name" ] && args="$args --name \"$name\"" + [ "$include_feeds" = "true" ] && args="$args --include-feeds" + local output="/tmp/secubox-profile-export-$$.json" + eval /usr/sbin/secubox-profile export $args --output "$output" >/dev/null 2>&1 + if [ -f "$output" ]; then + cat "$output" + rm -f "$output" + else + json_error "Export failed" + fi + ;; + import_profile) + read_input_json + local url=$(get_input "url") + local mode=$(get_input "mode") + local result=$(/usr/sbin/secubox-profile import "$url" "${mode:---merge}" 2>&1) + if [ $? -eq 0 ]; then + json_success "Profile imported" + else + json_error "$result" + fi + ;; + share_profile) + read_input_json + local profile=$(get_input "profile") + /usr/sbin/secubox-profile share "$profile" + ;; + *) + return 1 + ;; + esac +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/skills.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/skills.sh new file mode 100644 index 00000000..da851753 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/skills.sh @@ -0,0 +1,102 @@ +#!/bin/sh +# +# SecuBox RPCD - Skill Management +# List skills, get providers, install, check +# + +# Register methods +list_methods_skills() { + add_method "list_skills" + add_method_str "get_skill_providers" "skill" + add_method_str "install_skill" "skill" + add_method_str "check_skills" "profile" +} + +# Handle method calls +handle_skills() { + local method="$1" + case "$method" in + list_skills) + /usr/sbin/secubox-skill list --json + ;; + get_skill_providers) + read_input_json + local skill=$(get_input "skill") + _do_get_skill_providers "$skill" + ;; + install_skill) + read_input_json + local skill=$(get_input "skill") + local result=$(/usr/sbin/secubox-skill install "$skill" 2>&1) + if [ $? -eq 0 ]; then + json_init + json_add_boolean "success" 1 + json_add_string "message" "Skill provider installed" + json_add_string "skill" "$skill" + json_dump + else + json_error "$result" + fi + ;; + check_skills) + read_input_json + local profile=$(get_input "profile") + if [ -n "$profile" ]; then + /usr/sbin/secubox-skill check "$profile" + else + /usr/sbin/secubox-skill check + fi + ;; + *) + return 1 + ;; + esac +} + +# Get skill providers from catalog +_do_get_skill_providers() { + local skill="$1" + local CATALOG_FILE="/usr/share/secubox/catalog.json" + + json_init + json_add_string "skill" "$skill" + json_add_array "providers" + + if [ -f "$CATALOG_FILE" ]; then + local idx=0 + while true; do + local pid=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].id" 2>/dev/null) + [ -z "$pid" ] && break + + local caps=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].capabilities[@]" 2>/dev/null) + for cap in $caps; do + local cap_norm=$(echo "$cap" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') + if [ "$cap_norm" = "$skill" ]; then + local pname=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].name" 2>/dev/null) + local pstatus=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].status" 2>/dev/null) + local featured=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].featured" 2>/dev/null) + + json_add_object "" + json_add_string "id" "$pid" + json_add_string "name" "$pname" + json_add_string "status" "$pstatus" + json_add_boolean "featured" "$([ "$featured" = "true" ] && echo 1 || echo 0)" + + local main_pkg=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].packages.required[0]" 2>/dev/null) + local installed=0 + if [ -n "$main_pkg" ] && opkg list-installed 2>/dev/null | grep -q "^$main_pkg "; then + installed=1 + fi + json_add_boolean "installed" "$installed" + json_close_object + break + fi + done + + idx=$((idx + 1)) + done + fi + + json_close_array + json_dump +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/snapshots.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/snapshots.sh new file mode 100644 index 00000000..cb03f546 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/snapshots.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# +# SecuBox RPCD - Snapshot/Recovery Management +# Create, list, restore snapshots +# + +# Register methods +list_methods_snapshots() { + add_method_str "createSnapshot" "name" + add_method "listSnapshots" + add_method_str "restoreSnapshot" "snapshot" +} + +# Handle method calls +handle_snapshots() { + local method="$1" + case "$method" in + createSnapshot) + read_input_json + local name=$(get_input "name") + /usr/sbin/secubox-recovery snapshot "${name:-auto-$(date +%Y%m%d-%H%M%S)}" + ;; + listSnapshots) + /usr/sbin/secubox-recovery list --json + ;; + restoreSnapshot) + read_input_json + local snapshot=$(get_input "snapshot") + /usr/sbin/secubox-recovery rollback "$snapshot" + ;; + *) + return 1 + ;; + esac +} diff --git a/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/state.sh b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/state.sh new file mode 100644 index 00000000..83aabb77 --- /dev/null +++ b/package/secubox/secubox-core/root/usr/lib/secubox/rpcd.d/state.sh @@ -0,0 +1,156 @@ +#!/bin/sh +# +# SecuBox RPCD - State & Component Management +# Component state, history, registry +# + +# Register methods +list_methods_state() { + add_method_str "get_component_state" "component_id" + json_add_object "set_component_state" + json_add_string "component_id" "string" + json_add_string "new_state" "string" + json_add_string "reason" "string" + json_close_object + json_add_object "get_state_history" + json_add_string "component_id" "string" + json_add_int "limit" "integer" + json_close_object + json_add_object "list_components" + json_add_string "state_filter" "string" + json_add_string "type_filter" "string" + json_close_object + json_add_object "freeze_component" + json_add_string "component_id" "string" + json_add_string "reason" "string" + json_close_object + add_method_str "clear_error_state" "component_id" + add_method_str "get_component" "component_id" + json_add_object "list_all_components" + json_add_string "type" "string" + json_add_string "profile" "string" + json_close_object + add_method_str "get_component_tree" "component_id" + json_add_object "update_component_settings" + json_add_string "component_id" "string" + json_add_object "settings" + json_close_object + json_close_object + add_method "sync_component_registry" +} + +# Handle method calls +handle_state() { + local method="$1" + case "$method" in + get_component_state) + read_input_json + local component_id=$(get_input "component_id") + /usr/sbin/secubox-state get "$component_id" + ;; + set_component_state) + read_input_json + local component_id=$(get_input "component_id") + local new_state=$(get_input "new_state") + local reason=$(get_input "reason") + local result=$(/usr/sbin/secubox-state set "$component_id" "$new_state" "${reason:-manual}") + json_init + if echo "$result" | grep -q "Success:"; then + json_add_boolean "success" 1 + json_add_string "message" "$result" + json_add_string "component_id" "$component_id" + json_add_string "new_state" "$new_state" + else + json_add_boolean "success" 0 + json_add_string "error" "$result" + fi + json_dump + ;; + get_state_history) + read_input_json + local component_id=$(get_input "component_id") + local limit=$(get_input_int "limit" 20) + /usr/sbin/secubox-state history "$component_id" "$limit" + ;; + list_components) + read_input_json + local state_filter=$(get_input "state_filter") + local type_filter=$(get_input "type_filter") + local args="" + [ -n "$state_filter" ] && args="$args --state=$state_filter" + [ -n "$type_filter" ] && args="$args --type=$type_filter" + /usr/sbin/secubox-state list $args + ;; + freeze_component) + read_input_json + local component_id=$(get_input "component_id") + local reason=$(get_input "reason") + local result=$(/usr/sbin/secubox-state freeze "$component_id" "${reason:-manual_freeze}") + json_init + if echo "$result" | grep -q "Success:"; then + json_add_boolean "success" 1 + json_add_string "message" "$result" + else + json_add_boolean "success" 0 + json_add_string "error" "$result" + fi + json_dump + ;; + clear_error_state) + read_input_json + local component_id=$(get_input "component_id") + local result=$(/usr/sbin/secubox-state clear-error "$component_id") + json_init + if echo "$result" | grep -q "Success:"; then + json_add_boolean "success" 1 + json_add_string "message" "$result" + else + json_add_boolean "success" 0 + json_add_string "error" "$result" + fi + json_dump + ;; + get_component) + read_input_json + local component_id=$(get_input "component_id") + /usr/sbin/secubox-component get "$component_id" + ;; + list_all_components) + read_input_json + local type=$(get_input "type") + local profile=$(get_input "profile") + local args="" + [ -n "$type" ] && args="$args --type=$type" + [ -n "$profile" ] && args="$args --profile=$profile" + /usr/sbin/secubox-component list $args + ;; + get_component_tree) + read_input_json + local component_id=$(get_input "component_id") + /usr/sbin/secubox-component tree "$component_id" + ;; + update_component_settings) + read_input_json + local component_id=$(get_input "component_id") + json_init + json_add_boolean "success" 1 + json_add_string "message" "Settings update functionality available via CLI: secubox-component set-setting" + json_dump + ;; + sync_component_registry) + local result=$(/usr/sbin/secubox-sync-registry sync) + json_init + if echo "$result" | grep -q "successfully"; then + json_add_boolean "success" 1 + json_add_string "message" "$result" + else + json_add_boolean "success" 0 + json_add_string "error" "$result" + fi + json_dump + ;; + *) + return 1 + ;; + esac +} 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 65041ade..089ce733 100755 --- a/package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox +++ b/package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox @@ -1,2544 +1,167 @@ #!/bin/sh - # # SecuBox Core RPCD Interface -# Provides ubus API for SecuBox operations +# Thin dispatcher that loads modular method handlers +# +# Modules are in: /usr/lib/secubox/rpcd.d/ # . /usr/share/libubox/jshn.sh . /lib/functions.sh -case "$1" in - list) - json_init +RPCD_MODULE_DIR="/usr/lib/secubox/rpcd.d" - # Core status and information - json_add_object "getStatus" - json_close_object +# Source common utilities +[ -f "$RPCD_MODULE_DIR/_common.sh" ] && . "$RPCD_MODULE_DIR/_common.sh" - json_add_object "getVersion" - json_close_object +# Source all modules +_load_modules() { + for mod in "$RPCD_MODULE_DIR"/*.sh; do + [ -f "$mod" ] || continue + [ "$(basename "$mod")" = "_common.sh" ] && continue + . "$mod" + done +} - json_add_object "reload" - json_close_object +# List all methods from all modules +_list_all_methods() { + json_init - # Module management - json_add_object "getModules" - json_close_object + # Core module + [ "$(type -t list_methods_core)" = "function" ] 2>/dev/null && list_methods_core + type list_methods_core >/dev/null 2>&1 && list_methods_core - json_add_object "getModuleInfo" - json_add_string "module" "string" - json_close_object + # Modules module + type list_methods_modules >/dev/null 2>&1 && list_methods_modules - json_add_object "installModule" - json_add_string "module" "string" - json_add_boolean "dryrun" "boolean" - json_close_object + # Profiles module + type list_methods_profiles >/dev/null 2>&1 && list_methods_profiles - json_add_object "removeModule" - json_add_string "module" "string" - json_close_object + # Snapshots module + type list_methods_snapshots >/dev/null 2>&1 && list_methods_snapshots - json_add_object "updateModule" - json_add_string "module" "string" - json_close_object + # Health module + type list_methods_health >/dev/null 2>&1 && list_methods_health - # Profile management - json_add_object "listProfiles" - json_close_object + # Dashboard module + type list_methods_dashboard >/dev/null 2>&1 && list_methods_dashboard - json_add_object "getProfile" - json_add_string "profile" "string" - json_close_object + # Appstore module + type list_methods_appstore >/dev/null 2>&1 && list_methods_appstore - json_add_object "applyProfile" - json_add_string "profile" "string" - json_add_boolean "dryrun" "boolean" - json_close_object + # State module + type list_methods_state >/dev/null 2>&1 && list_methods_state - json_add_object "rollbackProfile" - json_close_object + # Network module + type list_methods_network >/dev/null 2>&1 && list_methods_network - json_add_object "validateProfile" - json_add_string "profile" "string" - json_close_object + # Feeds module + type list_methods_feeds >/dev/null 2>&1 && list_methods_feeds - # Diagnostics - json_add_object "runDiagnostics" - json_add_string "target" "string" - json_close_object + # Skills module + type list_methods_skills >/dev/null 2>&1 && list_methods_skills - json_add_object "getHealth" - json_close_object + # Feedback module + type list_methods_feedback >/dev/null 2>&1 && list_methods_feedback - json_add_object "get_network_health" - json_close_object - - json_add_object "get_vital_services" - json_close_object - - json_add_object "get_full_health_report" - json_close_object - - json_add_object "getLogs" - json_add_string "service" "string" - json_add_int "lines" "integer" - json_close_object - - # Snapshot/Recovery - json_add_object "createSnapshot" - json_add_string "name" "string" - json_close_object - - json_add_object "listSnapshots" - json_close_object - - json_add_object "restoreSnapshot" - json_add_string "snapshot" "string" - json_close_object - - # AppStore (NEW - uses catalog files) - json_add_object "get_appstore_apps" - json_close_object - - json_add_object "list_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 - - # Catalog source management - json_add_object "get_catalog_sources" - json_close_object - - json_add_object "set_catalog_source" - json_add_string "source" "string" - json_close_object - - json_add_object "sync_catalog" - json_add_string "source" "string" - json_close_object - - # Version and update management - json_add_object "check_updates" - json_close_object - - json_add_object "get_app_versions" - json_add_string "app_id" "string" - json_close_object - - json_add_object "get_changelog" - json_add_string "app_id" "string" - json_add_string "from_version" "string" - json_add_string "to_version" "string" - json_close_object - - # Widget data - json_add_object "get_widget_data" - json_add_string "app_id" "string" - json_close_object - - # Dashboard and monitoring - json_add_object "get_dashboard_data" - json_close_object - - json_add_object "get_system_health" - json_close_object - - json_add_object "get_public_ips" - json_close_object - - json_add_object "refresh_public_ips" - json_close_object - - json_add_object "get_alerts" - json_close_object - - # Quick actions - json_add_object "quick_action" - json_add_string "action" "string" - json_close_object - - # State management - json_add_object "get_component_state" - json_add_string "component_id" "string" - json_close_object - - json_add_object "set_component_state" - json_add_string "component_id" "string" - json_add_string "new_state" "string" - json_add_string "reason" "string" - json_close_object - - json_add_object "get_state_history" - json_add_string "component_id" "string" - json_add_int "limit" "integer" - json_close_object - - json_add_object "list_components" - json_add_string "state_filter" "string" - json_add_string "type_filter" "string" - json_close_object - - json_add_object "freeze_component" - json_add_string "component_id" "string" - json_add_string "reason" "string" - json_close_object - - json_add_object "clear_error_state" - json_add_string "component_id" "string" - json_close_object - - # Component registry management - json_add_object "get_component" - json_add_string "component_id" "string" - json_close_object - - json_add_object "list_all_components" - json_add_string "type" "string" - json_add_string "profile" "string" - json_close_object - - json_add_object "get_component_tree" - json_add_string "component_id" "string" - json_close_object - - json_add_object "update_component_settings" - json_add_string "component_id" "string" - json_add_object "settings" - json_close_object - json_close_object - - json_add_object "sync_component_registry" - json_close_object - - # WAN Access management - json_add_object "get_wan_access" - json_close_object - - json_add_object "set_wan_access" - json_add_boolean "enabled" "boolean" - json_add_boolean "https_enabled" "boolean" - json_add_int "https_port" "integer" - json_add_boolean "http_enabled" "boolean" - json_add_int "http_port" "integer" - json_add_boolean "ssh_enabled" "boolean" - json_add_int "ssh_port" "integer" - json_close_object - - json_add_object "apply_wan_access" - json_close_object - - # Services discovery - json_add_object "get_services" - json_close_object - - # Proxy mode management - json_add_object "get_proxy_mode" - json_close_object - - json_add_object "set_proxy_mode" - json_add_string "mode" "string" - json_close_object - - # Feed management (new) - json_add_object "list_feeds" - json_close_object - - json_add_object "add_feed" - json_add_string "name" "string" - json_add_string "url" "string" - json_add_string "feed_type" "string" - json_add_string "description" "string" - json_close_object - - json_add_object "remove_feed" - json_add_string "name" "string" - json_close_object - - json_add_object "share_feed" - json_add_string "name" "string" - json_close_object - - json_add_object "import_feed" - json_add_string "url" "string" - json_close_object - - # Profile management (extended) - json_add_object "export_profile" - json_add_string "name" "string" - json_add_boolean "include_feeds" "boolean" - json_close_object - - json_add_object "import_profile" - json_add_string "url" "string" - json_add_string "mode" "string" - json_close_object - - json_add_object "share_profile" - json_add_string "profile" "string" - json_close_object - - # Skill management (new) - json_add_object "list_skills" - json_close_object - - json_add_object "get_skill_providers" - json_add_string "skill" "string" - json_close_object - - json_add_object "install_skill" - json_add_string "skill" "string" - json_close_object - - json_add_object "check_skills" - json_add_string "profile" "string" - json_close_object - - # Feedback management (new) - json_add_object "report_issue" - json_add_string "app_id" "string" - json_add_string "type" "string" - json_add_string "summary" "string" - json_add_string "details" "string" - json_close_object - - json_add_object "resolve_issue" - json_add_string "issue_id" "string" - json_add_string "description" "string" - json_close_object - - json_add_object "search_resolutions" - json_add_string "keyword" "string" - json_close_object - - json_add_object "list_issues" - json_add_string "filter" "string" - json_close_object - - # P2P Hub - Collaborative catalog sharing - json_add_object "p2p_get_peers" - json_close_object - - json_add_object "p2p_discover" - json_close_object - - json_add_object "p2p_add_peer" - json_add_string "address" "string" - json_add_string "name" "string" - json_close_object - - json_add_object "p2p_remove_peer" - json_add_string "peer_id" "string" - json_close_object - - json_add_object "p2p_get_peer_catalog" - json_add_string "peer_id" "string" - json_close_object - - json_add_object "p2p_share_catalog" - json_add_boolean "enabled" "boolean" - json_close_object - - json_add_object "p2p_get_settings" - json_close_object - - json_add_object "p2p_set_settings" - json_add_object "settings" - json_close_object + # P2P module + type list_methods_p2p >/dev/null 2>&1 && list_methods_p2p json_dump +} + +# Dispatch method call to appropriate handler +_call_method() { + local method="$1" + + # Try each handler until one succeeds + # Order matches typical call frequency for performance + + # Core methods (most common) + if type handle_core >/dev/null 2>&1; then + handle_core "$method" && return 0 + fi + + # Dashboard methods + if type handle_dashboard >/dev/null 2>&1; then + handle_dashboard "$method" && return 0 + fi + + # Health methods + if type handle_health >/dev/null 2>&1; then + handle_health "$method" && return 0 + fi + + # Appstore methods + if type handle_appstore >/dev/null 2>&1; then + handle_appstore "$method" && return 0 + fi + + # Modules methods + if type handle_modules >/dev/null 2>&1; then + handle_modules "$method" && return 0 + fi + + # Profiles methods + if type handle_profiles >/dev/null 2>&1; then + handle_profiles "$method" && return 0 + fi + + # Snapshots methods + if type handle_snapshots >/dev/null 2>&1; then + handle_snapshots "$method" && return 0 + fi + + # State methods + if type handle_state >/dev/null 2>&1; then + handle_state "$method" && return 0 + fi + + # Network methods + if type handle_network >/dev/null 2>&1; then + handle_network "$method" && return 0 + fi + + # Feeds methods + if type handle_feeds >/dev/null 2>&1; then + handle_feeds "$method" && return 0 + fi + + # Skills methods + if type handle_skills >/dev/null 2>&1; then + handle_skills "$method" && return 0 + fi + + # Feedback methods + if type handle_feedback >/dev/null 2>&1; then + handle_feedback "$method" && return 0 + fi + + # P2P methods + if type handle_p2p >/dev/null 2>&1; then + handle_p2p "$method" && return 0 + fi + + # Unknown method + json_init + json_add_boolean "error" true + json_add_string "message" "Unknown method: $method" + json_dump + return 1 +} + +# Main dispatcher +case "$1" in + list) + _load_modules + _list_all_methods ;; - call) - case "$2" in - getStatus) - /usr/sbin/secubox-core status - ;; - - getVersion) - json_init - json_add_string "version" "0.8.0" - json_add_string "core" "secubox-core" - json_add_string "build_date" "$(date -u +%Y-%m-%d)" - json_dump - ;; - - reload) - /usr/sbin/secubox-core reload - json_init - json_add_boolean "success" 1 - json_dump - ;; - - getModules) - /usr/sbin/secubox-appstore list --json - ;; - - getModuleInfo) - read -r input - module=$(echo "$input" | jsonfilter -e '@.module') - /usr/sbin/secubox-appstore info "$module" - ;; - - installModule) - read -r input - module=$(echo "$input" | jsonfilter -e '@.module') - dryrun=$(echo "$input" | jsonfilter -e '@.dryrun') - /usr/sbin/secubox-appstore install "$module" ${dryrun:+--dryrun} - ;; - - removeModule) - read -r input - module=$(echo "$input" | jsonfilter -e '@.module') - /usr/sbin/secubox-appstore remove "$module" - ;; - - updateModule) - read -r input - module=$(echo "$input" | jsonfilter -e '@.module') - /usr/sbin/secubox-appstore update "$module" - ;; - - listProfiles) - /usr/sbin/secubox-profile list --json - ;; - - getProfile) - read -r input - profile=$(echo "$input" | jsonfilter -e '@.profile') - /usr/sbin/secubox-profile show "$profile" - ;; - - applyProfile) - read -r input - profile=$(echo "$input" | jsonfilter -e '@.profile') - dryrun=$(echo "$input" | jsonfilter -e '@.dryrun') - result=$(/usr/sbin/secubox-profile apply "$profile" ${dryrun:+--dryrun} 2>&1) - if [ $? -eq 0 ]; then - echo '{"success":true,"message":"Profile applied successfully"}' - else - echo "{\"success\":false,\"message\":\"Failed to apply profile\",\"error\":\"$result\"}" - fi - ;; - - rollbackProfile) - # Rollback to last snapshot created before profile application - if [ -f /usr/sbin/secubox-recovery ]; then - result=$(/usr/sbin/secubox-recovery restore last 2>&1) - if [ $? -eq 0 ]; then - echo '{"success":true,"message":"Rolled back to last snapshot"}' - else - echo "{\"success\":false,\"message\":\"Rollback failed\",\"error\":\"$result\"}" - fi - else - echo '{"success":false,"message":"Recovery system not available"}' - fi - ;; - - validateProfile) - read -r input - profile=$(echo "$input" | jsonfilter -e '@.profile') - /usr/sbin/secubox-profile validate "$profile" - ;; - - runDiagnostics) - read -r input - target=$(echo "$input" | jsonfilter -e '@.target') - /usr/sbin/secubox-diagnostics run "${target:-all}" - ;; - - getHealth) - /usr/sbin/secubox-core health - ;; - - get_network_health) - # Network health monitoring - detects CRC errors, link flapping - DMESG_LINES=500 - FLAP_THRESHOLD=5 - CRC_THRESHOLD=10 - - json_init - json_add_string "timestamp" "$(date -Iseconds)" - json_add_object "interfaces" - - overall="healthy" - critical_count=0 - warning_count=0 - - for iface_path in /sys/class/net/eth* /sys/class/net/wan* /sys/class/net/lan*; do - [ -d "$iface_path" ] || continue - [ -d "$iface_path/device" ] || continue - iface=$(basename "$iface_path") - - current_state=$(cat "$iface_path/operstate" 2>/dev/null || echo "unknown") - crc_count=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface.*crc error" 2>/dev/null) - crc_count=${crc_count:-0} - link_up=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Up" 2>/dev/null) - link_up=${link_up:-0} - link_down=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Down" 2>/dev/null) - link_down=${link_down:-0} - link_changes=$((link_up + link_down)) - - status="ok" - issues="" - - if [ "$crc_count" -ge "$CRC_THRESHOLD" ]; then - status="critical" - issues="CRC errors ($crc_count)" - critical_count=$((critical_count + 1)) - fi - - if [ "$link_changes" -ge "$FLAP_THRESHOLD" ]; then - [ "$status" = "ok" ] && status="warning" - [ -n "$issues" ] && issues="$issues; " - issues="${issues}Link flapping ($link_changes changes)" - warning_count=$((warning_count + 1)) - fi - - rx_errors=$(cat "$iface_path/statistics/rx_errors" 2>/dev/null || echo 0) - tx_errors=$(cat "$iface_path/statistics/tx_errors" 2>/dev/null || echo 0) - - json_add_object "$iface" - json_add_string "status" "$status" - json_add_string "state" "$current_state" - json_add_int "crc_errors" "$crc_count" - json_add_int "link_changes" "$link_changes" - json_add_int "rx_errors" "$rx_errors" - json_add_int "tx_errors" "$tx_errors" - json_add_string "issues" "$issues" - json_close_object - done - - json_close_object - - if [ "$critical_count" -gt 0 ]; then - overall="critical" - elif [ "$warning_count" -gt 0 ]; then - overall="warning" - fi - - json_add_string "overall" "$overall" - json_add_int "critical_interfaces" "$critical_count" - json_add_int "warning_interfaces" "$warning_count" - - if [ "$overall" != "healthy" ]; then - json_add_array "recommendations" - [ "$critical_count" -gt 0 ] && json_add_string "" "Check/replace Ethernet cables" - [ "$critical_count" -gt 0 ] && json_add_string "" "Try different port on switch/modem" - [ "$warning_count" -gt 0 ] && json_add_string "" "Monitor link stability" - json_close_array - fi - - json_dump - ;; - - get_vital_services) - # Vital services monitoring for web hosting and remote management - json_init - json_add_string "timestamp" "$(date -Iseconds)" - - # Helper function to check service - check_service() { - local name="$1" - local category="$2" - local check_type="$3" - local check_value="$4" - local description="$5" - local critical="$6" - - local status="unknown" - local details="" - - case "$check_type" in - process) - if pgrep -f "$check_value" >/dev/null 2>&1; then - status="running" - else - status="stopped" - fi - ;; - port) - if netstat -tln 2>/dev/null | grep -q ":${check_value} "; then - status="running" - details="Port $check_value listening" - else - status="stopped" - details="Port $check_value not listening" - fi - ;; - init) - if [ -f "/etc/init.d/$check_value" ]; then - if /etc/init.d/$check_value enabled 2>/dev/null; then - if /etc/init.d/$check_value running 2>/dev/null; then - status="running" - else - status="stopped" - fi - else - status="disabled" - fi - else - status="not_installed" - fi - ;; - lxc) - if lxc-info -n "$check_value" -s 2>/dev/null | grep -q "RUNNING"; then - status="running" - elif lxc-info -n "$check_value" 2>/dev/null | grep -q "State"; then - status="stopped" - else - status="not_installed" - fi - ;; - file) - if [ -f "$check_value" ]; then - status="present" - else - status="missing" - fi - ;; - esac - - json_add_object "" - json_add_string "name" "$name" - json_add_string "category" "$category" - json_add_string "status" "$status" - json_add_string "description" "$description" - json_add_boolean "critical" "${critical:-0}" - [ -n "$details" ] && json_add_string "details" "$details" - json_close_object - } - - # Core Infrastructure Services - json_add_array "core" - check_service "SSH" "remote" "port" "22" "Remote shell access" 1 - check_service "HTTPS Admin" "remote" "port" "8444" "LuCI admin interface" 1 - check_service "DNS" "network" "port" "53" "Domain name resolution" 1 - check_service "DHCP" "network" "process" "dnsmasq" "IP address assignment" 1 - check_service "Firewall" "security" "process" "fw4" "Network firewall" 1 - json_close_array - - # Security Services - json_add_array "security" - check_service "CrowdSec" "security" "process" "crowdsec" "Intrusion prevention" 1 - check_service "CrowdSec Bouncer" "security" "process" "crowdsec-firewall-bouncer" "Firewall bouncer" 1 - check_service "Tor" "privacy" "init" "tor" "Anonymous routing" 0 - json_close_array - - # Web Publishing Services - json_add_array "publishers" - check_service "HAProxy" "proxy" "lxc" "haproxy" "Load balancer & reverse proxy" 1 - check_service "HexoJS" "cms" "lxc" "hexojs" "Static blog generator" 0 - check_service "Gitea" "devops" "lxc" "gitea" "Git repository hosting" 0 - check_service "Streamlit" "app" "lxc" "streamlit" "Python web apps" 0 - json_close_array - - # Media & App Services - json_add_array "apps" - check_service "Lyrion" "media" "lxc" "lyrion" "Music streaming server" 0 - check_service "MagicMirror" "display" "lxc" "magicmirror2" "Smart mirror display" 0 - check_service "PicoBrew" "app" "lxc" "picobrew" "Brewing automation" 0 - json_close_array - - # Monitoring Services - json_add_array "monitoring" - check_service "Netifyd" "monitoring" "process" "netifyd" "Network intelligence" 0 - check_service "Syslog-ng" "logging" "process" "syslog-ng" "System logging" 1 - json_close_array - - # Calculate summary - json_add_object "summary" - total=0 - running=0 - stopped=0 - critical_down=0 - - for svc in /etc/init.d/*; do - [ -x "$svc" ] || continue - total=$((total + 1)) - done - - # Count running LXC containers - lxc_running=$(lxc-ls --running 2>/dev/null | wc -w) - lxc_total=$(lxc-ls 2>/dev/null | wc -w) - - json_add_int "init_services" "$total" - json_add_int "lxc_running" "$lxc_running" - json_add_int "lxc_total" "$lxc_total" - json_close_object - - json_dump - ;; - - get_full_health_report) - # Combined health report: network + services + system - json_init - json_add_string "timestamp" "$(date -Iseconds)" - json_add_string "hostname" "$(uci get system.@system[0].hostname 2>/dev/null || hostname)" - - # System info - json_add_object "system" - json_add_int "uptime" "$(cut -d. -f1 /proc/uptime)" - json_add_string "load" "$(cut -d' ' -f1-3 /proc/loadavg)" - - mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo) - mem_avail=$(awk '/MemAvailable/ {print $2}' /proc/meminfo) - mem_avail=${mem_avail:-0} - mem_used=$((mem_total - mem_avail)) - mem_pct=$((mem_used * 100 / mem_total)) - json_add_int "memory_percent" "$mem_pct" - - disk_pct=$(df / | tail -1 | awk '{print $5}' | tr -d '%') - json_add_int "disk_percent" "${disk_pct:-0}" - json_close_object - - # Network Health Summary - json_add_object "network" - net_overall="healthy" - net_issues=0 - - for iface_path in /sys/class/net/eth* /sys/class/net/wan*; do - [ -d "$iface_path" ] || continue - [ -d "$iface_path/device" ] || continue - iface=$(basename "$iface_path") - - crc=$(dmesg | tail -n 500 | grep -c "$iface.*crc error" 2>/dev/null) - crc=${crc:-0} - flap=$(dmesg | tail -n 500 | grep -c "$iface: Link is" 2>/dev/null) - flap=${flap:-0} - - if [ "$crc" -ge 10 ] || [ "$flap" -ge 10 ]; then - net_overall="critical" - net_issues=$((net_issues + 1)) - json_add_object "$iface" - json_add_string "status" "critical" - json_add_int "crc_errors" "$crc" - json_add_int "link_changes" "$flap" - json_close_object - fi - done - - json_add_string "overall" "$net_overall" - json_add_int "issues" "$net_issues" - json_close_object - - # Critical Services Status - json_add_object "services" - svc_ok=0 - svc_down=0 - - # Check critical services - for svc in sshd dropbear dnsmasq haproxy crowdsec; do - if pgrep -x "$svc" >/dev/null 2>&1 || pgrep -f "$svc" >/dev/null 2>&1; then - svc_ok=$((svc_ok + 1)) - else - # Check if it's supposed to be running - if [ -f "/etc/init.d/$svc" ] && /etc/init.d/$svc enabled 2>/dev/null; then - svc_down=$((svc_down + 1)) - fi - fi - done - - # Check LXC containers - lxc_expected=$(lxc-ls 2>/dev/null | wc -w) - lxc_running=$(lxc-ls --running 2>/dev/null | wc -w) - - json_add_int "services_ok" "$svc_ok" - json_add_int "services_down" "$svc_down" - json_add_int "containers_running" "$lxc_running" - json_add_int "containers_total" "$lxc_expected" - - if [ "$svc_down" -gt 0 ]; then - json_add_string "overall" "warning" - else - json_add_string "overall" "healthy" - fi - json_close_object - - # Overall health score - health_score=100 - [ "$net_overall" = "critical" ] && health_score=$((health_score - 30)) - [ "$svc_down" -gt 0 ] && health_score=$((health_score - (svc_down * 10))) - [ "$mem_pct" -gt 90 ] && health_score=$((health_score - 10)) - [ "${disk_pct:-0}" -gt 90 ] && health_score=$((health_score - 10)) - - json_add_int "health_score" "$health_score" - - if [ "$health_score" -ge 80 ]; then - json_add_string "overall_status" "healthy" - elif [ "$health_score" -ge 50 ]; then - json_add_string "overall_status" "warning" - else - json_add_string "overall_status" "critical" - fi - - # Alerts - json_add_array "alerts" - [ "$net_overall" = "critical" ] && { - json_add_object "" - json_add_string "level" "critical" - json_add_string "message" "Network interface issues detected - check cables" - json_close_object - } - [ "$svc_down" -gt 0 ] && { - json_add_object "" - json_add_string "level" "warning" - json_add_string "message" "$svc_down critical service(s) not running" - json_close_object - } - [ "$mem_pct" -gt 90 ] && { - json_add_object "" - json_add_string "level" "warning" - json_add_string "message" "High memory usage: ${mem_pct}%" - json_close_object - } - json_close_array - - json_dump - ;; - - get_dashboard_data) - # Return dashboard summary data (OPTIMIZED - no slow appstore call) - json_init - - # Fast module counting: count installed secubox packages - # This avoids the slow secubox-appstore list --json call - total_modules=0 - running_modules=0 - - # Count from catalog (fast - just count JSON entries) - CATALOG_FILE="/usr/share/secubox/catalog.json" - if [ -f "$CATALOG_FILE" ]; then - total_modules=$(jsonfilter -i "$CATALOG_FILE" -e '@.plugins[*].id' 2>/dev/null | wc -l) - fi - [ -z "$total_modules" ] || [ "$total_modules" -eq 0 ] && total_modules=0 - - # Count running LXC containers (fast) - lxc_running=$(lxc-ls --running 2>/dev/null | wc -w) - lxc_running=${lxc_running:-0} - - # Count running init services that are SecuBox-related (fast) - svc_running=0 - for svc in crowdsec tor haproxy netifyd syslog-ng; do - if pgrep -f "$svc" >/dev/null 2>&1; then - svc_running=$((svc_running + 1)) - fi - done - - running_modules=$((lxc_running + svc_running)) - - # Get system info - uptime_seconds=$(cut -d' ' -f1 /proc/uptime | cut -d'.' -f1) - load_avg=$(cut -d' ' -f1-3 /proc/loadavg) - - # Build response - json_add_object "status" - json_add_string "version" "0.8.0" - json_add_int "uptime" "$uptime_seconds" - json_add_string "load" "$load_avg" - json_close_object - - json_add_object "counts" - json_add_int "total" "$total_modules" - json_add_int "running" "$running_modules" - json_add_int "lxc_running" "$lxc_running" - json_add_int "services_running" "$svc_running" - json_close_object - - json_dump - ;; - - get_system_health) - # Return system health metrics - json_init - - uptime_seconds=$(cut -d' ' -f1 /proc/uptime | cut -d'.' -f1) - - # CPU usage (simple average from /proc/loadavg) - load1=$(awk '{print $1}' /proc/loadavg) - cpu_count=$(grep -c ^processor /proc/cpuinfo) - if [ "$cpu_count" -le 0 ]; then - cpu_count=1 - fi - cpu_percent=$(awk -v load="$load1" -v cores="$cpu_count" 'BEGIN {printf "%.0f", (load / cores) * 100}') - - # Memory - mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}') - mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}') - mem_free=${mem_free:-0} - if [ "$mem_total" -le 0 ]; then - mem_total=1 - fi - mem_used=$((mem_total - mem_free)) - mem_percent=$(awk -v used="$mem_used" -v total="$mem_total" 'BEGIN {printf "%.0f", (used / total) * 100}') - - # Disk - disk_info=$(df / | tail -1) - disk_total=$(echo "$disk_info" | awk '{print $2}') - disk_used=$(echo "$disk_info" | awk '{print $3}') - disk_used=${disk_used:-0} - if [ -z "$disk_total" ] || [ "$disk_total" -le 0 ]; then - disk_total=0 - disk_used=0 - fi - disk_free=$((disk_total - disk_used)) - disk_percent=$(echo "$disk_info" | awk '{print $5}' | tr -d '%') - [ -n "$disk_percent" ] || disk_percent=0 - - # Calculate overall score (100 - average of usage percentages) - overall_score=$(awk -v c="$cpu_percent" -v m="$mem_percent" -v d="$disk_percent" 'BEGIN {printf "%.0f", 100 - ((c + m + d) / 3)}') - - json_add_int "uptime" "$uptime_seconds" - - if [ "$overall_score" -gt 70 ]; then - overall_status="healthy" - else - overall_status="warning" - fi - - json_add_object "overall" - json_add_int "score" "$overall_score" - json_add_string "status" "$overall_status" - json_close_object - - json_add_object "cpu" - json_add_int "usage_percent" "$cpu_percent" - json_add_string "load" "$(cut -d' ' -f1-3 /proc/loadavg)" - json_add_int "count" "$cpu_count" - json_close_object - - json_add_object "memory" - json_add_int "total_kb" "$mem_total" - json_add_int "used_kb" "$mem_used" - json_add_int "free_kb" "$mem_free" - json_add_int "usage_percent" "$mem_percent" - json_close_object - - json_add_object "disk" - json_add_int "total_kb" "$disk_total" - json_add_int "used_kb" "$disk_used" - json_add_int "free_kb" "$disk_free" - json_add_int "usage_percent" "$disk_percent" - json_close_object - - json_dump - ;; - - get_public_ips) - # Return public IPv4 and IPv6 addresses (cached to avoid blocking) - json_init - - cache_v4="/tmp/secubox_public_ipv4" - cache_v6="/tmp/secubox_public_ipv6" - - # Use cached values if available - ipv4="" - ipv6="" - [ -f "$cache_v4" ] && ipv4=$(cat "$cache_v4" 2>/dev/null) - [ -f "$cache_v6" ] && ipv6=$(cat "$cache_v6" 2>/dev/null) - - # Fallback: get from interface (fast, no network) - if [ -z "$ipv4" ]; then - ipv4=$(ip -4 addr show scope global 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) - fi - if [ -z "$ipv6" ]; then - ipv6=$(ip -6 addr show scope global 2>/dev/null | grep -oE '[0-9a-f:]+::[0-9a-f:]+' | head -1) - fi - - json_add_string "ipv4" "${ipv4:-N/A}" - json_add_string "ipv6" "${ipv6:-N/A}" - json_add_boolean "ipv4_available" "$([ -n "$ipv4" ] && [ "$ipv4" != "N/A" ] && echo 1 || echo 0)" - json_add_boolean "ipv6_available" "$([ -n "$ipv6" ] && [ "$ipv6" != "N/A" ] && echo 1 || echo 0)" - - json_dump - ;; - - refresh_public_ips) - # Refresh public IPs in background - json_init - ( - for svc in "https://api.ipify.org" "https://ipv4.icanhazip.com"; do - ipv4=$(curl -4 -s --connect-timeout 2 --max-time 3 "$svc" 2>/dev/null | tr -d '\n') - [ -n "$ipv4" ] && { echo "$ipv4" > /tmp/secubox_public_ipv4; break; } - done - for svc in "https://api64.ipify.org" "https://ipv6.icanhazip.com"; do - ipv6=$(curl -6 -s --connect-timeout 2 --max-time 3 "$svc" 2>/dev/null | tr -d '\n') - [ -n "$ipv6" ] && { echo "$ipv6" > /tmp/secubox_public_ipv6; break; } - done - ) & - json_add_boolean "success" 1 - json_dump - ;; - - get_alerts) - # Return system alerts - json_init - json_add_array "alerts" - - # Check for common issues - # 1. High memory usage - mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}') - mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}') - mem_percent=$(awk "BEGIN {printf \"%.0f\", (($mem_total - $mem_free) / $mem_total) * 100}") - if [ "$mem_percent" -gt 90 ]; then - json_add_object "" - json_add_string "id" "high_memory" - json_add_string "level" "warning" - json_add_string "title" "High Memory Usage" - json_add_string "message" "Memory usage is at ${mem_percent}%" - json_add_int "timestamp" "$(date +%s)" - json_close_object - fi - - # 2. High disk usage - disk_percent=$(df / | tail -1 | awk '{print $5}' | tr -d '%') - if [ "$disk_percent" -gt 85 ]; then - json_add_object "" - json_add_string "id" "high_disk" - json_add_string "level" "warning" - json_add_string "title" "High Disk Usage" - json_add_string "message" "Disk usage is at ${disk_percent}%" - json_add_int "timestamp" "$(date +%s)" - json_close_object - fi - - json_close_array - json_dump - ;; - - quick_action) - read -r input - action=$(echo "$input" | jsonfilter -e '@.action') - json_init - - case "$action" in - restart_services) - # Restart all SecuBox services - for svc in haproxy crowdsec tor netifyd; do - if [ -x "/etc/init.d/$svc" ]; then - /etc/init.d/$svc restart 2>/dev/null & - elif [ -x "/usr/sbin/${svc}ctl" ]; then - /usr/sbin/${svc}ctl restart 2>/dev/null & - fi - done - json_add_boolean "success" 1 - json_add_string "message" "Services restart initiated" - ;; - restart_*) - # Restart specific service: restart_haproxy, restart_crowdsec, etc. - svc_name="${action#restart_}" - if [ -x "/etc/init.d/$svc_name" ]; then - /etc/init.d/$svc_name restart 2>/dev/null - json_add_boolean "success" 1 - json_add_string "message" "Service $svc_name restarted" - elif [ -x "/usr/sbin/${svc_name}ctl" ]; then - /usr/sbin/${svc_name}ctl restart 2>/dev/null - json_add_boolean "success" 1 - json_add_string "message" "Service $svc_name restarted" - else - json_add_boolean "success" 0 - json_add_string "message" "Service $svc_name not found" - fi - ;; - update_packages) - opkg update 2>/dev/null & - json_add_boolean "success" 1 - json_add_string "message" "Package update initiated" - ;; - view_logs) - # Return recent system logs - json_add_boolean "success" 1 - json_add_string "redirect" "/cgi-bin/luci/admin/status/syslog" - ;; - export_config) - # Export configuration - if [ -x "/usr/sbin/secubox-recovery" ]; then - snapshot_name="export-$(date +%Y%m%d-%H%M%S)" - /usr/sbin/secubox-recovery snapshot "$snapshot_name" 2>/dev/null - json_add_boolean "success" 1 - json_add_string "message" "Configuration exported as $snapshot_name" - else - json_add_boolean "success" 0 - json_add_string "message" "Recovery system not available" - fi - ;; - *) - json_add_boolean "success" 0 - json_add_string "message" "Unknown action: $action" - ;; - esac - - json_dump - ;; - - getLogs) - read -r input - service=$(echo "$input" | jsonfilter -e '@.service') - lines=$(echo "$input" | jsonfilter -e '@.lines') - - json_init - json_add_array "logs" - if [ -n "$service" ]; then - logread -e "$service" | tail -n "${lines:-100}" | while read -r line; do - json_add_string "" "$line" - done - else - logread | tail -n "${lines:-100}" | while read -r line; do - json_add_string "" "$line" - done - fi - json_close_array - json_dump - ;; - - createSnapshot) - read -r input - name=$(echo "$input" | jsonfilter -e '@.name') - /usr/sbin/secubox-recovery snapshot "${name:-auto-$(date +%Y%m%d-%H%M%S)}" - ;; - - listSnapshots) - /usr/sbin/secubox-recovery list --json - ;; - - restoreSnapshot) - read -r input - snapshot=$(echo "$input" | jsonfilter -e '@.snapshot') - /usr/sbin/secubox-recovery rollback "$snapshot" - ;; - - get_appstore_apps) - # Return apps from catalog with installation status - # Use secubox-appstore which properly detects installed packages - CATALOG_FILE="/usr/share/secubox/catalog.json" - - if [ -f "$CATALOG_FILE" ]; then - # Get modules list with installation status from secubox-appstore - MODULES_JSON=$(/usr/sbin/secubox-appstore list --json 2>/dev/null) - - if [ -n "$MODULES_JSON" ]; then - # Merge installation status from modules into catalog apps - jq --argjson modules "$MODULES_JSON" ' - { - apps: [.plugins[] | . as $app | - ($modules.modules // [] | map(select(.id == $app.id or .name == $app.id)) | first) as $mod | - $app + { - installed: (if $mod then ($mod.installed // false) else false end), - enabled: (if $mod then ($mod.enabled // false) else false end), - status: (if $mod then ($mod.status // "unknown") else "not_installed" end) - } - ], - categories: .categories - } - ' "$CATALOG_FILE" - else - # Fallback: just return catalog without status - jq '{apps: .plugins, categories: .categories}' "$CATALOG_FILE" - fi - else - echo '{"apps":[],"categories":{}}' - fi - ;; - - list_apps) - # Returns apps from catalog and adds apps from manifests with wizards - CATALOG_FILE="/usr/share/secubox/catalog.json" - PLUGINS_DIR="/usr/share/secubox/plugins" - - if [ -f "$CATALOG_FILE" ]; then - # Get base apps from catalog - APPS_JSON=$(jq '.plugins' "$CATALOG_FILE") - else - APPS_JSON='[]' - fi - - # Scan plugin manifests and add apps with wizards - for plugin_dir in "$PLUGINS_DIR"/*; do - [ -d "$plugin_dir" ] || continue - manifest="$plugin_dir/manifest.json" - [ -f "$manifest" ] || continue - - # Get app ID from manifest - app_id=$(jq -r '.id // empty' "$manifest" 2>/dev/null) - [ -n "$app_id" ] || continue - - # Check if manifest has wizard configuration - has_wizard=$(jq -e '.wizard.fields | length > 0' "$manifest" >/dev/null 2>&1 && echo "true" || echo "false") - - if [ "$has_wizard" = "true" ]; then - # Check if app already exists in catalog - app_exists=$(echo "$APPS_JSON" | jq --arg id "$app_id" 'map(select(.id == $id)) | length') - - if [ "$app_exists" -gt 0 ]; then - # Update existing catalog app with has_wizard flag - APPS_JSON=$(echo "$APPS_JSON" | jq --arg id "$app_id" \ - 'map(if .id == $id then . + {has_wizard: true} else . end)') - else - # Add new app from manifest - app_data=$(jq '{ - id: .id, - name: .name, - description: .description, - version: .version, - icon: "📦", - has_wizard: true, - state: "available" - }' "$manifest") - APPS_JSON=$(echo "$APPS_JSON" | jq --argjson app "$app_data" '. + [$app]') - fi - fi - done - - # Return modified apps list - if [ -f "$CATALOG_FILE" ]; then - jq -n --argjson apps "$APPS_JSON" --argjson cats "$(jq '.categories // {}' "$CATALOG_FILE")" \ - '{apps: $apps, categories: $cats}' - else - jq -n --argjson apps "$APPS_JSON" '{apps: $apps, categories: {}}' - fi - ;; - - get_appstore_app) - # Get single app details from catalog - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - - CATALOG_DIR="/usr/share/secubox/plugins/catalog" - CATALOG_FILE="$CATALOG_DIR/${app_id}.json" - - if [ -f "$CATALOG_FILE" ]; then - # Add installed status - json_init - cat "$CATALOG_FILE" | jsonfilter -e '@' - - # Check if installed - pkg=$(jsonfilter -i "$CATALOG_FILE" -e '@.packages.required[0]') - if [ -n "$pkg" ] && opkg list-installed | grep -q "^$pkg "; then - echo ',"installed":true' - else - echo ',"installed":false' - fi - else - json_init - json_add_boolean "error" true - json_add_string "message" "App not found: $app_id" - json_dump - fi - ;; - - install_appstore_app) - # Install app using secubox-appstore CLI - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - - # Use secubox-appstore for installation - if /usr/sbin/secubox-appstore install "$app_id" >/dev/null 2>&1; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "App installed successfully" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "Installation failed" - json_add_string "details" "Check system logs for more information" - json_dump - fi - ;; - - remove_appstore_app) - # Remove app using secubox-appstore CLI - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - - # Use secubox-appstore for removal - if /usr/sbin/secubox-appstore remove "$app_id" >/dev/null 2>&1; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "App removed successfully" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "Removal failed" - json_add_string "details" "Check system logs for more information" - json_dump - fi - ;; - - get_catalog_sources) - # Return configured catalog sources from UCI (OPTIMIZED) - CONFIG_NAME="secubox-appstore" - METADATA_FILE="/var/lib/secubox/catalog-metadata.json" - - _add_default_source() { - local name="$1" - local type="$2" - local url="$3" - local path="$4" - local priority="$5" - - json_add_object "" - json_add_string "name" "$name" - json_add_boolean "enabled" 1 - json_add_string "type" "$type" - [ -n "$url" ] && json_add_string "url" "$url" - [ -n "$path" ] && json_add_string "path" "$path" - json_add_int "priority" "$priority" - [ "$name" = "embedded" ] && json_add_boolean "active" 1 || json_add_boolean "active" 0 - json_add_string "status" "default" - json_add_string "last_success" "" - json_close_object - } - - # Fast check: if UCI config doesn't exist, return sensible defaults - if [ ! -f "/etc/config/$CONFIG_NAME" ]; then - json_init - json_add_array "sources" - _add_default_source "github" "remote" "https://raw.githubusercontent.com/CyberMind-FR/secubox-openwrt/refs/heads/master/package/secubox/secubox-core/root/usr/share/secubox/catalog.json" "" 1 - _add_default_source "embedded" "embedded" "" "/usr/share/secubox/catalog.json" 999 - json_close_array - json_add_boolean "defaults" true - json_add_string "message" "Catalog config missing, using built-in defaults" - json_dump - exit 0 - fi - - json_init - json_add_array "sources" - - # Parse UCI config sources - . /lib/functions.sh - - # OPTIMIZATION: Add timeout guard for config_load, but still load - if ! timeout 5 sh -c ". /lib/functions.sh; config_load $CONFIG_NAME >/dev/null 2>&1" 2>/dev/null; then - # Config load failed or timed out, return defaults - _add_default_source "github" "remote" "https://raw.githubusercontent.com/CyberMind-FR/secubox-openwrt/refs/heads/master/package/secubox/secubox-core/root/usr/share/secubox/catalog.json" "" 1 - _add_default_source "embedded" "embedded" "" "/usr/share/secubox/catalog.json" 999 - json_close_array - json_add_boolean "defaults" true - json_add_string "message" "Catalog config unreadable, using defaults" - json_dump - exit 0 - fi - - # Load config in current shell for config_foreach usage - config_load "$CONFIG_NAME" - - # Get active source once (optimization) - local active_source="" - if [ -f "$METADATA_FILE" ]; then - active_source=$(timeout 2 jsonfilter -i "$METADATA_FILE" -e '@.active_source' 2>/dev/null || echo "") - fi - - # Cache metadata content to avoid multiple reads - local metadata_content="" - if [ -f "$METADATA_FILE" ]; then - metadata_content=$(timeout 2 cat "$METADATA_FILE" 2>/dev/null || echo "{}") - fi - - local sources_count=0 - local defaults_used=0 - local defaults_message="" - - _add_source_info() { - local section="$1" - local enabled type url path priority - - config_get_bool enabled "$section" enabled 0 - config_get type "$section" type - config_get url "$section" url - config_get path "$section" path - config_get priority "$section" priority 999 - - json_add_object "" - json_add_string "name" "$section" - json_add_boolean "enabled" "$enabled" - json_add_string "type" "$type" - [ -n "$url" ] && json_add_string "url" "$url" - [ -n "$path" ] && json_add_string "path" "$path" - json_add_int "priority" "$priority" - json_add_boolean "active" "$([ "$section" = "$active_source" ] && echo 1 || echo 0)" - - # Get status from cached metadata (optimization with timeout) - if [ -n "$metadata_content" ]; then - local status=$(echo "$metadata_content" | timeout 1 jsonfilter -e "@.sources['$section'].status" 2>/dev/null || echo "") - local last_success=$(echo "$metadata_content" | timeout 1 jsonfilter -e "@.sources['$section'].last_success" 2>/dev/null || echo "") - [ -n "$status" ] && json_add_string "status" "$status" - [ -n "$last_success" ] && json_add_string "last_success" "$last_success" - fi - json_close_object - - sources_count=$((sources_count + 1)) - } - - config_foreach _add_source_info source - - # If config exists but contains no sources, fall back to defaults - if [ "$sources_count" -eq 0 ]; then - _add_default_source "github" "remote" "https://raw.githubusercontent.com/CyberMind-FR/secubox-openwrt/refs/heads/master/package/secubox/secubox-core/root/usr/share/secubox/catalog.json" "" 1 - _add_default_source "embedded" "embedded" "" "/usr/share/secubox/catalog.json" 999 - defaults_used=1 - defaults_message="Catalog config empty, using built-in defaults" - fi - - json_close_array - if [ "$defaults_used" -eq 1 ]; then - json_add_boolean "defaults" true - json_add_string "message" "$defaults_message" - fi - json_dump - ;; - - set_catalog_source) - # Set force_source in UCI config - read -r input - source=$(echo "$input" | jsonfilter -e '@.source') - - CONFIG_NAME="secubox-appstore" - SECTION_NAME=$(uci -q show "$CONFIG_NAME" | grep "=settings" | head -n1 | cut -d'.' -f2 | cut -d'=' -f1) - [ -z "$SECTION_NAME" ] && SECTION_NAME="main" - - if [ -n "$source" ]; then - if uci set "${CONFIG_NAME}.${SECTION_NAME}.force_source=$source" >/dev/null 2>&1 && \ - uci commit "$CONFIG_NAME" >/dev/null 2>&1; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Catalog source set to: $source" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "Failed to update UCI config" - json_dump - fi - - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "No source specified" - json_dump - fi - ;; - - sync_catalog) - # Trigger catalog sync - read -r input - source=$(echo "$input" | jsonfilter -e '@.source') - - # Call secubox-catalog-sync (with or without source) - if /usr/sbin/secubox-appstore sync ${source:+"$source"} 2>&1; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Catalog synced successfully" - [ -n "$source" ] && json_add_string "source" "$source" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "Sync failed" - json_dump - fi - ;; - - check_updates) - # Check for available updates - /usr/sbin/secubox-appstore check-updates --json - ;; - - get_app_versions) - # Get version info for specific app - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - - CATALOG_FILE="/usr/share/secubox/catalog.json" - METADATA_FILE="/var/lib/secubox/catalog-metadata.json" - - json_init - - # Get catalog version - if [ -f "$CATALOG_FILE" ]; then - pkg_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].pkg_version" 2>/dev/null) - app_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].app_version" 2>/dev/null) - [ -n "$pkg_version" ] && json_add_string "catalog_pkg_version" "$pkg_version" - [ -n "$app_version" ] && json_add_string "catalog_app_version" "$app_version" - fi - - # Get installed version - pkg_name=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].packages.required[0]" 2>/dev/null) - if [ -n "$pkg_name" ]; then - installed_version=$(opkg list-installed | grep "^$pkg_name " | awk '{print $3}') - [ -n "$installed_version" ] && json_add_string "installed_version" "$installed_version" - fi - - # Get metadata version info - if [ -f "$METADATA_FILE" ]; then - metadata_version=$(jsonfilter -i "$METADATA_FILE" -e "@.installed_apps['$app_id'].installed_version" 2>/dev/null) - update_available=$(jsonfilter -i "$METADATA_FILE" -e "@.installed_apps['$app_id'].update_available" 2>/dev/null) - [ -n "$update_available" ] && json_add_boolean "update_available" "$update_available" - fi - - json_add_string "app_id" "$app_id" - json_dump - ;; - - get_changelog) - # Get changelog for app - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - from_version=$(echo "$input" | jsonfilter -e '@.from_version') - to_version=$(echo "$input" | jsonfilter -e '@.to_version') - - # Use secubox-appstore CLI - /usr/sbin/secubox-appstore changelog "$app_id" ${from_version:+"$from_version"} ${to_version:+"$to_version"} - ;; - - get_widget_data) - # Get real-time widget data for app - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - - CATALOG_FILE="/usr/share/secubox/catalog.json" - - json_init - json_add_string "app_id" "$app_id" - json_add_int "timestamp" "$(date +%s)" - - # Get widget configuration from catalog - if [ -f "$CATALOG_FILE" ]; then - widget_enabled=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].widget.enabled" 2>/dev/null) - - if [ "$widget_enabled" = "true" ]; then - json_add_boolean "widget_enabled" true - - # Get version information from catalog - catalog_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].version" 2>/dev/null) - pkg_version=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].pkg_version" 2>/dev/null) - - [ -n "$catalog_version" ] && json_add_string "catalog_version" "$catalog_version" - [ -n "$pkg_version" ] && json_add_string "pkg_version" "$pkg_version" - - # Get installed version from opkg - installed_version="" - if [ -n "$pkg_version" ]; then - package_name=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].packages.required[0]" 2>/dev/null) - if [ -n "$package_name" ]; then - installed_version=$(opkg info "$package_name" 2>/dev/null | awk '/^Version:/ {print $2}') - fi - fi - [ -n "$installed_version" ] && json_add_string "installed_version" "$installed_version" - - # Check if installed and running - json_add_boolean "installed" false - json_add_boolean "running" false - json_add_string "status" "unknown" - - # Check installation status via ubus - if command -v ubus >/dev/null 2>&1; then - modules_json=$(ubus call luci.secubox get_modules 2>/dev/null) - if [ -n "$modules_json" ]; then - package_name=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[@.id='$app_id'].packages.required[0]" 2>/dev/null) - if [ -n "$package_name" ]; then - module_enabled=$(echo "$modules_json" | jsonfilter -e "@.modules['$package_name'].enabled" 2>/dev/null) - module_running=$(echo "$modules_json" | jsonfilter -e "@.modules['$package_name'].running" 2>/dev/null) - - [ "$module_enabled" = "true" ] && json_add_boolean "installed" true - [ "$module_running" = "true" ] && json_add_boolean "running" true - - # Set status based on state - if [ "$module_running" = "true" ]; then - json_add_string "status" "running" - elif [ "$module_enabled" = "true" ]; then - json_add_string "status" "stopped" - else - json_add_string "status" "not_installed" - fi - fi - fi - fi - - # Get metrics from catalog definition - # This would call app-specific data sources (ubus, files, etc.) - # For now, return placeholder structure - json_add_array "metrics" - json_close_array - else - json_add_boolean "widget_enabled" false - fi - else - json_add_boolean "widget_enabled" false - fi - - json_dump - ;; - - # State management methods - get_component_state) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - /usr/sbin/secubox-state get "$component_id" - ;; - - set_component_state) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - new_state=$(echo "$input" | jsonfilter -e '@.new_state') - reason=$(echo "$input" | jsonfilter -e '@.reason') - - result=$(/usr/sbin/secubox-state set "$component_id" "$new_state" "${reason:-manual}") - - json_init - if echo "$result" | grep -q "Success:"; then - json_add_boolean "success" 1 - json_add_string "message" "$result" - json_add_string "component_id" "$component_id" - json_add_string "new_state" "$new_state" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - json_dump - ;; - - get_state_history) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - limit=$(echo "$input" | jsonfilter -e '@.limit') - /usr/sbin/secubox-state history "$component_id" "${limit:-20}" - ;; - - list_components) - read -r input - state_filter=$(echo "$input" | jsonfilter -e '@.state_filter') - type_filter=$(echo "$input" | jsonfilter -e '@.type_filter') - - args="" - [ -n "$state_filter" ] && args="$args --state=$state_filter" - [ -n "$type_filter" ] && args="$args --type=$type_filter" - - /usr/sbin/secubox-state list $args - ;; - - freeze_component) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - reason=$(echo "$input" | jsonfilter -e '@.reason') - - result=$(/usr/sbin/secubox-state freeze "$component_id" "${reason:-manual_freeze}") - - json_init - if echo "$result" | grep -q "Success:"; then - json_add_boolean "success" 1 - json_add_string "message" "$result" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - json_dump - ;; - - clear_error_state) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - - result=$(/usr/sbin/secubox-state clear-error "$component_id") - - json_init - if echo "$result" | grep -q "Success:"; then - json_add_boolean "success" 1 - json_add_string "message" "$result" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - json_dump - ;; - - # Component registry methods - get_component) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - /usr/sbin/secubox-component get "$component_id" - ;; - - list_all_components) - read -r input - type=$(echo "$input" | jsonfilter -e '@.type') - profile=$(echo "$input" | jsonfilter -e '@.profile') - - args="" - [ -n "$type" ] && args="$args --type=$type" - [ -n "$profile" ] && args="$args --profile=$profile" - - /usr/sbin/secubox-component list $args - ;; - - get_component_tree) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - /usr/sbin/secubox-component tree "$component_id" - ;; - - update_component_settings) - read -r input - component_id=$(echo "$input" | jsonfilter -e '@.component_id') - - # Extract settings object from input - # This is simplified - full implementation would parse settings JSON - json_init - json_add_boolean "success" 1 - json_add_string "message" "Settings update functionality available via CLI: secubox-component set-setting" - json_dump - ;; - - sync_component_registry) - result=$(/usr/sbin/secubox-sync-registry sync) - - json_init - if echo "$result" | grep -q "successfully"; then - json_add_boolean "success" 1 - json_add_string "message" "$result" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - json_dump - ;; - - get_wan_access) - /usr/sbin/secubox-wan-access json - ;; - - set_wan_access) - read -r input - enabled=$(echo "$input" | jsonfilter -e '@.enabled' 2>/dev/null) - https_enabled=$(echo "$input" | jsonfilter -e '@.https_enabled' 2>/dev/null) - https_port=$(echo "$input" | jsonfilter -e '@.https_port' 2>/dev/null) - http_enabled=$(echo "$input" | jsonfilter -e '@.http_enabled' 2>/dev/null) - http_port=$(echo "$input" | jsonfilter -e '@.http_port' 2>/dev/null) - ssh_enabled=$(echo "$input" | jsonfilter -e '@.ssh_enabled' 2>/dev/null) - ssh_port=$(echo "$input" | jsonfilter -e '@.ssh_port' 2>/dev/null) - - # Update UCI settings - [ -n "$enabled" ] && uci set secubox.remote.enabled="$enabled" - [ -n "$https_enabled" ] && uci set secubox.remote.https_enabled="$https_enabled" - [ -n "$https_port" ] && uci set secubox.remote.https_port="$https_port" - [ -n "$http_enabled" ] && uci set secubox.remote.http_enabled="$http_enabled" - [ -n "$http_port" ] && uci set secubox.remote.http_port="$http_port" - [ -n "$ssh_enabled" ] && uci set secubox.remote.ssh_enabled="$ssh_enabled" - [ -n "$ssh_port" ] && uci set secubox.remote.ssh_port="$ssh_port" - uci commit secubox - - json_init - json_add_boolean "success" 1 - json_add_string "message" "WAN access settings updated" - json_dump - ;; - - apply_wan_access) - /usr/sbin/secubox-wan-access apply >/dev/null 2>&1 - json_init - json_add_boolean "success" 1 - json_add_string "message" "WAN access rules applied" - json_dump - ;; - - get_services) - # Discover listening services from netstat - # Save to temp file to avoid subshell issues with json - TMP_SERVICES="/tmp/services_$$" - netstat -tlnp 2>/dev/null | grep LISTEN | awk '{ - split($4, a, ":") - port = a[length(a)] - if (!seen[port]++) { - split($7, p, "/") - proc = p[2] - if (proc == "") proc = "unknown" - print port, $4, proc - } - }' | sort -n -u > "$TMP_SERVICES" - - json_init - json_add_array "services" - - while read port local proc; do - addr=$(echo "$local" | sed 's/:[^:]*$//') - name=""; icon=""; category="other"; path="" - - # 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 process name if port didn't match - if [ -z "$name" ]; then - 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 - - external=0 - case "$addr" in 0.0.0.0|::) external=1 ;; 127.0.0.1|::1) ;; *) external=1 ;; esac - - json_add_object "" - json_add_int "port" "$port" - json_add_string "address" "$addr" - json_add_string "name" "$name" - json_add_string "icon" "$icon" - json_add_string "process" "$proc" - json_add_string "category" "$category" - json_add_boolean "external" "$external" - [ -n "$path" ] && [ "$external" = "1" ] && json_add_string "url" "$path" - json_close_object - done < "$TMP_SERVICES" - - rm -f "$TMP_SERVICES" - json_close_array - json_dump - ;; - - get_proxy_mode) - json_init - local mode="direct" - local wpad_enabled=0 - - # Check if WPAD PAC file exists and determine mode - if [ -f "/www/wpad/wpad.dat" ]; then - wpad_enabled=1 - if grep -q "SOCKS5.*9050" /www/wpad/wpad.dat 2>/dev/null; then - mode="tor" - elif grep -q "PROXY.*3128" /www/wpad/wpad.dat 2>/dev/null; then - mode="cdn" - elif grep -q "PROXY.*8080" /www/wpad/wpad.dat 2>/dev/null; then - mode="mitmproxy" - fi - fi - - # Check DHCP WPAD option - local dhcp_wpad=$(uci -q get dhcp.lan.dhcp_option | grep -c "252") - - json_add_string "mode" "$mode" - json_add_boolean "wpad_enabled" "$wpad_enabled" - json_add_boolean "dhcp_wpad" "$dhcp_wpad" - json_add_string "pac_url" "http://192.168.255.1/wpad/wpad.dat" - json_dump - ;; - - set_proxy_mode) - read input - json_load "$input" - json_get_var mode mode - - json_init - - mkdir -p /www/wpad - - case "$mode" in - direct) - # Remove PAC file for direct mode - rm -f /www/wpad/wpad.dat - uci -q delete dhcp.lan.dhcp_option - uci commit dhcp - json_add_boolean "success" 1 - json_add_string "message" "Proxy disabled - direct connections" - ;; - cdn) - # CDN cache mode - HTTP through nginx cache - cat > /www/wpad/wpad.dat << 'PACEOF' -function FindProxyForURL(url, host) { - if (isPlainHostName(host) || shExpMatch(host, "*.local") || shExpMatch(host, "*.lan") || - isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || - isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || - isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || - isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) { - return "DIRECT"; - } - if (url.substring(0, 5) == "http:") { - return "PROXY 192.168.255.1:3128; DIRECT"; - } - return "DIRECT"; -} -PACEOF - uci set dhcp.lan.dhcp_option="252,http://192.168.255.1/wpad/wpad.dat" - uci commit dhcp - json_add_boolean "success" 1 - json_add_string "message" "CDN cache mode enabled - HTTP cached" - ;; - tor) - # Tor bypass mode - HTTPS through Tor SOCKS - cat > /www/wpad/wpad.dat << 'PACEOF' -function FindProxyForURL(url, host) { - if (isPlainHostName(host) || shExpMatch(host, "*.local") || shExpMatch(host, "*.lan") || - isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || - isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || - isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || - isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) { - return "DIRECT"; - } - if (url.substring(0, 5) == "http:") { - return "PROXY 192.168.255.1:3128; DIRECT"; - } - if (url.substring(0, 6) == "https:") { - return "SOCKS5 192.168.255.1:9050; DIRECT"; - } - return "DIRECT"; -} -PACEOF - uci set dhcp.lan.dhcp_option="252,http://192.168.255.1/wpad/wpad.dat" - uci commit dhcp - json_add_boolean "success" 1 - json_add_string "message" "Tor bypass mode enabled - HTTPS through Tor" - ;; - mitmproxy) - # mitmproxy mode - all traffic through mitmproxy - cat > /www/wpad/wpad.dat << 'PACEOF' -function FindProxyForURL(url, host) { - if (isPlainHostName(host) || shExpMatch(host, "*.local") || shExpMatch(host, "*.lan") || - isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || - isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || - isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || - isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) { - return "DIRECT"; - } - return "PROXY 192.168.255.1:8080; DIRECT"; -} -PACEOF - uci set dhcp.lan.dhcp_option="252,http://192.168.255.1/wpad/wpad.dat" - uci commit dhcp - json_add_boolean "success" 1 - json_add_string "message" "mitmproxy mode enabled - all traffic inspectable" - ;; - *) - json_add_boolean "success" 0 - json_add_string "error" "Unknown mode: $mode" - ;; - esac - - # Restart dnsmasq to apply DHCP changes - /etc/init.d/dnsmasq restart >/dev/null 2>&1 & - - json_dump - ;; - - # Feed management handlers - list_feeds) - /usr/sbin/secubox-feed-manager list --json - ;; - - add_feed) - read -r input - name=$(echo "$input" | jsonfilter -e '@.name') - url=$(echo "$input" | jsonfilter -e '@.url') - feed_type=$(echo "$input" | jsonfilter -e '@.feed_type') - description=$(echo "$input" | jsonfilter -e '@.description') - - result=$(/usr/sbin/secubox-feed-manager add "$name" "$url" --type "${feed_type:-unpublished}" --description "$description" 2>&1) - if [ $? -eq 0 ]; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Feed added successfully" - json_add_string "name" "$name" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - remove_feed) - read -r input - name=$(echo "$input" | jsonfilter -e '@.name') - - result=$(/usr/sbin/secubox-feed-manager remove "$name" 2>&1) - if [ $? -eq 0 ]; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Feed removed" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - share_feed) - read -r input - name=$(echo "$input" | jsonfilter -e '@.name') - /usr/sbin/secubox-feed-manager share "$name" - ;; - - import_feed) - read -r input - url=$(echo "$input" | jsonfilter -e '@.url') - - result=$(/usr/sbin/secubox-feed-manager import "$url" 2>&1) - if [ $? -eq 0 ]; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Feed imported" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - # Profile management handlers (extended) - export_profile) - read -r input - name=$(echo "$input" | jsonfilter -e '@.name') - include_feeds=$(echo "$input" | jsonfilter -e '@.include_feeds') - - args="" - [ -n "$name" ] && args="$args --name \"$name\"" - [ "$include_feeds" = "true" ] && args="$args --include-feeds" - - output="/tmp/secubox-profile-export-$$.json" - eval /usr/sbin/secubox-profile export $args --output "$output" >/dev/null 2>&1 - - if [ -f "$output" ]; then - cat "$output" - rm -f "$output" - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "Export failed" - json_dump - fi - ;; - - import_profile) - read -r input - url=$(echo "$input" | jsonfilter -e '@.url') - mode=$(echo "$input" | jsonfilter -e '@.mode') - - result=$(/usr/sbin/secubox-profile import "$url" "${mode:---merge}" 2>&1) - if [ $? -eq 0 ]; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Profile imported" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - share_profile) - read -r input - profile=$(echo "$input" | jsonfilter -e '@.profile') - /usr/sbin/secubox-profile share "$profile" - ;; - - # Skill management handlers - list_skills) - /usr/sbin/secubox-skill list --json - ;; - - get_skill_providers) - read -r input - skill=$(echo "$input" | jsonfilter -e '@.skill') - - # Return JSON list of providers for skill - json_init - json_add_string "skill" "$skill" - json_add_array "providers" - - CATALOG_FILE="/usr/share/secubox/catalog.json" - if [ -f "$CATALOG_FILE" ]; then - idx=0 - while true; do - pid=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].id" 2>/dev/null) - [ -z "$pid" ] && break - - caps=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].capabilities[@]" 2>/dev/null) - for cap in $caps; do - cap_norm=$(echo "$cap" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') - if [ "$cap_norm" = "$skill" ]; then - pname=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].name" 2>/dev/null) - pstatus=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].status" 2>/dev/null) - featured=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].featured" 2>/dev/null) - - json_add_object "" - json_add_string "id" "$pid" - json_add_string "name" "$pname" - json_add_string "status" "$pstatus" - json_add_boolean "featured" "$([ "$featured" = "true" ] && echo 1 || echo 0)" - - # Check if installed - main_pkg=$(jsonfilter -i "$CATALOG_FILE" -e "@.plugins[$idx].packages.required[0]" 2>/dev/null) - installed=0 - if [ -n "$main_pkg" ] && opkg list-installed 2>/dev/null | grep -q "^$main_pkg "; then - installed=1 - fi - json_add_boolean "installed" "$installed" - json_close_object - break - fi - done - - idx=$((idx + 1)) - done - fi - - json_close_array - json_dump - ;; - - install_skill) - read -r input - skill=$(echo "$input" | jsonfilter -e '@.skill') - - result=$(/usr/sbin/secubox-skill install "$skill" 2>&1) - if [ $? -eq 0 ]; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Skill provider installed" - json_add_string "skill" "$skill" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - check_skills) - read -r input - profile=$(echo "$input" | jsonfilter -e '@.profile') - - # Check skills for profile or system - if [ -n "$profile" ]; then - /usr/sbin/secubox-skill check "$profile" - else - /usr/sbin/secubox-skill check - fi - ;; - - # Feedback management handlers - report_issue) - read -r input - app_id=$(echo "$input" | jsonfilter -e '@.app_id') - type=$(echo "$input" | jsonfilter -e '@.type') - summary=$(echo "$input" | jsonfilter -e '@.summary') - details=$(echo "$input" | jsonfilter -e '@.details') - - result=$(/usr/sbin/secubox-feedback report "$app_id" --type "${type:-bug}" --summary "$summary" --details "$details" 2>&1) - if [ $? -eq 0 ]; then - # Extract issue number from result - issue_num=$(echo "$result" | grep -oE 'Issue #[0-9]+' | grep -oE '[0-9]+') - json_init - json_add_boolean "success" 1 - json_add_string "message" "Issue reported" - [ -n "$issue_num" ] && json_add_int "issue_number" "$issue_num" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - resolve_issue) - read -r input - issue_id=$(echo "$input" | jsonfilter -e '@.issue_id') - description=$(echo "$input" | jsonfilter -e '@.description') - - result=$(/usr/sbin/secubox-feedback resolve "$issue_id" --description "$description" 2>&1) - if [ $? -eq 0 ]; then - json_init - json_add_boolean "success" 1 - json_add_string "message" "Resolution added" - json_dump - else - json_init - json_add_boolean "success" 0 - json_add_string "error" "$result" - json_dump - fi - ;; - - search_resolutions) - read -r input - keyword=$(echo "$input" | jsonfilter -e '@.keyword') - - # Return JSON search results - RESOLUTIONS_FILE="/var/lib/secubox/feedback/resolutions.json" - ISSUES_FILE="/var/lib/secubox/feedback/issues.json" - - json_init - json_add_string "keyword" "$keyword" - json_add_array "results" - - if [ -f "$RESOLUTIONS_FILE" ]; then - idx=0 - while true; do - res_id=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].id" 2>/dev/null) - [ -z "$res_id" ] && break - - description=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].description" 2>/dev/null) - issue_summary=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].issue_summary" 2>/dev/null) - app_id=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].app_id" 2>/dev/null) - - if echo "$description $issue_summary $app_id" | grep -qi "$keyword"; then - issue_num=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].issue_number" 2>/dev/null) - upvotes=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].upvotes" 2>/dev/null) - verified=$(jsonfilter -i "$RESOLUTIONS_FILE" -e "@.resolutions[$idx].verified" 2>/dev/null) - - json_add_object "" - json_add_string "id" "$res_id" - json_add_int "issue_number" "$issue_num" - json_add_string "app_id" "$app_id" - json_add_string "issue_summary" "$issue_summary" - json_add_string "description" "$description" - json_add_int "upvotes" "${upvotes:-0}" - json_add_boolean "verified" "$([ "$verified" = "true" ] && echo 1 || echo 0)" - json_close_object - fi - - idx=$((idx + 1)) - done - fi - - json_close_array - json_dump - ;; - - list_issues) - read -r input - filter=$(echo "$input" | jsonfilter -e '@.filter') - - ISSUES_FILE="/var/lib/secubox/feedback/issues.json" - - json_init - json_add_array "issues" - - if [ -f "$ISSUES_FILE" ]; then - idx=0 - while true; do - issue_id=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].id" 2>/dev/null) - [ -z "$issue_id" ] && break - - status=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].status" 2>/dev/null) - - # Apply filter - if [ -n "$filter" ] && [ "$filter" != "all" ] && [ "$filter" != "$status" ]; then - idx=$((idx + 1)) - continue - fi - - issue_num=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].number" 2>/dev/null) - app_id=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].app_id" 2>/dev/null) - summary=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].summary" 2>/dev/null) - issue_type=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].type" 2>/dev/null) - created=$(jsonfilter -i "$ISSUES_FILE" -e "@.issues[$idx].created_at" 2>/dev/null) - - json_add_object "" - json_add_string "id" "$issue_id" - json_add_int "number" "$issue_num" - json_add_string "app_id" "$app_id" - json_add_string "summary" "$summary" - json_add_string "type" "$issue_type" - json_add_string "status" "$status" - json_add_string "created_at" "$created" - json_close_object - - idx=$((idx + 1)) - done - fi - - json_close_array - json_dump - ;; - - # P2P Hub - Collaborative catalog sharing - p2p_get_peers) - P2P_PEERS_FILE="/var/lib/secubox/p2p/peers.json" - P2P_DIR="/var/lib/secubox/p2p" - mkdir -p "$P2P_DIR" - - json_init - json_add_array "peers" - - if [ -f "$P2P_PEERS_FILE" ]; then - idx=0 - while true; do - peer_id=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].id" 2>/dev/null) - [ -z "$peer_id" ] && break - - peer_name=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].name" 2>/dev/null) - peer_addr=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].address" 2>/dev/null) - peer_status=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].status" 2>/dev/null) - last_seen=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].last_seen" 2>/dev/null) - - json_add_object "" - json_add_string "id" "$peer_id" - json_add_string "name" "${peer_name:-$peer_id}" - json_add_string "address" "$peer_addr" - json_add_string "status" "${peer_status:-unknown}" - json_add_string "last_seen" "$last_seen" - json_close_object - - idx=$((idx + 1)) - done - fi - - json_close_array - json_dump - ;; - - p2p_discover) - P2P_DIR="/var/lib/secubox/p2p" - mkdir -p "$P2P_DIR" - - # mDNS discovery for SecuBox peers - discovered=0 - json_init - json_add_array "peers" - - # Try to discover using avahi-browse if available - if command -v avahi-browse >/dev/null 2>&1; then - avahi-browse -t -r _secubox._tcp 2>/dev/null | grep -E "^\+" | while read -r line; do - addr=$(echo "$line" | awk '{print $7}') - name=$(echo "$line" | awk '{print $4}') - if [ -n "$addr" ]; then - json_add_object "" - json_add_string "address" "$addr" - json_add_string "name" "$name" - json_add_string "discovered" "mdns" - json_close_object - discovered=$((discovered + 1)) - fi - done - fi - - json_close_array - json_add_int "discovered" "$discovered" - json_dump - ;; - - p2p_add_peer) - read -r input - address=$(echo "$input" | jsonfilter -e '@.address') - name=$(echo "$input" | jsonfilter -e '@.name') - - P2P_PEERS_FILE="/var/lib/secubox/p2p/peers.json" - P2P_DIR="/var/lib/secubox/p2p" - mkdir -p "$P2P_DIR" - - # Generate peer ID - peer_id="peer_$(echo "$address" | md5sum | cut -c1-8)" - - # Initialize file if not exists - [ ! -f "$P2P_PEERS_FILE" ] && echo '{"peers":[]}' > "$P2P_PEERS_FILE" - - # Add peer using jsonfilter/sed - timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) - tmp_file="${P2P_PEERS_FILE}.tmp" - { - echo '{"peers":[' - # Existing peers - if [ -f "$P2P_PEERS_FILE" ]; then - jsonfilter -i "$P2P_PEERS_FILE" -e '@.peers[*]' 2>/dev/null | while read -r p; do - echo "$p," - done - fi - # New peer - echo "{\"id\":\"$peer_id\",\"name\":\"$name\",\"address\":\"$address\",\"status\":\"added\",\"last_seen\":\"$timestamp\"}" - echo ']}' - } > "$tmp_file" - mv "$tmp_file" "$P2P_PEERS_FILE" - - json_init - json_add_boolean "success" 1 - json_add_string "peer_id" "$peer_id" - json_dump - ;; - - p2p_remove_peer) - read -r input - peer_id=$(echo "$input" | jsonfilter -e '@.peer_id') - - P2P_PEERS_FILE="/var/lib/secubox/p2p/peers.json" - - if [ -f "$P2P_PEERS_FILE" ] && [ -n "$peer_id" ]; then - # Filter out the peer - tmp_file="${P2P_PEERS_FILE}.tmp" - { - echo '{"peers":[' - first=1 - idx=0 - while true; do - id=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].id" 2>/dev/null) - [ -z "$id" ] && break - if [ "$id" != "$peer_id" ]; then - [ "$first" -eq 0 ] && echo "," - jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx]" 2>/dev/null - first=0 - fi - idx=$((idx + 1)) - done - echo ']}' - } > "$tmp_file" - mv "$tmp_file" "$P2P_PEERS_FILE" - fi - - json_init - json_add_boolean "success" 1 - json_dump - ;; - - p2p_get_peer_catalog) - read -r input - peer_id=$(echo "$input" | jsonfilter -e '@.peer_id') - - P2P_PEERS_FILE="/var/lib/secubox/p2p/peers.json" - - # Get peer address - idx=0 - peer_addr="" - while true; do - id=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].id" 2>/dev/null) - [ -z "$id" ] && break - if [ "$id" = "$peer_id" ]; then - peer_addr=$(jsonfilter -i "$P2P_PEERS_FILE" -e "@.peers[$idx].address" 2>/dev/null) - break - fi - idx=$((idx + 1)) - done - - json_init - - if [ -n "$peer_addr" ]; then - # Fetch catalog from peer - catalog=$(wget -q -O - "http://${peer_addr}:8080/api/catalog" 2>/dev/null) - if [ -n "$catalog" ]; then - echo "$catalog" - exit 0 - fi - fi - - json_add_array "apps" - json_close_array - json_add_string "error" "Could not fetch catalog from peer" - json_dump - ;; - - p2p_share_catalog) - read -r input - enabled=$(echo "$input" | jsonfilter -e '@.enabled') - - P2P_CONFIG="/var/lib/secubox/p2p/config.json" - P2P_DIR="/var/lib/secubox/p2p" - mkdir -p "$P2P_DIR" - - # Update config - echo "{\"sharing_enabled\":$enabled}" > "$P2P_CONFIG" - - json_init - json_add_boolean "success" 1 - json_add_boolean "sharing_enabled" "$enabled" - json_dump - ;; - - p2p_get_settings) - P2P_CONFIG="/var/lib/secubox/p2p/config.json" - - json_init - if [ -f "$P2P_CONFIG" ]; then - sharing=$(jsonfilter -i "$P2P_CONFIG" -e '@.sharing_enabled' 2>/dev/null) - json_add_boolean "sharing_enabled" "${sharing:-0}" - else - json_add_boolean "sharing_enabled" 0 - fi - json_add_string "hub_version" "1.0.0" - json_add_string "protocol" "http" - json_add_int "port" 8080 - json_dump - ;; - - p2p_set_settings) - read -r input - - P2P_CONFIG="/var/lib/secubox/p2p/config.json" - P2P_DIR="/var/lib/secubox/p2p" - mkdir -p "$P2P_DIR" - - # Extract settings - sharing=$(echo "$input" | jsonfilter -e '@.settings.sharing_enabled' 2>/dev/null) - - # Save config - { - echo '{' - echo "\"sharing_enabled\":${sharing:-false}" - echo '}' - } > "$P2P_CONFIG" - - json_init - json_add_boolean "success" 1 - json_dump - ;; - - *) - json_init - json_add_boolean "error" true - json_add_string "message" "Unknown method: $2" - json_dump - exit 1 - ;; - esac + _load_modules + _call_method "$2" + ;; + *) + echo "Usage: $0 {list|call} [method]" + exit 1 ;; esac