useWebSocket.ts 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import { useEffect, useRef, useState } from "react";
  2. interface UseWebSocketOptions {
  3. onMessage?: (data: unknown) => void;
  4. onError?: (error: Event) => void;
  5. onClose?: () => void;
  6. sinceEventId?: number;
  7. }
  8. export const useWebSocket = (traceId: string | null, options: UseWebSocketOptions = {}) => {
  9. const wsRef = useRef<WebSocket | null>(null);
  10. const [connected, setConnected] = useState(false);
  11. const { onMessage, onError, onClose, sinceEventId = 0 } = options;
  12. useEffect(() => {
  13. if (!traceId) return;
  14. const httpBase =
  15. (typeof window !== "undefined"
  16. ? (window as unknown as { CONFIG?: { API_BASE_URL?: string } }).CONFIG?.API_BASE_URL
  17. : undefined) ||
  18. (typeof import.meta !== "undefined" && import.meta.env && import.meta.env.VITE_API_BASE_URL
  19. ? import.meta.env.VITE_API_BASE_URL
  20. : `${window.location.protocol}//${window.location.hostname}:8000`);
  21. const wsBase = httpBase.replace(/^http(s?):\/\//, "ws$1://").replace(/\/+$/, "");
  22. const url = `${wsBase}/api/traces/${traceId}/watch?since_event_id=${sinceEventId}`;
  23. const ws = new WebSocket(url);
  24. let pingTimer: number | null = null;
  25. ws.onopen = () => {
  26. setConnected(true);
  27. pingTimer = window.setInterval(() => {
  28. if (ws.readyState === WebSocket.OPEN) {
  29. ws.send("ping");
  30. }
  31. }, 15000);
  32. };
  33. ws.onmessage = (event) => {
  34. try {
  35. const data = JSON.parse(event.data);
  36. onMessage?.(data);
  37. } catch {
  38. onMessage?.(event.data);
  39. }
  40. };
  41. ws.onerror = (error) => {
  42. onError?.(error);
  43. };
  44. ws.onclose = () => {
  45. setConnected(false);
  46. if (pingTimer) {
  47. window.clearInterval(pingTimer);
  48. pingTimer = null;
  49. }
  50. onClose?.();
  51. };
  52. wsRef.current = ws;
  53. return () => {
  54. if (pingTimer) {
  55. window.clearInterval(pingTimer);
  56. pingTimer = null;
  57. }
  58. ws.close();
  59. };
  60. }, [traceId, onMessage, onError, onClose, sinceEventId]);
  61. return { connected, ws: wsRef.current };
  62. };