feat(p2p): Add mDNS service publishing and REST API for mesh visibility

- Add mDNS service announcement via avahi-publish for _secubox._tcp
- Add REST API endpoints on port 7331 (/api/peers, /api/status, /api/services)
- Add node self-registration to ensure local node visible in mesh view
- Add UCI defaults for uhttpd P2P API instance and firewall rules
- Bump secubox-p2p version to 0.2.0

fix(vhost-manager): Fix uninitialized variable syntax errors

- Add 'local' keyword to variable declarations on lines 606, 621, 693

fix(metablogizer,service-registry): Add HAProxy availability fallback

- Add haproxy_available() helper to check if HAProxy is running
- Gracefully skip HAProxy operations when service unavailable
- Store pending HAProxy config for later when service becomes available
- Prevent crashes when HAProxy container is stopped

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-30 19:03:14 +01:00
parent 4166f4574e
commit ce512bbda0
11 changed files with 428 additions and 94 deletions

View File

@ -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

View File

@ -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)

View File

@ -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-)

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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 <<EOF
{
"node_id": "$node_id",
"node_name": "$node_name",
"version": "$VERSION",
"address": "$lan_ip",
"api_port": $API_PORT,
"uptime": $uptime,
"discovery_enabled": $(get_config main discovery_enabled 1),
"sharing_enabled": $(get_config main sharing_enabled 1),
"peer_count": $(get_peers | jsonfilter -e '@.peers[*]' 2>/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

View File

@ -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"}'

View File

@ -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"}'

View File

@ -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"}'

View File

@ -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"}'