feat: update WebRTC handlers to support dynamic SFU host configuration and add monitoring script

This commit is contained in:
anonpenguin23 2026-02-23 06:30:57 +02:00
parent 3597c61cfc
commit bcfdabb32d
7 changed files with 50 additions and 165 deletions

View File

@ -1,158 +0,0 @@
### Orama Network Gateway API Examples
# This file is designed for the VS Code "REST Client" extension.
# It demonstrates the core capabilities of the DeBros Network Gateway.
@baseUrl = http://localhost:6001
@apiKey = ak_X32jj2fiin8zzv0hmBKTC5b5:default
@contentType = application/json
############################################################
### 1. SYSTEM & HEALTH
############################################################
# @name HealthCheck
GET {{baseUrl}}/v1/health
X-API-Key: {{apiKey}}
###
# @name SystemStatus
# Returns the full status of the gateway and connected services
GET {{baseUrl}}/v1/status
X-API-Key: {{apiKey}}
###
# @name NetworkStatus
# Returns the P2P network status and PeerID
GET {{baseUrl}}/v1/network/status
X-API-Key: {{apiKey}}
############################################################
### 2. DISTRIBUTED CACHE (OLRIC)
############################################################
# @name CachePut
# Stores a value in the distributed cache (DMap)
POST {{baseUrl}}/v1/cache/put
X-API-Key: {{apiKey}}
Content-Type: {{contentType}}
{
"dmap": "demo-cache",
"key": "video-demo",
"value": "Hello from REST Client!"
}
###
# @name CacheGet
# Retrieves a value from the distributed cache
POST {{baseUrl}}/v1/cache/get
X-API-Key: {{apiKey}}
Content-Type: {{contentType}}
{
"dmap": "demo-cache",
"key": "video-demo"
}
###
# @name CacheScan
# Scans for keys in a specific DMap
POST {{baseUrl}}/v1/cache/scan
X-API-Key: {{apiKey}}
Content-Type: {{contentType}}
{
"dmap": "demo-cache"
}
############################################################
### 3. DECENTRALIZED STORAGE (IPFS)
############################################################
# @name StorageUpload
# Uploads a file to IPFS (Multipart)
POST {{baseUrl}}/v1/storage/upload
X-API-Key: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary
--boundary
Content-Disposition: form-data; name="file"; filename="demo.txt"
Content-Type: text/plain
This is a demonstration of decentralized storage on the Sonr Network.
--boundary--
###
# @name StorageStatus
# Check the pinning status and replication of a CID
# Replace {cid} with the CID returned from the upload above
@demoCid = bafkreid76y6x6v2n5o4n6n5o4n6n5o4n6n5o4n6n5o4
GET {{baseUrl}}/v1/storage/status/{{demoCid}}
X-API-Key: {{apiKey}}
###
# @name StorageDownload
# Retrieve content directly from IPFS via the gateway
GET {{baseUrl}}/v1/storage/get/{{demoCid}}
X-API-Key: {{apiKey}}
############################################################
### 4. REAL-TIME PUB/SUB
############################################################
# @name ListTopics
# Lists all active topics in the current namespace
GET {{baseUrl}}/v1/pubsub/topics
X-API-Key: {{apiKey}}
###
# @name PublishMessage
# Publishes a base64 encoded message to a topic
POST {{baseUrl}}/v1/pubsub/publish
X-API-Key: {{apiKey}}
Content-Type: {{contentType}}
{
"topic": "network-updates",
"data_base64": "U29uciBOZXR3b3JrIGlzIGF3ZXNvbWUh"
}
############################################################
### 5. SERVERLESS FUNCTIONS
############################################################
# @name ListFunctions
# Lists all deployed serverless functions
GET {{baseUrl}}/v1/functions
X-API-Key: {{apiKey}}
###
# @name InvokeFunction
# Invokes a deployed function by name
# Path: /v1/invoke/{namespace}/{functionName}
POST {{baseUrl}}/v1/invoke/default/hello
X-API-Key: {{apiKey}}
Content-Type: {{contentType}}
{
"name": "Developer"
}
###
# @name WhoAmI
# Validates the API Key and returns caller identity
GET {{baseUrl}}/v1/auth/whoami
X-API-Key: {{apiKey}}

View File

