mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-25 12:01:04 +02:00
fix(ui): use direct localhost WebSocket signaling
This commit is contained in:
parent
98962da74f
commit
98e57457bb
2 changed files with 69 additions and 36 deletions
|
|
@ -8,35 +8,20 @@ const nextConfig: NextConfig = {
|
|||
serverSourceMaps: true,
|
||||
},
|
||||
async rewrites() {
|
||||
return {
|
||||
// beforeFiles runs before Next.js route handlers, so the WebSocket
|
||||
// signaling path is proxied straight to the backend instead of being
|
||||
// swallowed by api/v1/[...path]/route.ts — a Route Handler that proxies
|
||||
// HTTP fine but CANNOT upgrade WebSocket connections. The pre-1.34.0
|
||||
// /api proxy *rewrite* used to carry this upgrade; removing it broke all
|
||||
// local web calls (dograh #425). Scoped to /ws/ so HTTP /api/v1 still
|
||||
// flows through the route handler (auth/cookie handling intact).
|
||||
beforeFiles: [
|
||||
{
|
||||
source: "/api/v1/ws/:path*",
|
||||
destination: `${process.env.BACKEND_URL || 'http://localhost:8000'}/api/v1/ws/:path*`,
|
||||
},
|
||||
],
|
||||
afterFiles: [
|
||||
{
|
||||
source: "/ingest/static/:path*",
|
||||
destination: "https://us-assets.i.posthog.com/static/:path*",
|
||||
},
|
||||
{
|
||||
source: "/ingest/:path*",
|
||||
destination: "https://us.i.posthog.com/:path*",
|
||||
},
|
||||
{
|
||||
source: "/ingest/decide",
|
||||
destination: "https://us.i.posthog.com/decide",
|
||||
},
|
||||
],
|
||||
};
|
||||
return [
|
||||
{
|
||||
source: "/ingest/static/:path*",
|
||||
destination: "https://us-assets.i.posthog.com/static/:path*",
|
||||
},
|
||||
{
|
||||
source: "/ingest/:path*",
|
||||
destination: "https://us.i.posthog.com/:path*",
|
||||
},
|
||||
{
|
||||
source: "/ingest/decide",
|
||||
destination: "https://us.i.posthog.com/decide",
|
||||
},
|
||||
];
|
||||
},
|
||||
// This is required to support PostHog trailing slash API requests
|
||||
skipTrailingSlashRedirect: true,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,34 @@ const HANDLED_SERVICE_ERROR_TYPES = new Set([
|
|||
'quota_check_failed',
|
||||
]);
|
||||
|
||||
const LOCALHOST_API_BASE_URL = 'http://localhost:8000';
|
||||
const LOCALHOST_API_HEALTH_URL = `${LOCALHOST_API_BASE_URL}/api/v1/health`;
|
||||
const LOCALHOST_API_PROBE_TIMEOUT_MS = 1500;
|
||||
|
||||
function isLocalhostUi() {
|
||||
if (typeof window === 'undefined') return false;
|
||||
|
||||
return ['localhost', '127.0.0.1', '::1'].includes(window.location.hostname);
|
||||
}
|
||||
|
||||
async function probeLocalhostApi() {
|
||||
const controller = new AbortController();
|
||||
const timeout = window.setTimeout(() => controller.abort(), LOCALHOST_API_PROBE_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const response = await fetch(LOCALHOST_API_HEALTH_URL, {
|
||||
cache: 'no-store',
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
window.clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
export const useWebSocketRTC = ({ workflowId, workflowRunId, accessToken, initialContextVariables, onNodeTransition }: UseWebSocketRTCProps) => {
|
||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('idle');
|
||||
const [connectionActive, setConnectionActive] = useState(false);
|
||||
|
|
@ -108,10 +136,26 @@ export const useWebSocketRTC = ({ workflowId, workflowRunId, accessToken, initia
|
|||
const currentAllowInterruptRef = useRef<boolean | undefined>(undefined);
|
||||
const interruptWarningShownRef = useRef(false);
|
||||
|
||||
// Get WebSocket URL from client configuration
|
||||
const getWebSocketUrl = useCallback(() => {
|
||||
// Get base URL from client configuration
|
||||
const baseUrl = client.getConfig().baseUrl || 'http://127.0.0.1:8000';
|
||||
const getWebSocketUrl = useCallback(async () => {
|
||||
let baseUrl = client.getConfig().baseUrl || 'http://127.0.0.1:8000';
|
||||
|
||||
if (isLocalhostUi()) {
|
||||
// Local Docker exposes the API on localhost:8000 while the UI runs
|
||||
// on localhost:3010. WebSocket upgrades cannot pass through the
|
||||
// Next.js route-handler HTTP proxy, so local browser calls should
|
||||
// connect to the API directly when that port is available. A
|
||||
// Next.js rewrite/proxy for the upgrade was considered, but we
|
||||
// keep the WebRTC signaling path direct so signaling and the API's
|
||||
// ICE/WebRTC handling terminate at the same local endpoint.
|
||||
const localhostApiReachable = await probeLocalhostApi();
|
||||
|
||||
if (!localhostApiReachable) {
|
||||
throw new Error('Dograh API is not reachable at http://localhost:8000. Ensure the api container is running and port 8000 is published.');
|
||||
}
|
||||
|
||||
baseUrl = LOCALHOST_API_BASE_URL;
|
||||
}
|
||||
|
||||
// Convert HTTP to WS protocol
|
||||
const wsUrl = baseUrl.replace(/^http/, 'ws');
|
||||
return `${wsUrl}/api/v1/ws/signaling/${workflowId}/${workflowRunId}?token=${accessToken}`;
|
||||
|
|
@ -292,9 +336,10 @@ export const useWebSocketRTC = ({ workflowId, workflowRunId, accessToken, initia
|
|||
return pc;
|
||||
};
|
||||
|
||||
const connectWebSocket = useCallback(() => {
|
||||
const connectWebSocket = useCallback(async () => {
|
||||
const wsUrl = await getWebSocketUrl();
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const wsUrl = getWebSocketUrl();
|
||||
logger.info(`Connecting to WebSocket: ${wsUrl}`);
|
||||
|
||||
const ws = new WebSocket(wsUrl);
|
||||
|
|
@ -307,7 +352,7 @@ export const useWebSocketRTC = ({ workflowId, workflowRunId, accessToken, initia
|
|||
|
||||
ws.onerror = (error) => {
|
||||
logger.error('WebSocket error:', error);
|
||||
reject(error);
|
||||
reject(new Error(`WebSocket connection failed at ${wsUrl}`));
|
||||
};
|
||||
|
||||
ws.onclose = (event) => {
|
||||
|
|
@ -774,6 +819,9 @@ export const useWebSocketRTC = ({ workflowId, workflowRunId, accessToken, initia
|
|||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to start connection:', error);
|
||||
if (error instanceof Error) {
|
||||
setPermissionError(error.message);
|
||||
}
|
||||
setConnectionStatus('failed');
|
||||
} finally {
|
||||
setIsStarting(false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue