#!/bin/sh # SecuBox P2P RPCD Handler . /usr/share/libubox/jshn.sh P2P_CMD="/usr/sbin/secubox-p2p" case "$1" in list) cat </dev/null || echo "5") $P2P_CMD discover "$timeout" ;; add_peer) read input address=$(echo "$input" | jsonfilter -e '@.address') name=$(echo "$input" | jsonfilter -e '@.name') if [ -n "$address" ]; then $P2P_CMD add-peer "$address" "$name" else echo '{"success":false,"error":"Address required"}' fi ;; remove_peer) read input peer_id=$(echo "$input" | jsonfilter -e '@.peer_id') if [ -n "$peer_id" ]; then $P2P_CMD remove-peer "$peer_id" else echo '{"success":false,"error":"Peer ID required"}' fi ;; set_settings) read input settings=$(echo "$input" | jsonfilter -e '@.settings') $P2P_CMD set-settings "$settings" ;; sync_catalog) $P2P_CMD sync ;; broadcast_command) read input command=$(echo "$input" | jsonfilter -e '@.command') $P2P_CMD broadcast "$command" ;; get_dns_config) cat </dev/null | wc -l) peers_online=$(jsonfilter -i /tmp/secubox-p2p-peers.json -e '@.peers[*].status' 2>/dev/null | grep -c "online" || echo 0) fi # Count running services for svc in dnsmasq uhttpd crowdsec haproxy; do pgrep "$svc" >/dev/null 2>&1 && services_running=$((services_running + 1)) done cat </dev/null) repo_name=$(echo "$input" | jsonfilter -e '@.config.repo_name' 2>/dev/null) repo_owner=$(echo "$input" | jsonfilter -e '@.config.repo_owner' 2>/dev/null) access_token=$(echo "$input" | jsonfilter -e '@.config.access_token' 2>/dev/null) enabled=$(echo "$input" | jsonfilter -e '@.config.enabled' 2>/dev/null) auto_backup=$(echo "$input" | jsonfilter -e '@.config.auto_backup' 2>/dev/null) backup_interval=$(echo "$input" | jsonfilter -e '@.config.backup_interval' 2>/dev/null) [ -n "$server_url" ] && uci set secubox-p2p.gitea.server_url="$server_url" [ -n "$repo_name" ] && uci set secubox-p2p.gitea.repo_name="$repo_name" [ -n "$repo_owner" ] && uci set secubox-p2p.gitea.repo_owner="$repo_owner" [ -n "$access_token" ] && uci set secubox-p2p.gitea.access_token="$access_token" [ -n "$enabled" ] && uci set secubox-p2p.gitea.enabled="$enabled" [ -n "$auto_backup" ] && uci set secubox-p2p.gitea.auto_backup="$auto_backup" [ -n "$backup_interval" ] && uci set secubox-p2p.gitea.backup_interval="$backup_interval" uci commit secubox-p2p echo '{"success":true}' ;; create_gitea_repo) read input repo_name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null) description=$(echo "$input" | jsonfilter -e '@.description' 2>/dev/null) is_private=$(echo "$input" | jsonfilter -e '@.private' 2>/dev/null) init_readme=$(echo "$input" | jsonfilter -e '@.init_readme' 2>/dev/null) server_url=$(uci -q get secubox-p2p.gitea.server_url) access_token=$(uci -q get secubox-p2p.gitea.access_token) if [ -z "$server_url" ] || [ -z "$access_token" ]; then echo '{"success":false,"error":"Gitea server URL and access token required"}' exit 0 fi if [ -z "$repo_name" ]; then echo '{"success":false,"error":"Repository name required"}' exit 0 fi # Create repo via Gitea API api_url="${server_url}/api/v1/user/repos" [ "$is_private" = "true" ] && private_val="true" || private_val="false" [ "$init_readme" = "true" ] && readme_val="true" || readme_val="false" response=$(curl -s -X POST "$api_url" \ -H "Authorization: token $access_token" \ -H "Content-Type: application/json" \ -d "{\"name\":\"$repo_name\",\"description\":\"$description\",\"private\":$private_val,\"auto_init\":$readme_val}" \ 2>/dev/null) if echo "$response" | jsonfilter -e '@.id' >/dev/null 2>&1; then clone_url=$(echo "$response" | jsonfilter -e '@.clone_url' 2>/dev/null) html_url=$(echo "$response" | jsonfilter -e '@.html_url' 2>/dev/null) owner=$(echo "$response" | jsonfilter -e '@.owner.login' 2>/dev/null) # Save repo config uci set secubox-p2p.gitea.repo_name="$repo_name" uci set secubox-p2p.gitea.repo_owner="$owner" uci set secubox-p2p.gitea.enabled=1 uci commit secubox-p2p cat </dev/null || echo "Failed to create repository") echo "{\"success\":false,\"error\":\"$error_msg\"}" fi ;; list_gitea_repos) server_url=$(uci -q get secubox-p2p.gitea.server_url) access_token=$(uci -q get secubox-p2p.gitea.access_token) if [ -z "$server_url" ] || [ -z "$access_token" ]; then echo '{"success":false,"repos":[],"error":"Gitea not configured"}' exit 0 fi response=$(curl -s "${server_url}/api/v1/user/repos" \ -H "Authorization: token $access_token" \ 2>/dev/null) if [ -n "$response" ]; then echo "{\"success\":true,\"repos\":$response}" else echo '{"success":false,"repos":[],"error":"Failed to fetch repositories"}' fi ;; get_gitea_commits) read input limit=$(echo "$input" | jsonfilter -e '@.limit' 2>/dev/null || echo "20") server_url=$(uci -q get secubox-p2p.gitea.server_url) access_token=$(uci -q get secubox-p2p.gitea.access_token) repo_owner=$(uci -q get secubox-p2p.gitea.repo_owner) repo_name=$(uci -q get secubox-p2p.gitea.repo_name) if [ -z "$server_url" ] || [ -z "$access_token" ] || [ -z "$repo_owner" ] || [ -z "$repo_name" ]; then echo '{"success":false,"commits":[],"error":"Gitea repository not configured"}' exit 0 fi response=$(curl -s "${server_url}/api/v1/repos/${repo_owner}/${repo_name}/commits?limit=${limit}" \ -H "Authorization: token $access_token" \ 2>/dev/null) if [ -n "$response" ] && echo "$response" | jsonfilter -e '@[0].sha' >/dev/null 2>&1; then echo "{\"success\":true,\"commits\":$response}" else echo '{"success":false,"commits":[],"error":"Failed to fetch commits or repository empty"}' fi ;; push_gitea_backup) read input message=$(echo "$input" | jsonfilter -e '@.message' 2>/dev/null || echo "SecuBox backup $(date +%Y%m%d-%H%M%S)") components=$(echo "$input" | jsonfilter -e '@.components' 2>/dev/null) server_url=$(uci -q get secubox-p2p.gitea.server_url) access_token=$(uci -q get secubox-p2p.gitea.access_token) repo_owner=$(uci -q get secubox-p2p.gitea.repo_owner) repo_name=$(uci -q get secubox-p2p.gitea.repo_name) if [ -z "$server_url" ] || [ -z "$access_token" ] || [ -z "$repo_owner" ] || [ -z "$repo_name" ]; then echo '{"success":false,"error":"Gitea repository not configured"}' exit 0 fi # Create backup directory backup_dir="/tmp/secubox-gitea-backup-$$" mkdir -p "$backup_dir" # 1. UCI Configs - Core system configuration mkdir -p "$backup_dir/configs" for cfg in secubox secubox-appstore secubox-appstore-opkg secubox-exposure \ secubox-netifyd secubox-p2p secubox-core network firewall \ wireless dhcp system dropbear uhttpd haproxy crowdsec acme \ wireguard wireguard_* gitea nodogsplash; do [ -f "/etc/config/$cfg" ] && cp "/etc/config/$cfg" "$backup_dir/configs/" 2>/dev/null done # 2. Profiles - Deployment templates if [ -d "/usr/share/secubox/profiles" ]; then mkdir -p "$backup_dir/profiles" cp -r /usr/share/secubox/profiles/* "$backup_dir/profiles/" 2>/dev/null fi # 3. Presets - Settings presets if [ -d "/etc/secubox/presets" ]; then mkdir -p "$backup_dir/presets" cp -r /etc/secubox/presets/* "$backup_dir/presets/" 2>/dev/null fi # 4. App Manifests - Plugin definitions if [ -d "/usr/share/secubox/plugins" ]; then mkdir -p "$backup_dir/manifests" cp -r /usr/share/secubox/plugins/* "$backup_dir/manifests/" 2>/dev/null fi # 5. Scripts - Automation scripts if [ -d "/etc/secubox/scripts" ]; then mkdir -p "$backup_dir/scripts" cp -r /etc/secubox/scripts/* "$backup_dir/scripts/" 2>/dev/null fi # 6. Macros - Macro definitions if [ -d "/etc/secubox/macros" ]; then mkdir -p "$backup_dir/macros" cp -r /etc/secubox/macros/* "$backup_dir/macros/" 2>/dev/null fi # 7. Workflows - Automation workflows if [ -d "/etc/secubox/workflows" ]; then mkdir -p "$backup_dir/workflows" cp -r /etc/secubox/workflows/* "$backup_dir/workflows/" 2>/dev/null fi # 8. Package lists mkdir -p "$backup_dir/packages" opkg list-installed > "$backup_dir/packages/installed.txt" 2>/dev/null # Also save SecuBox-specific package list opkg list-installed | grep -E "^(secubox|luci-app-secubox)" > "$backup_dir/packages/secubox-packages.txt" 2>/dev/null # 9. Service states mkdir -p "$backup_dir/services" for svc in /etc/init.d/secubox*; do [ -x "$svc" ] && basename "$svc" >> "$backup_dir/services/enabled.txt" done # 10. Crontabs if [ -f "/etc/crontabs/root" ]; then mkdir -p "$backup_dir/cron" cp /etc/crontabs/root "$backup_dir/cron/root" 2>/dev/null fi # 11. SSH authorized keys (for mesh access) if [ -f "/etc/dropbear/authorized_keys" ]; then mkdir -p "$backup_dir/ssh" cp /etc/dropbear/authorized_keys "$backup_dir/ssh/" 2>/dev/null fi # 12. SSL Certificates (ACME) if [ -d "/etc/acme" ]; then mkdir -p "$backup_dir/certificates" for cert in /etc/acme/*.cer; do [ -f "$cert" ] && cp "$cert" "$backup_dir/certificates/" 2>/dev/null done fi # 13. HAProxy configs if [ -d "/etc/haproxy" ]; then mkdir -p "$backup_dir/haproxy" cp /etc/haproxy/*.cfg "$backup_dir/haproxy/" 2>/dev/null fi # 14. DNS/Hosts mkdir -p "$backup_dir/dns" [ -f "/etc/hosts" ] && cp /etc/hosts "$backup_dir/dns/" 2>/dev/null [ -f "/etc/dnsmasq.conf" ] && cp /etc/dnsmasq.conf "$backup_dir/dns/" 2>/dev/null [ -d "/etc/dnsmasq.d" ] && cp /etc/dnsmasq.d/* "$backup_dir/dns/" 2>/dev/null # 15. Device info mkdir -p "$backup_dir/device" ubus call system board > "$backup_dir/device/board.json" 2>/dev/null cat /proc/cpuinfo > "$backup_dir/device/cpuinfo.txt" 2>/dev/null free -h > "$backup_dir/device/memory.txt" 2>/dev/null # Create comprehensive manifest config_count=$(find "$backup_dir/configs" -type f 2>/dev/null | wc -l) profile_count=$(find "$backup_dir/profiles" -type f 2>/dev/null | wc -l) manifest_count=$(find "$backup_dir/manifests" -type f 2>/dev/null | wc -l) script_count=$(find "$backup_dir/scripts" -type f 2>/dev/null | wc -l) cat > "$backup_dir/manifest.json" </dev/null || echo "unknown")", "openwrt_version": "$(. /etc/openwrt_release 2>/dev/null && echo $DISTRIB_RELEASE || echo unknown)", "device_model": "$(cat /tmp/sysinfo/model 2>/dev/null || echo "unknown")", "message": "$message", "contents": { "configs": $config_count, "profiles": $profile_count, "manifests": $manifest_count, "scripts": $script_count }, "components": [ "configs", "profiles", "presets", "manifests", "scripts", "macros", "workflows", "packages", "services", "cron", "ssh", "certificates", "haproxy", "dns", "device" ] } MANIFEST # Create bootstrap.sh for quick restore cat > "$backup_dir/bootstrap.sh" <<'BOOTSTRAP' #!/bin/sh # SecuBox Quick Restore Bootstrap # Run: wget -qO- URL | sh OR curl -sL URL | sh set -e SERVER_URL="__SERVER_URL__" REPO_OWNER="__REPO_OWNER__" REPO_NAME="__REPO_NAME__" echo "SecuBox Quick Restore" echo "=====================" echo "From: $SERVER_URL/$REPO_OWNER/$REPO_NAME" echo "" # Check OpenWrt [ -f /etc/openwrt_release ] || { echo "Error: Not OpenWrt"; exit 1; } # Install deps opkg update >/dev/null 2>&1 || true opkg install curl git-http 2>/dev/null || true # Download restore script RESTORE_URL="$SERVER_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/contents/scripts/secubox-restore?ref=master" SCRIPT=$(curl -sL "$RESTORE_URL" | jsonfilter -e '@.content' 2>/dev/null | base64 -d 2>/dev/null) if [ -n "$SCRIPT" ]; then echo "$SCRIPT" > /tmp/secubox-restore chmod +x /tmp/secubox-restore /tmp/secubox-restore "$SERVER_URL" "$REPO_OWNER" "$REPO_NAME" else echo "Fallback: Direct config restore..." # Minimal restore - fetch and apply configs API_BASE="$SERVER_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/contents" mkdir -p /tmp/secubox-restore-$$ # Get configs list configs=$(curl -sL "$API_BASE/configs" | jsonfilter -e '@[*].name' 2>/dev/null) for cfg in $configs; do echo "Restoring: $cfg" content=$(curl -sL "$API_BASE/configs/$cfg" | jsonfilter -e '@.content' 2>/dev/null | base64 -d) [ -n "$content" ] && echo "$content" > "/etc/config/$cfg" done echo "" echo "Basic restore complete. Reboot recommended." fi BOOTSTRAP # Replace placeholders in bootstrap sed -i "s|__SERVER_URL__|$server_url|g" "$backup_dir/bootstrap.sh" sed -i "s|__REPO_OWNER__|$repo_owner|g" "$backup_dir/bootstrap.sh" sed -i "s|__REPO_NAME__|$repo_name|g" "$backup_dir/bootstrap.sh" # Also copy the full restore script mkdir -p "$backup_dir/scripts" if [ -f "/usr/bin/secubox-restore" ]; then cp /usr/bin/secubox-restore "$backup_dir/scripts/" fi # Push each file via Gitea API pushed_files=0 api_base="${server_url}/api/v1/repos/${repo_owner}/${repo_name}/contents" for file in $(find "$backup_dir" -type f); do rel_path="${file#$backup_dir/}" content=$(base64 "$file" | tr -d '\n') # Check if file exists (to update vs create) - use main branch existing=$(curl -s "${api_base}/${rel_path}?ref=main" \ -H "Authorization: token $access_token" 2>/dev/null) sha=$(echo "$existing" | jsonfilter -e '@.sha' 2>/dev/null) if [ -n "$sha" ]; then # Update existing file with branch specification curl -s -X PUT "${api_base}/${rel_path}" \ -H "Authorization: token $access_token" \ -H "Content-Type: application/json" \ -d "{\"message\":\"$message\",\"content\":\"$content\",\"sha\":\"$sha\",\"branch\":\"main\"}" \ >/dev/null 2>&1 else # Create new file on main branch curl -s -X POST "${api_base}/${rel_path}" \ -H "Authorization: token $access_token" \ -H "Content-Type: application/json" \ -d "{\"message\":\"$message\",\"content\":\"$content\",\"branch\":\"main\"}" \ >/dev/null 2>&1 fi pushed_files=$((pushed_files + 1)) done # Cleanup rm -rf "$backup_dir" echo "{\"success\":true,\"files_pushed\":$pushed_files,\"message\":\"$message\"}" ;; pull_gitea_backup) read input commit_sha=$(echo "$input" | jsonfilter -e '@.commit_sha' 2>/dev/null) server_url=$(uci -q get secubox-p2p.gitea.server_url) access_token=$(uci -q get secubox-p2p.gitea.access_token) repo_owner=$(uci -q get secubox-p2p.gitea.repo_owner) repo_name=$(uci -q get secubox-p2p.gitea.repo_name) if [ -z "$server_url" ] || [ -z "$access_token" ] || [ -z "$repo_owner" ] || [ -z "$repo_name" ]; then echo '{"success":false,"error":"Gitea repository not configured"}' exit 0 fi # Get file tree at commit ref_param="" [ -n "$commit_sha" ] && ref_param="?ref=$commit_sha" tree=$(curl -s "${server_url}/api/v1/repos/${repo_owner}/${repo_name}/contents${ref_param}" \ -H "Authorization: token $access_token" 2>/dev/null) if [ -z "$tree" ]; then echo '{"success":false,"error":"Failed to fetch repository contents"}' exit 0 fi # Create restore directory restore_dir="/tmp/secubox-restore-$$" mkdir -p "$restore_dir" restored_files=0 # Download configs directory configs=$(curl -s "${server_url}/api/v1/repos/${repo_owner}/${repo_name}/contents/configs${ref_param}" \ -H "Authorization: token $access_token" 2>/dev/null) if echo "$configs" | jsonfilter -e '@[0].name' >/dev/null 2>&1; then mkdir -p "$restore_dir/configs" for file_info in $(echo "$configs" | jsonfilter -e '@[*].name'); do file_content=$(curl -s "${server_url}/api/v1/repos/${repo_owner}/${repo_name}/contents/configs/${file_info}${ref_param}" \ -H "Authorization: token $access_token" 2>/dev/null) content_b64=$(echo "$file_content" | jsonfilter -e '@.content' 2>/dev/null) if [ -n "$content_b64" ]; then echo "$content_b64" | base64 -d > "$restore_dir/configs/$file_info" restored_files=$((restored_files + 1)) fi done # Apply configs (with backup) if [ -d "$restore_dir/configs" ]; then cp -r /etc/config /etc/config.bak.$(date +%Y%m%d%H%M%S) 2>/dev/null for cfg in "$restore_dir"/configs/secubox*; do [ -f "$cfg" ] && cp "$cfg" /etc/config/ 2>/dev/null done fi fi # Cleanup rm -rf "$restore_dir" echo "{\"success\":true,\"files_restored\":$restored_files,\"commit\":\"${commit_sha:-HEAD}\"}" ;; create_local_backup) read input backup_name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null || echo "backup-$(date +%Y%m%d-%H%M%S)") backup_base=$(uci -q get secubox-p2p.backup.backup_dir || echo "/etc/secubox/backups") mkdir -p "$backup_base" backup_dir="$backup_base/$backup_name" mkdir -p "$backup_dir" # Backup configs mkdir -p "$backup_dir/configs" cp -r /etc/config/secubox* "$backup_dir/configs/" 2>/dev/null cp /etc/config/network "$backup_dir/configs/" 2>/dev/null cp /etc/config/firewall "$backup_dir/configs/" 2>/dev/null cp /etc/config/wireless "$backup_dir/configs/" 2>/dev/null # Backup package list opkg list-installed > "$backup_dir/packages.txt" 2>/dev/null # Create manifest cat > "$backup_dir/manifest.json" </dev/null || echo "unknown")" } MANIFEST # Cleanup old backups max_backups=$(uci -q get secubox-p2p.backup.max_backups || echo 10) if [ "$(uci -q get secubox-p2p.backup.auto_cleanup)" = "1" ]; then ls -1dt "$backup_base"/*/ 2>/dev/null | tail -n +$((max_backups + 1)) | xargs rm -rf 2>/dev/null fi backup_size=$(du -sh "$backup_dir" 2>/dev/null | cut -f1) echo "{\"success\":true,\"backup_id\":\"$backup_name\",\"path\":\"$backup_dir\",\"size\":\"$backup_size\"}" ;; list_local_backups) backup_base=$(uci -q get secubox-p2p.backup.backup_dir || echo "/etc/secubox/backups") mkdir -p "$backup_base" echo '{"success":true,"backups":[' first=1 for dir in "$backup_base"/*/; do [ -d "$dir" ] || continue backup_id=$(basename "$dir") if [ -f "$dir/manifest.json" ]; then timestamp=$(jsonfilter -i "$dir/manifest.json" -e '@.timestamp' 2>/dev/null || echo "") hostname=$(jsonfilter -i "$dir/manifest.json" -e '@.hostname' 2>/dev/null || echo "") else timestamp="" hostname="" fi size=$(du -sh "$dir" 2>/dev/null | cut -f1) [ $first -eq 1 ] || echo "," first=0 echo "{\"id\":\"$backup_id\",\"timestamp\":\"$timestamp\",\"hostname\":\"$hostname\",\"size\":\"$size\"}" done echo ']}' ;; restore_local_backup) read input backup_id=$(echo "$input" | jsonfilter -e '@.backup_id' 2>/dev/null) backup_base=$(uci -q get secubox-p2p.backup.backup_dir || echo "/etc/secubox/backups") backup_dir="$backup_base/$backup_id" if [ ! -d "$backup_dir" ]; then echo '{"success":false,"error":"Backup not found"}' exit 0 fi # Create pre-restore backup pre_restore="$backup_base/pre-restore-$(date +%Y%m%d-%H%M%S)" mkdir -p "$pre_restore/configs" cp -r /etc/config/secubox* "$pre_restore/configs/" 2>/dev/null # Restore configs restored=0 if [ -d "$backup_dir/configs" ]; then for cfg in "$backup_dir"/configs/*; do [ -f "$cfg" ] && cp "$cfg" /etc/config/ 2>/dev/null && restored=$((restored + 1)) done fi echo "{\"success\":true,\"files_restored\":$restored,\"pre_restore_backup\":\"$pre_restore\"}" ;; get_feed_peers) # Return list of peers with active package feeds . /usr/lib/secubox/p2p-feed.sh 2>/dev/null json_init json_add_boolean "success" 1 json_add_array "peers" peers=$(get_feed_peers) for peer_line in $peers; do peer_addr=$(echo "$peer_line" | cut -d'|' -f1) peer_name=$(echo "$peer_line" | cut -d'|' -f2) feed_info=$(echo "$peer_line" | cut -d'|' -f3-) feed_hash=$(echo "$feed_info" | cut -d'|' -f1) pkg_count=$(echo "$feed_info" | cut -d'|' -f2) json_add_object "" json_add_string "address" "$peer_addr" json_add_string "name" "$peer_name" json_add_string "feed_hash" "$feed_hash" json_add_int "package_count" "${pkg_count:-0}" json_close_object done json_close_array json_dump ;; get_peer_packages) # Fetch package list from a specific peer read input peer_addr=$(echo "$input" | jsonfilter -e '@.peer_addr' 2>/dev/null) if [ -z "$peer_addr" ]; then echo '{"success":false,"error":"peer_addr required"}' exit 0 fi result=$(curl -s --connect-timeout 5 --max-time 30 \ "http://${peer_addr}:7331/api/factory/packages" 2>/dev/null) if [ -n "$result" ]; then echo "$result" else echo '{"success":false,"error":"Failed to fetch from peer"}' fi ;; get_all_packages) # Return aggregated packages from local + all peers curl -s "http://127.0.0.1:7331/api/factory/packages-sync" 2>/dev/null || \ echo '{"success":false,"error":"Failed to sync packages"}' ;; fetch_package) # Download a package from a peer to local feed read input package=$(echo "$input" | jsonfilter -e '@.package' 2>/dev/null) peer_addr=$(echo "$input" | jsonfilter -e '@.peer_addr' 2>/dev/null) if [ -z "$package" ]; then echo '{"success":false,"error":"package name required"}' exit 0 fi . /usr/lib/secubox/p2p-feed.sh 2>/dev/null # Get package info from peer (or search all peers if no peer specified) if [ -n "$peer_addr" ]; then pkg_info=$(curl -s --connect-timeout 5 --max-time 10 \ "http://${peer_addr}:7331/api/factory/packages?package=$package" 2>/dev/null) filename=$(echo "$pkg_info" | jsonfilter -e '@.packages[0].filename' 2>/dev/null) if [ -n "$filename" ]; then # Download the IPK dest="/www/secubox-feed/$filename" if curl -s --connect-timeout 5 --max-time 300 \ -o "$dest" \ "http://${peer_addr}/secubox-feed/$filename" 2>/dev/null; then if [ -s "$dest" ]; then # Update Packages index /usr/sbin/secubox-feed update >/dev/null 2>&1 echo "{\"success\":true,\"package\":\"$package\",\"filename\":\"$filename\",\"source\":\"$peer_addr\"}" else rm -f "$dest" echo '{"success":false,"error":"Downloaded file is empty"}' fi else echo '{"success":false,"error":"Failed to download package"}' fi else echo '{"success":false,"error":"Package not found on peer"}' fi else # Search all peers search_result=$(search_package "$package" | head -1) if [ -n "$search_result" ]; then source=$(echo "$search_result" | cut -d'|' -f1) if [ "$source" = "local" ]; then echo '{"success":true,"package":"'$package'","source":"local","message":"Package already available locally"}' else found_peer=$(echo "$search_result" | cut -d'|' -f2) # Recursive call with specific peer echo "{\"package\":\"$package\",\"peer_addr\":\"$found_peer\"}" | \ /usr/libexec/rpcd/luci.secubox-p2p call fetch_package fi else echo '{"success":false,"error":"Package not found on any peer"}' fi fi ;; sync_package_catalog) # Sync package catalogs from all peers read input refresh=$(echo "$input" | jsonfilter -e '@.refresh' 2>/dev/null || echo "0") url="http://127.0.0.1:7331/api/factory/packages-sync" [ "$refresh" = "1" ] || [ "$refresh" = "true" ] && url="$url?refresh=1" curl -s "$url" 2>/dev/null || \ echo '{"success":false,"error":"Failed to sync package catalog"}' ;; get_feed_settings) # Return P2P feed settings json_init json_add_boolean "success" 1 json_add_boolean "enabled" $(uci -q get secubox-p2p.feed.enabled || echo "1") json_add_boolean "share_feed" $(uci -q get secubox-p2p.feed.share_feed || echo "1") json_add_boolean "auto_sync" $(uci -q get secubox-p2p.feed.auto_sync || echo "1") json_add_int "sync_interval" $(uci -q get secubox-p2p.feed.sync_interval || echo "3600") json_add_boolean "prefer_local" $(uci -q get secubox-p2p.feed.prefer_local || echo "1") json_add_int "cache_ttl" $(uci -q get secubox-p2p.feed.cache_ttl || echo "300") json_dump ;; set_feed_settings) # Update P2P feed settings read input share_feed=$(echo "$input" | jsonfilter -e '@.share_feed' 2>/dev/null) auto_sync=$(echo "$input" | jsonfilter -e '@.auto_sync' 2>/dev/null) sync_interval=$(echo "$input" | jsonfilter -e '@.sync_interval' 2>/dev/null) prefer_local=$(echo "$input" | jsonfilter -e '@.prefer_local' 2>/dev/null) [ -n "$share_feed" ] && uci set secubox-p2p.feed.share_feed="$share_feed" [ -n "$auto_sync" ] && uci set secubox-p2p.feed.auto_sync="$auto_sync" [ -n "$sync_interval" ] && uci set secubox-p2p.feed.sync_interval="$sync_interval" [ -n "$prefer_local" ] && uci set secubox-p2p.feed.prefer_local="$prefer_local" uci commit secubox-p2p echo '{"success":true}' ;; *) echo '{"error":"Unknown method"}' ;; esac ;; esac