@ -340,6 +340,7 @@ func New(logger *logging.ColoredLogger, cfg *Config) (*Gateway, error) {
if cfg.WebRTCEnabled && cfg.SFUPort > 0 {
gw.webrtcHandlers = webrtchandlers.NewWebRTCHandlers(
logger,
gw.localWireGuardIP,
cfg.SFUPort,
cfg.TURNDomain,
cfg.TURNSecret,

View File

@ -15,6 +15,7 @@ func testHandlers() *WebRTCHandlers {
logger, _ := logging.NewColoredLogger(logging.ComponentGeneral, false)
return NewWebRTCHandlers(
logger,
"", // defaults to 127.0.0.1 in tests
8443,
"turn.ns-test.dbrs.space",
"test-secret-key-32bytes-long!!!!",
@ -91,7 +92,7 @@ func TestCredentialsHandler_NoNamespace(t *testing.T) {
func TestCredentialsHandler_NoTURNSecret(t *testing.T) {
logger, _ := logging.NewColoredLogger(logging.ComponentGeneral, false)
h := NewWebRTCHandlers(logger, 8443, "turn.test.dbrs.space", "", nil)
h := NewWebRTCHandlers(logger, "", 8443, "turn.test.dbrs.space", "", nil)
req := requestWithNamespace("POST", "/v1/webrtc/turn/credentials", "test-ns")
w := httptest.NewRecorder()
@ -119,7 +120,7 @@ func TestSignalHandler_NoNamespace(t *testing.T) {
func TestSignalHandler_NoSFUPort(t *testing.T) {
logger, _ := logging.NewColoredLogger(logging.ComponentGeneral, false)
h := NewWebRTCHandlers(logger, 0, "", "secret", nil)
h := NewWebRTCHandlers(logger, "", 0, "", "secret", nil)
req := requestWithNamespace("GET", "/v1/webrtc/signal", "test-ns")
w := httptest.NewRecorder()
@ -171,7 +172,7 @@ func TestRoomsHandler_NoNamespace(t *testing.T) {
func TestRoomsHandler_NoSFUPort(t *testing.T) {
logger, _ := logging.NewColoredLogger(logging.ComponentGeneral, false)
h := NewWebRTCHandlers(logger, 0, "", "secret", nil)
h := NewWebRTCHandlers(logger, "", 0, "", "secret", nil)
req := requestWithNamespace("GET", "/v1/webrtc/rooms", "test-ns")
w := httptest.NewRecorder()
@ -206,7 +207,7 @@ func TestRoomsHandler_SFUProxySuccess(t *testing.T) {
}
}
h := NewWebRTCHandlers(logger, port, "", "secret", nil)
h := NewWebRTCHandlers(logger, "", port, "", "secret", nil)
req := requestWithNamespace("GET", "/v1/webrtc/rooms", "test-ns")
w := httptest.NewRecorder()

View File

@ -31,7 +31,7 @@ func (h *WebRTCHandlers) RoomsHandler(w http.ResponseWriter, r *http.Request) {
}
// Proxy to SFU health endpoint which returns room count
targetURL := fmt.Sprintf("http://127.0.0.1:%d/health", h.sfuPort)
targetURL := fmt.Sprintf("http://%s:%d/health", h.sfuHost, h.sfuPort)
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(targetURL)

View File

@ -23,8 +23,7 @@ func (h *WebRTCHandlers) SignalHandler(w http.ResponseWriter, r *http.Request) {
}
// Proxy WebSocket to local SFU on WireGuard IP
// SFU binds to WireGuard IP, so we use 127.0.0.1 since we're on the same node
targetHost := fmt.Sprintf("127.0.0.1:%d", h.sfuPort)
targetHost := fmt.Sprintf("%s:%d", h.sfuHost, h.sfuPort)
h.logger.ComponentDebug(logging.ComponentGeneral, "Proxying WebRTC signal to SFU",
zap.String("namespace", ns),

View File

@ -12,6 +12,7 @@ import (
// These run on the namespace gateway and proxy signaling to the local SFU.
type WebRTCHandlers struct {
logger *logging.ColoredLogger
sfuHost string // SFU host IP (WireGuard IP) to proxy connections to
sfuPort int // Local SFU signaling port to proxy WebSocket connections to
turnDomain string // TURN server domain for building URIs
turnSecret string // HMAC-SHA1 shared secret for TURN credential generation
@ -23,13 +24,18 @@ type WebRTCHandlers struct {
// NewWebRTCHandlers creates a new WebRTCHandlers instance.
func NewWebRTCHandlers(
logger *logging.ColoredLogger,
sfuHost string,
sfuPort int,
turnDomain string,
turnSecret string,
proxyWS func(w http.ResponseWriter, r *http.Request, targetHost string) bool,
) *WebRTCHandlers {
if sfuHost == "" {
sfuHost = "127.0.0.1"
}
return &WebRTCHandlers{
logger: logger,
sfuHost: sfuHost,
sfuPort: sfuPort,
turnDomain: turnDomain,
turnSecret: turnSecret,

36
scripts/monitor-webrtc.sh Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Monitor WebRTC endpoints every 10 seconds
# Usage: ./scripts/monitor-webrtc.sh
API_KEY="ak_SiODBDJFHrfE3HxjOtSe4CYm:anchat-test"
BASE="https://ns-anchat-test.orama-devnet.network"
while true; do
TS=$(date '+%H:%M:%S')
# 1. Health check
HEALTH=$(curl -sk -o /dev/null -w "%{http_code}" "${BASE}/v1/health")
# 2. TURN credentials
CREDS=$(curl -sk -o /dev/null -w "%{http_code}" -X POST -H "X-API-Key: ${API_KEY}" "${BASE}/v1/webrtc/turn/credentials")
# 3. WebSocket signal (connect, send join, read response, disconnect)
WS_OUT=$(echo '{"type":"join","data":{"roomId":"monitor-room","userId":"monitor"}}' \
| timeout 5 websocat -k --no-close -t "wss://ns-anchat-test.orama-devnet.network/v1/webrtc/signal?token=${API_KEY}" 2>&1 \
| head -1)
if echo "$WS_OUT" | grep -q '"welcome"'; then
SIGNAL="OK"
else
SIGNAL="FAIL"
fi
# Print status
if [ "$HEALTH" = "200" ] && [ "$CREDS" = "200" ] && [ "$SIGNAL" = "OK" ]; then
echo "$TS health=$HEALTH creds=$CREDS signal=$SIGNAL"
else
echo "$TS health=$HEALTH creds=$CREDS signal=$SIGNAL ✗ PROBLEM"
fi
sleep 10
done