From 3b72f353063dc61ff67055a87d2a31057d6cc96e Mon Sep 17 00:00:00 2001 From: ruv Date: Sun, 1 Mar 2026 02:09:23 -0500 Subject: [PATCH] fix: UI auto-detects server port from page origin (#55) The UI had hardcoded localhost:8080 for HTTP and localhost:8765 for WebSocket, causing "Backend unavailable" when served from Docker (port 3000) or any non-default port. Changes: - api.config.js: BASE_URL now uses window.location.origin instead of hardcoded localhost:8080 - api.config.js: buildWsUrl() uses window.location.host instead of hardcoded localhost:8080 - sensing.service.js: WebSocket URL derived from page origin instead of hardcoded localhost:8765 - main.rs: Added /ws/sensing route to the HTTP server so WebSocket and REST are reachable on a single port Fixes #55 Co-Authored-By: claude-flow --- .../wifi-densepose-sensing-server/src/main.rs | 2 ++ ui/config/api.config.js | 16 +++++++++++----- ui/services/sensing.service.js | 6 +++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/src/main.rs b/rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/src/main.rs index 7985449..cbf728e 100644 --- a/rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/src/main.rs +++ b/rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/src/main.rs @@ -2091,6 +2091,8 @@ async fn main() { // Stream endpoints .route("/api/v1/stream/status", get(stream_status)) .route("/api/v1/stream/pose", get(ws_pose_handler)) + // Sensing WebSocket on the HTTP port so the UI can reach it without a second port + .route("/ws/sensing", get(ws_sensing_handler)) // Static UI files .nest_service("/ui", ServeDir::new(&ui_path)) .layer(SetResponseHeaderLayer::overriding( diff --git a/ui/config/api.config.js b/ui/config/api.config.js index fee9f42..c475099 100644 --- a/ui/config/api.config.js +++ b/ui/config/api.config.js @@ -1,11 +1,17 @@ // API Configuration for WiFi-DensePose UI +// Auto-detect the backend URL from the page origin so the UI works whether +// served from Docker (:3000), local dev (:8080), or any other port. +const _origin = (typeof window !== 'undefined' && window.location && window.location.origin) + ? window.location.origin + : 'http://localhost:3000'; + export const API_CONFIG = { - BASE_URL: 'http://localhost:8080', // Rust sensing server port + BASE_URL: _origin, API_VERSION: '/api/v1', WS_PREFIX: 'ws://', WSS_PREFIX: 'wss://', - + // Mock server configuration (only for testing) MOCK_SERVER: { ENABLED: false, // Set to true only for testing without backend @@ -114,9 +120,9 @@ export function buildWsUrl(endpoint, params = {}) { const protocol = (isSecure || !isLocalhost) ? API_CONFIG.WSS_PREFIX : API_CONFIG.WS_PREFIX; - - // Match Rust sensing server port - const host = 'localhost:8080'; + + // Derive host from the page origin so it works on any port (Docker :3000, dev :8080, etc.) + const host = window.location.host; let url = `${protocol}${host}${endpoint}`; // Add query parameters diff --git a/ui/services/sensing.service.js b/ui/services/sensing.service.js index 06e11f8..d789a3c 100644 --- a/ui/services/sensing.service.js +++ b/ui/services/sensing.service.js @@ -8,7 +8,11 @@ * always shows something. */ -const SENSING_WS_URL = 'ws://localhost:8765/ws/sensing'; +// Derive WebSocket URL from the page origin so it works on any port +// (Docker :3000, native :8080, etc.) +const _wsProto = (typeof window !== 'undefined' && window.location.protocol === 'https:') ? 'wss:' : 'ws:'; +const _wsHost = (typeof window !== 'undefined' && window.location.host) ? window.location.host : 'localhost:3000'; +const SENSING_WS_URL = `${_wsProto}//${_wsHost}/ws/sensing`; const RECONNECT_DELAYS = [1000, 2000, 4000, 8000, 16000]; const MAX_RECONNECT_ATTEMPTS = 10; const SIMULATION_INTERVAL = 500; // ms