diff --git a/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer b/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer index 7a0356f2..45fd337b 100755 --- a/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer +++ b/package/secubox/luci-app-metablogizer/root/usr/libexec/rpcd/luci.metablogizer @@ -66,8 +66,31 @@ fix_permissions() { done } -# Reload HAProxy configuration properly +# Check if HAProxy is available and running +haproxy_available() { + # Check if HAProxy container is running (preferred method) + if command -v lxc-info >/dev/null 2>&1; then + lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING" && return 0 + fi + + # Fallback: check if haproxy process is running + pgrep haproxy >/dev/null 2>&1 && return 0 + + # Fallback: check if init script exists and service is enabled + if [ -x /etc/init.d/haproxy ]; then + /etc/init.d/haproxy status >/dev/null 2>&1 && return 0 + fi + + return 1 +} + +# Reload HAProxy configuration properly (with availability check) reload_haproxy() { + if ! haproxy_available; then + logger -t metablogizer "HAProxy not available, skipping reload" + return 1 + fi + /usr/sbin/haproxyctl generate >/dev/null 2>&1 if [ -x /etc/init.d/haproxy ]; then /etc/init.d/haproxy reload >/dev/null 2>&1 @@ -362,42 +385,48 @@ EOF [ -n "$port" ] && uci set "$UCI_CONFIG.$section_id.port=$port" uci set "$UCI_CONFIG.$section_id.runtime=$current_runtime" - # 7. Create HAProxy backend - local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')" + # 7. Create HAProxy backend (if HAProxy is available) + local haproxy_configured=0 + if haproxy_available; then + local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')" - uci set "haproxy.$backend_name=backend" - uci set "haproxy.$backend_name.name=$backend_name" - uci set "haproxy.$backend_name.mode=http" - uci set "haproxy.$backend_name.balance=roundrobin" - uci set "haproxy.$backend_name.enabled=1" + uci set "haproxy.$backend_name=backend" + uci set "haproxy.$backend_name.name=$backend_name" + uci set "haproxy.$backend_name.mode=http" + uci set "haproxy.$backend_name.balance=roundrobin" + uci set "haproxy.$backend_name.enabled=1" - # Create server - local server_name="${backend_name}_srv" - uci set "haproxy.$server_name=server" - uci set "haproxy.$server_name.backend=$backend_name" - uci set "haproxy.$server_name.name=srv" - uci set "haproxy.$server_name.address=$server_address" - uci set "haproxy.$server_name.port=$server_port" - uci set "haproxy.$server_name.weight=100" - uci set "haproxy.$server_name.check=1" - uci set "haproxy.$server_name.enabled=1" + # Create server + local server_name="${backend_name}_srv" + uci set "haproxy.$server_name=server" + uci set "haproxy.$server_name.backend=$backend_name" + uci set "haproxy.$server_name.name=srv" + uci set "haproxy.$server_name.address=$server_address" + uci set "haproxy.$server_name.port=$server_port" + uci set "haproxy.$server_name.weight=100" + uci set "haproxy.$server_name.check=1" + uci set "haproxy.$server_name.enabled=1" - # 8. Create HAProxy vhost - local vhost_name=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g') - local acme_val="0" - [ "$ssl" = "1" ] && acme_val="1" + # 8. Create HAProxy vhost + local vhost_name=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g') + local acme_val="0" + [ "$ssl" = "1" ] && acme_val="1" - uci set "haproxy.$vhost_name=vhost" - uci set "haproxy.$vhost_name.domain=$domain" - uci set "haproxy.$vhost_name.backend=$backend_name" - uci set "haproxy.$vhost_name.ssl=$ssl" - uci set "haproxy.$vhost_name.ssl_redirect=$ssl" - uci set "haproxy.$vhost_name.acme=$acme_val" - uci set "haproxy.$vhost_name.enabled=1" - uci commit haproxy + uci set "haproxy.$vhost_name=vhost" + uci set "haproxy.$vhost_name.domain=$domain" + uci set "haproxy.$vhost_name.backend=$backend_name" + uci set "haproxy.$vhost_name.ssl=$ssl" + uci set "haproxy.$vhost_name.ssl_redirect=$ssl" + uci set "haproxy.$vhost_name.acme=$acme_val" + uci set "haproxy.$vhost_name.enabled=1" + uci commit haproxy - # Regenerate HAProxy config and reload - reload_haproxy + # Regenerate HAProxy config and reload + reload_haproxy + haproxy_configured=1 + else + logger -t metablogizer "HAProxy not available, site created without proxy config" + fi uci commit "$UCI_CONFIG" @@ -464,16 +493,22 @@ method_delete_site() { # Get site runtime local site_runtime=$(get_uci "$id" runtime "") - # 1. Delete HAProxy vhost - local vhost_id=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g') - uci delete "haproxy.$vhost_id" 2>/dev/null + # 1. Delete HAProxy vhost (if HAProxy config exists) + if uci -q get haproxy >/dev/null 2>&1; then + local vhost_id=$(echo "$domain" | sed 's/[^a-zA-Z0-9]/_/g') + uci delete "haproxy.$vhost_id" 2>/dev/null - # 2. Delete HAProxy backend and server - local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')" - uci delete "haproxy.$backend_name" 2>/dev/null - uci delete "haproxy.${backend_name}_srv" 2>/dev/null - uci commit haproxy - reload_haproxy + # 2. Delete HAProxy backend and server + local backend_name="metablog_$(echo "$name" | sed 's/[^a-zA-Z0-9]/_/g')" + uci delete "haproxy.$backend_name" 2>/dev/null + uci delete "haproxy.${backend_name}_srv" 2>/dev/null + uci commit haproxy + + # Only reload if HAProxy is actually running + if haproxy_available; then + reload_haproxy + fi + fi # 3. Remove runtime config if [ "$site_runtime" = "uhttpd" ]; then diff --git a/package/secubox/luci-app-service-registry/root/usr/libexec/rpcd/luci.service-registry b/package/secubox/luci-app-service-registry/root/usr/libexec/rpcd/luci.service-registry index bcf49234..2ddfb421 100755 --- a/package/secubox/luci-app-service-registry/root/usr/libexec/rpcd/luci.service-registry +++ b/package/secubox/luci-app-service-registry/root/usr/libexec/rpcd/luci.service-registry @@ -35,6 +35,22 @@ is_port_listening() { return 1 } +# Helper: Check if HAProxy is available and running +haproxy_available() { + # Check if HAProxy container is running (preferred method) + if command -v lxc-info >/dev/null 2>&1; then + lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING" && return 0 + fi + + # Fallback: check if haproxy process is running + pgrep haproxy >/dev/null 2>&1 && return 0 + + # Fallback: check if RPCD haproxy interface is available + ubus call luci.haproxy list 2>/dev/null | jsonfilter -e '@' >/dev/null 2>&1 && return 0 + + return 1 +} + # Helper: Get process name for port get_process_for_port() { local port="$1" @@ -263,6 +279,11 @@ _aggregate_haproxy_services() { local lan_ip="$1" local tmp_file="$2" + # Check if HAProxy is available before calling RPCD + if ! haproxy_available; then + return 0 + fi + # Call HAProxy RPCD to get vhosts and backends local vhosts_json backends_json certs_json vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null) @@ -614,29 +635,37 @@ method_publish_service() { local urls_clearnet="" local urls_onion="" - # Create HAProxy vhost if domain specified + # Create HAProxy vhost if domain specified and HAProxy is available if [ -n "$domain" ]; then - # Ensure firewall allows HTTP/HTTPS from WAN (for public access + ACME) - ensure_haproxy_firewall_rules + if haproxy_available; then + # Ensure firewall allows HTTP/HTTPS from WAN (for public access + ACME) + ensure_haproxy_firewall_rules - # Create backend - ubus call luci.haproxy create_backend "{\"name\":\"$section_id\",\"mode\":\"http\"}" 2>/dev/null + # Create backend + ubus call luci.haproxy create_backend "{\"name\":\"$section_id\",\"mode\":\"http\"}" 2>/dev/null - # Create server pointing to local port - ubus call luci.haproxy create_server "{\"backend\":\"$section_id\",\"name\":\"local\",\"address\":\"127.0.0.1\",\"port\":$local_port}" 2>/dev/null + # Create server pointing to local port + ubus call luci.haproxy create_server "{\"backend\":\"$section_id\",\"name\":\"local\",\"address\":\"127.0.0.1\",\"port\":$local_port}" 2>/dev/null - # Create vhost with SSL - ubus call luci.haproxy create_vhost "{\"domain\":\"$domain\",\"backend\":\"$section_id\",\"ssl\":1,\"ssl_redirect\":1,\"acme\":1,\"enabled\":1}" 2>/dev/null + # Create vhost with SSL + ubus call luci.haproxy create_vhost "{\"domain\":\"$domain\",\"backend\":\"$section_id\",\"ssl\":1,\"ssl_redirect\":1,\"acme\":1,\"enabled\":1}" 2>/dev/null - # Regenerate HAProxy config to include the new vhost - ubus call luci.haproxy generate 2>/dev/null - ubus call luci.haproxy reload 2>/dev/null + # Regenerate HAProxy config to include the new vhost + ubus call luci.haproxy generate 2>/dev/null + ubus call luci.haproxy reload 2>/dev/null - uci set "$UCI_CONFIG.$section_id.haproxy_enabled=1" - uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain" - uci set "$UCI_CONFIG.$section_id.haproxy_ssl=1" + uci set "$UCI_CONFIG.$section_id.haproxy_enabled=1" + uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain" + uci set "$UCI_CONFIG.$section_id.haproxy_ssl=1" - urls_clearnet="https://${domain}" + urls_clearnet="https://${domain}" + else + # Store domain for later HAProxy configuration when it becomes available + uci set "$UCI_CONFIG.$section_id.haproxy_enabled=0" + uci set "$UCI_CONFIG.$section_id.haproxy_domain=$domain" + uci set "$UCI_CONFIG.$section_id.haproxy_pending=1" + logger -t service-registry "HAProxy unavailable, domain $domain saved for later" + fi fi # Create Tor hidden service if enabled @@ -717,12 +746,14 @@ method_unpublish_service() { config_get haproxy_domain "$service_id" haproxy_domain "" config_get tor_enabled "$service_id" tor_enabled "0" - # Remove HAProxy vhost + # Remove HAProxy vhost (if HAProxy is available) if [ "$haproxy_enabled" = "1" ] && [ -n "$haproxy_domain" ]; then - local vhost_id - vhost_id=$(echo "$haproxy_domain" | sed 's/[^a-zA-Z0-9]/_/g') - ubus call luci.haproxy delete_vhost "{\"id\":\"$vhost_id\"}" 2>/dev/null - ubus call luci.haproxy delete_backend "{\"id\":\"$service_id\"}" 2>/dev/null + if haproxy_available; then + local vhost_id + vhost_id=$(echo "$haproxy_domain" | sed 's/[^a-zA-Z0-9]/_/g') + ubus call luci.haproxy delete_vhost "{\"id\":\"$vhost_id\"}" 2>/dev/null + ubus call luci.haproxy delete_backend "{\"id\":\"$service_id\"}" 2>/dev/null + fi fi # Remove Tor hidden service @@ -1368,9 +1399,11 @@ method_check_all_health() { # Check individual services with domains json_add_array "services" - # Get all published services with domains from HAProxy + # Get all published services with domains from HAProxy (if available) local vhosts_json - vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null) + if haproxy_available; then + vhosts_json=$(ubus call luci.haproxy list_vhosts 2>/dev/null) + fi if [ -n "$vhosts_json" ]; then local count count=$(echo "$vhosts_json" | jsonfilter -e '@.vhosts[*].domain' 2>/dev/null | wc -l) diff --git a/package/secubox/luci-app-vhost-manager/root/usr/libexec/rpcd/luci.vhost-manager b/package/secubox/luci-app-vhost-manager/root/usr/libexec/rpcd/luci.vhost-manager index e61940fc..33bb5c60 100755 --- a/package/secubox/luci-app-vhost-manager/root/usr/libexec/rpcd/luci.vhost-manager +++ b/package/secubox/luci-app-vhost-manager/root/usr/libexec/rpcd/luci.vhost-manager @@ -603,7 +603,7 @@ case "$1" in if [ -z "$domain" ]; then json_init; json_add_boolean "success" 0; json_add_string "message" "Domain required"; json_dump; exit 0 fi - section + local section section=$(find_section "$domain") if [ -z "$section" ]; then json_init; json_add_boolean "success" 0; json_add_string "message" "VHost not found"; json_dump; exit 0 @@ -618,7 +618,7 @@ case "$1" in if [ -z "$enabled" ]; then config_get_bool enabled "$section" enabled 1 fi - current_tls current_cert_path current_key_path + local current_tls current_cert_path current_key_path config_get current_tls "$section" tls config_get current_cert_path "$section" cert_path config_get current_key_path "$section" key_path @@ -690,9 +690,8 @@ case "$1" in json_add_array "certificates" if [ -d "$ACME_STATE_DIR" ]; then find "$ACME_STATE_DIR" -name "fullchain.cer" -type f 2>/dev/null | while read -r cert_file; do - domain + local domain expires issuer subject domain=$(basename "$(dirname "$cert_file")") - expires issuer subject expires=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | cut -d'=' -f2) issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | cut -d'=' -f2-) subject=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | cut -d'=' -f2-) diff --git a/package/secubox/secubox-p2p/Makefile b/package/secubox/secubox-p2p/Makefile index 903a8734..e5dfe88e 100644 --- a/package/secubox/secubox-p2p/Makefile +++ b/package/secubox/secubox-p2p/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=secubox-p2p -PKG_VERSION:=0.1.0 +PKG_VERSION:=0.2.0 PKG_RELEASE:=1 PKG_MAINTAINER:=SecuBox Team @@ -13,13 +13,14 @@ define Package/secubox-p2p SECTION:=secubox CATEGORY:=SecuBox TITLE:=SecuBox P2P Hub Backend - DEPENDS:=+jsonfilter +curl +avahi-daemon + DEPENDS:=+jsonfilter +curl +avahi-daemon +avahi-utils +uhttpd PKGARCH:=all endef define Package/secubox-p2p/description SecuBox P2P Hub backend providing peer discovery, mesh networking, - DNS federation, and distributed service management. + DNS federation, and distributed service management. Includes mDNS + service announcement and REST API on port 7331 for mesh visibility. endef define Package/secubox-p2p/conffiles @@ -33,6 +34,9 @@ define Package/secubox-p2p/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) ./root/usr/sbin/secubox-p2p $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) ./root/usr/bin/secubox-restore $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/usr/libexec/rpcd $(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.secubox-p2p $(1)/usr/libexec/rpcd/ @@ -41,12 +45,27 @@ define Package/secubox-p2p/install $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_BIN) ./root/etc/init.d/secubox-p2p $(1)/etc/init.d/ + + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./root/etc/uci-defaults/99-secubox-p2p-api $(1)/etc/uci-defaults/ + + $(INSTALL_DIR) $(1)/www/api + $(INSTALL_BIN) ./root/www/api/peers $(1)/www/api/ + $(INSTALL_BIN) ./root/www/api/status $(1)/www/api/ + $(INSTALL_BIN) ./root/www/api/services $(1)/www/api/ + $(INSTALL_BIN) ./root/www/api/sync $(1)/www/api/ endef define Package/secubox-p2p/postinst #!/bin/sh [ -n "$${IPKG_INSTROOT}" ] || { + # Run UCI defaults + [ -x /etc/uci-defaults/99-secubox-p2p-api ] && /etc/uci-defaults/99-secubox-p2p-api + # Reload uhttpd to pick up new instance + /etc/init.d/uhttpd reload 2>/dev/null + # Enable and start P2P service /etc/init.d/secubox-p2p enable + /etc/init.d/secubox-p2p start /etc/init.d/rpcd restart } exit 0 diff --git a/package/secubox/secubox-p2p/root/etc/init.d/secubox-p2p b/package/secubox/secubox-p2p/root/etc/init.d/secubox-p2p index e59d3646..95ff4cf0 100644 --- a/package/secubox/secubox-p2p/root/etc/init.d/secubox-p2p +++ b/package/secubox/secubox-p2p/root/etc/init.d/secubox-p2p @@ -7,6 +7,9 @@ USE_PROCD=1 NAME="secubox-p2p" PROG="/usr/sbin/secubox-p2p" +# Ensure avahi-daemon is started before us +DEPEND="avahi-daemon" + start_service() { local enabled config_load secubox-p2p @@ -14,14 +17,36 @@ start_service() { [ "$enabled" = "1" ] || return + # Ensure state directory exists + mkdir -p /var/run/secubox-p2p + + # Ensure avahi-daemon is running for mDNS + if [ -x /etc/init.d/avahi-daemon ]; then + /etc/init.d/avahi-daemon running || /etc/init.d/avahi-daemon start + fi + procd_open_instance procd_set_param command $PROG daemon procd_set_param respawn procd_set_param stdout 1 procd_set_param stderr 1 + procd_set_param pidfile /var/run/secubox-p2p/daemon.pid procd_close_instance + + logger -t secubox-p2p "P2P mesh daemon started" +} + +stop_service() { + # Stop mDNS announcement + $PROG stop-mdns 2>/dev/null + logger -t secubox-p2p "P2P mesh daemon stopped" } service_triggers() { procd_add_reload_trigger "secubox-p2p" } + +reload_service() { + stop + start +} diff --git a/package/secubox/secubox-p2p/root/etc/uci-defaults/99-secubox-p2p-api b/package/secubox/secubox-p2p/root/etc/uci-defaults/99-secubox-p2p-api new file mode 100644 index 00000000..eceb14d0 --- /dev/null +++ b/package/secubox/secubox-p2p/root/etc/uci-defaults/99-secubox-p2p-api @@ -0,0 +1,41 @@ +#!/bin/sh +# Configure uhttpd instance for P2P REST API on port 7331 + +# Check if p2p_api instance already exists +if ! uci -q get uhttpd.p2p_api >/dev/null 2>&1; then + uci set uhttpd.p2p_api=uhttpd + uci set uhttpd.p2p_api.listen_http='0.0.0.0:7331' + uci set uhttpd.p2p_api.home='/www/api' + uci set uhttpd.p2p_api.cgi_prefix='/' + uci set uhttpd.p2p_api.no_symlinks='0' + uci set uhttpd.p2p_api.no_dirlists='1' + uci set uhttpd.p2p_api.script_timeout='60' + uci set uhttpd.p2p_api.network_timeout='30' + uci commit uhttpd +fi + +# Add firewall rule for P2P API port (LAN only by default) +if ! uci show firewall 2>/dev/null | grep -q "P2P-API"; then + uci add firewall rule + uci set firewall.@rule[-1].name='P2P-API' + uci set firewall.@rule[-1].src='lan' + uci set firewall.@rule[-1].dest_port='7331' + uci set firewall.@rule[-1].proto='tcp' + uci set firewall.@rule[-1].target='ACCEPT' + uci set firewall.@rule[-1].enabled='1' + uci commit firewall +fi + +# Add mDNS firewall rule if not exists +if ! uci show firewall 2>/dev/null | grep -q "mDNS"; then + uci add firewall rule + uci set firewall.@rule[-1].name='mDNS' + uci set firewall.@rule[-1].src='lan' + uci set firewall.@rule[-1].dest_port='5353' + uci set firewall.@rule[-1].proto='udp' + uci set firewall.@rule[-1].target='ACCEPT' + uci set firewall.@rule[-1].enabled='1' + uci commit firewall +fi + +exit 0 diff --git a/package/secubox/secubox-p2p/root/usr/sbin/secubox-p2p b/package/secubox/secubox-p2p/root/usr/sbin/secubox-p2p index c472d2bd..6085f0aa 100644 --- a/package/secubox/secubox-p2p/root/usr/sbin/secubox-p2p +++ b/package/secubox/secubox-p2p/root/usr/sbin/secubox-p2p @@ -2,17 +2,44 @@ # SecuBox P2P Hub Manager # Handles peer discovery, mesh networking, and service federation -VERSION="0.1.0" +VERSION="0.2.0" CONFIG_FILE="/etc/config/secubox-p2p" PEERS_FILE="/tmp/secubox-p2p-peers.json" SERVICES_FILE="/tmp/secubox-p2p-services.json" STATE_DIR="/var/run/secubox-p2p" +MDNS_PID_FILE="$STATE_DIR/mdns.pid" +API_PORT=7331 # Initialize init() { mkdir -p "$STATE_DIR" [ -f "$PEERS_FILE" ] || echo '{"peers":[]}' > "$PEERS_FILE" [ -f "$SERVICES_FILE" ] || echo '{"services":[]}' > "$SERVICES_FILE" + + # Initialize node info + _init_node_info +} + +# Initialize node identity +_init_node_info() { + local node_name + node_name=$(get_config main node_name "") + + # Generate node name from hostname if not set + if [ -z "$node_name" ]; then + node_name=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo "secubox") + set_config main node_name "$node_name" + fi + + # Generate node ID if not exists + local node_id_file="$STATE_DIR/node.id" + if [ ! -f "$node_id_file" ]; then + # Generate unique node ID from MAC address + local mac + mac=$(ip link show br-lan 2>/dev/null | grep ether | awk '{print $2}' | tr -d ':') + [ -z "$mac" ] && mac=$(cat /proc/sys/kernel/random/uuid | tr -d '-' | head -c 12) + echo "sb-${mac}" > "$node_id_file" + fi } # Get config value @@ -276,44 +303,146 @@ broadcast_command() { echo "{\"success\":true,\"broadcast_success\":$success,\"broadcast_failed\":$failed}" } +# Publish mDNS service announcement +publish_mdns() { + local node_name + node_name=$(get_config main node_name "secubox") + + # Kill any existing avahi-publish process + [ -f "$MDNS_PID_FILE" ] && kill $(cat "$MDNS_PID_FILE") 2>/dev/null + rm -f "$MDNS_PID_FILE" + + # Check if avahi-publish is available + if command -v avahi-publish >/dev/null 2>&1; then + # Get LAN IP for service registration + local lan_ip + lan_ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1) + [ -z "$lan_ip" ] && lan_ip=$(uci -q get network.lan.ipaddr) + + # Publish _secubox._tcp service + avahi-publish -s "$node_name" "_secubox._tcp" "$API_PORT" \ + "version=$VERSION" \ + "type=mesh-node" \ + "ip=$lan_ip" & + echo $! > "$MDNS_PID_FILE" + + logger -t secubox-p2p "mDNS service published: $node_name._secubox._tcp on port $API_PORT" + return 0 + else + logger -t secubox-p2p "WARNING: avahi-publish not available, mDNS disabled" + return 1 + fi +} + +# Stop mDNS service announcement +stop_mdns() { + if [ -f "$MDNS_PID_FILE" ]; then + kill $(cat "$MDNS_PID_FILE") 2>/dev/null + rm -f "$MDNS_PID_FILE" + logger -t secubox-p2p "mDNS service stopped" + fi +} + +# Get node status JSON (for REST API) +get_node_status() { + local node_name node_id lan_ip uptime + node_name=$(get_config main node_name "secubox") + node_id=$(cat "$STATE_DIR/node.id" 2>/dev/null || echo "unknown") + lan_ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1) + uptime=$(cat /proc/uptime | cut -d' ' -f1) + + cat </dev/null | wc -l) +} +EOF +} + +# Register self in peer list (ensure node is visible in its own mesh view) +register_self() { + local node_name node_id lan_ip + node_name=$(get_config main node_name "secubox") + node_id=$(cat "$STATE_DIR/node.id" 2>/dev/null || echo "unknown") + lan_ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1) + + # Check if self is already registered + local current=$(get_peers) + local exists + exists=$(echo "$current" | jsonfilter -e "@.peers[@.id='$node_id']" 2>/dev/null) + + if [ -z "$exists" ]; then + local self_peer="{\"id\":\"$node_id\",\"name\":\"$node_name (local)\",\"address\":\"$lan_ip\",\"status\":\"online\",\"is_local\":true,\"added\":\"$(date -Iseconds)\"}" + echo "$current" | jsonfilter -e '@' 2>/dev/null | sed "s/\"peers\":\[/\"peers\":[$self_peer,/" > "$PEERS_FILE.tmp" + if [ -s "$PEERS_FILE.tmp" ]; then + mv "$PEERS_FILE.tmp" "$PEERS_FILE" + else + # Fallback: just create a fresh peers file with self + echo "{\"peers\":[$self_peer]}" > "$PEERS_FILE" + fi + logger -t secubox-p2p "Registered local node: $node_name ($node_id)" + fi +} + # Daemon mode daemon_loop() { init + # Publish mDNS service + publish_mdns + + # Register self in peer list + register_self + + # Setup signal handlers + trap 'stop_mdns; exit 0' INT TERM + while true; do # Auto-discovery if enabled if [ "$(get_config main discovery_enabled 1)" = "1" ]; then local discovered=$(discover_mdns 3) - if [ "$discovered" != "[]" ]; then + if [ "$discovered" != "[]" ] && [ -n "$discovered" ]; then # Update peers file with discovered peers local current=$(get_peers) - for peer in $(echo "$discovered" | jq -c '.[]'); do - local peer_id=$(echo "$peer" | jq -r '.id') - local exists=$(echo "$current" | jq ".peers[] | select(.id==\"$peer_id\")") - if [ -z "$exists" ]; then - current=$(echo "$current" | jq ".peers += [$peer]") - fi - done - echo "$current" > "$PEERS_FILE" + # Parse discovered peers (handle jq or jsonfilter) + if command -v jq >/dev/null 2>&1; then + for peer in $(echo "$discovered" | jq -c '.[]' 2>/dev/null); do + local peer_id=$(echo "$peer" | jq -r '.id') + local exists=$(echo "$current" | jq ".peers[] | select(.id==\"$peer_id\")") + if [ -z "$exists" ]; then + current=$(echo "$current" | jq ".peers += [$peer]") + fi + done + echo "$current" > "$PEERS_FILE" + fi fi fi - # Update peer status + # Update peer status (skip local node) local peers=$(get_peers) - local updated_peers=$(echo "$peers" | jq '.peers | map(. + {"status": "checking"})' | jq -c '.[]') + if command -v jq >/dev/null 2>&1; then + local updated_peers=$(echo "$peers" | jq -c '.peers[] | select(.is_local != true)' 2>/dev/null) - for peer in $updated_peers; do - local addr=$(echo "$peer" | jq -r '.address') - local id=$(echo "$peer" | jq -r '.id') + for peer in $updated_peers; do + local addr=$(echo "$peer" | jq -r '.address') + local id=$(echo "$peer" | jq -r '.id') - if ping -c1 -W1 "$addr" >/dev/null 2>&1; then - peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"online\"") - else - peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"offline\"") - fi - done + if ping -c1 -W1 "$addr" >/dev/null 2>&1; then + peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"online\"") + else + peers=$(echo "$peers" | jq "(.peers[] | select(.id==\"$id\")).status = \"offline\"") + fi + done - echo "$peers" > "$PEERS_FILE" + echo "$peers" > "$PEERS_FILE" + fi # Sleep interval local interval=$(get_config main sync_interval 60) @@ -359,8 +488,21 @@ case "$1" in version) echo "$VERSION" ;; + status) + get_node_status + ;; + publish-mdns) + publish_mdns + ;; + stop-mdns) + stop_mdns + ;; + register-self) + init + register_self + ;; *) - echo "Usage: $0 {daemon|discover|peers|add-peer|remove-peer|settings|set-settings|services|shared-services|sync|broadcast|version}" + echo "Usage: $0 {daemon|discover|peers|add-peer|remove-peer|settings|set-settings|services|shared-services|sync|broadcast|version|status|publish-mdns|stop-mdns|register-self}" exit 1 ;; esac diff --git a/package/secubox/secubox-p2p/root/www/api/peers b/package/secubox/secubox-p2p/root/www/api/peers new file mode 100644 index 00000000..97e7835f --- /dev/null +++ b/package/secubox/secubox-p2p/root/www/api/peers @@ -0,0 +1,10 @@ +#!/bin/sh +# P2P API - List peers endpoint +# Responds to requests on port 7331 + +echo "Content-Type: application/json" +echo "Access-Control-Allow-Origin: *" +echo "" + +# Get peers from secubox-p2p +/usr/sbin/secubox-p2p peers 2>/dev/null || echo '{"peers":[],"error":"daemon_unavailable"}' diff --git a/package/secubox/secubox-p2p/root/www/api/services b/package/secubox/secubox-p2p/root/www/api/services new file mode 100644 index 00000000..3a3b3a92 --- /dev/null +++ b/package/secubox/secubox-p2p/root/www/api/services @@ -0,0 +1,10 @@ +#!/bin/sh +# P2P API - List services endpoint +# Responds to requests on port 7331 + +echo "Content-Type: application/json" +echo "Access-Control-Allow-Origin: *" +echo "" + +# Get services from secubox-p2p +/usr/sbin/secubox-p2p services 2>/dev/null || echo '{"services":[],"error":"daemon_unavailable"}' diff --git a/package/secubox/secubox-p2p/root/www/api/status b/package/secubox/secubox-p2p/root/www/api/status new file mode 100644 index 00000000..e6c0eac0 --- /dev/null +++ b/package/secubox/secubox-p2p/root/www/api/status @@ -0,0 +1,10 @@ +#!/bin/sh +# P2P API - Node status endpoint +# Responds to requests on port 7331 + +echo "Content-Type: application/json" +echo "Access-Control-Allow-Origin: *" +echo "" + +# Get node status from secubox-p2p +/usr/sbin/secubox-p2p status 2>/dev/null || echo '{"error":"daemon_unavailable"}' diff --git a/package/secubox/secubox-p2p/root/www/api/sync b/package/secubox/secubox-p2p/root/www/api/sync new file mode 100644 index 00000000..cc176f7c --- /dev/null +++ b/package/secubox/secubox-p2p/root/www/api/sync @@ -0,0 +1,10 @@ +#!/bin/sh +# P2P API - Trigger sync endpoint +# Responds to requests on port 7331 + +echo "Content-Type: application/json" +echo "Access-Control-Allow-Origin: *" +echo "" + +# Trigger catalog sync +/usr/sbin/secubox-p2p sync 2>/dev/null || echo '{"success":false,"error":"daemon_unavailable"}'