| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- import { useEffect, useRef, useState } from "react";
- interface UseWebSocketOptions {
- onMessage?: (data: unknown) => void;
- onError?: (error: Event) => void;
- onClose?: () => void;
- sinceEventId?: number;
- }
- export const useWebSocket = (traceId: string | null, options: UseWebSocketOptions = {}) => {
- const wsRef = useRef<WebSocket | null>(null);
- const [connected, setConnected] = useState(false);
- const { onMessage, onError, onClose, sinceEventId = 0 } = options;
- useEffect(() => {
- if (!traceId) return;
- const httpBase =
- (typeof window !== "undefined"
- ? (window as unknown as { CONFIG?: { API_BASE_URL?: string } }).CONFIG?.API_BASE_URL
- : undefined) ||
- (typeof import.meta !== "undefined" && import.meta.env && import.meta.env.VITE_API_BASE_URL
- ? import.meta.env.VITE_API_BASE_URL
- : `${window.location.protocol}//${window.location.hostname}:8000`);
- const wsBase = httpBase.replace(/^http(s?):\/\//, "ws$1://").replace(/\/+$/, "");
- const url = `${wsBase}/api/traces/${traceId}/watch?since_event_id=${sinceEventId}`;
- const ws = new WebSocket(url);
- let pingTimer: number | null = null;
- ws.onopen = () => {
- setConnected(true);
- pingTimer = window.setInterval(() => {
- if (ws.readyState === WebSocket.OPEN) {
- ws.send("ping");
- }
- }, 15000);
- };
- ws.onmessage = (event) => {
- try {
- const data = JSON.parse(event.data);
- onMessage?.(data);
- } catch {
- onMessage?.(event.data);
- }
- };
- ws.onerror = (error) => {
- onError?.(error);
- };
- ws.onclose = () => {
- setConnected(false);
- if (pingTimer) {
- window.clearInterval(pingTimer);
- pingTimer = null;
- }
- onClose?.();
- };
- wsRef.current = ws;
- return () => {
- if (pingTimer) {
- window.clearInterval(pingTimer);
- pingTimer = null;
- }
- ws.close();
- };
- }, [traceId, onMessage, onError, onClose, sinceEventId]);
- return { connected, ws: wsRef.current };
- };
|