websocketBridge.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { useEffect } from 'react';
  2. import { useQueryClient } from '@tanstack/react-query';
  3. import { WebSocketClient } from '@/api/websocket';
  4. import { keys } from '@/api/queryKeys';
  5. import { isRecentLocalInvalidate } from '@/api/invalidationTracker';
  6. type Handler = (payload: unknown) => void;
  7. interface SharedClient {
  8. connect(): void;
  9. on(event: string, fn: Handler): void;
  10. off(event: string, fn: Handler): void;
  11. }
  12. let sharedClient: SharedClient | null = null;
  13. function getSharedClient(): SharedClient {
  14. if (sharedClient) return sharedClient;
  15. const basePath = (typeof window !== 'undefined' && window.X_UI_BASE_PATH) || '';
  16. sharedClient = new WebSocketClient(basePath) as SharedClient;
  17. return sharedClient;
  18. }
  19. let invalidateTimer: number | null = null;
  20. export function useWebSocketBridge() {
  21. const queryClient = useQueryClient();
  22. useEffect(() => {
  23. const client = getSharedClient();
  24. const onInvalidate: Handler = (payload) => {
  25. const p = payload as { type?: string } | undefined;
  26. if (!p || (p.type !== 'inbounds' && p.type !== 'clients')) return;
  27. if (invalidateTimer != null) clearTimeout(invalidateTimer);
  28. invalidateTimer = window.setTimeout(() => {
  29. invalidateTimer = null;
  30. if (isRecentLocalInvalidate()) return;
  31. if (p.type === 'inbounds') {
  32. queryClient.invalidateQueries({ queryKey: ['inbounds'] });
  33. } else {
  34. queryClient.invalidateQueries({ queryKey: ['clients'] });
  35. }
  36. }, 200);
  37. };
  38. const onOutbounds: Handler = (payload) => {
  39. queryClient.setQueryData(keys.xray.outboundsTraffic(), payload);
  40. };
  41. const onNodes: Handler = (payload) => {
  42. if (!Array.isArray(payload)) return;
  43. queryClient.setQueryData(keys.nodes.list(), payload);
  44. };
  45. const onInbounds: Handler = (payload) => {
  46. if (!Array.isArray(payload)) return;
  47. queryClient.setQueryData(keys.inbounds.slim(), payload);
  48. };
  49. client.on('invalidate', onInvalidate);
  50. client.on('outbounds', onOutbounds);
  51. client.on('nodes', onNodes);
  52. client.on('inbounds', onInbounds);
  53. client.connect();
  54. return () => {
  55. client.off('invalidate', onInvalidate);
  56. client.off('outbounds', onOutbounds);
  57. client.off('nodes', onNodes);
  58. client.off('inbounds', onInbounds);
  59. if (invalidateTimer != null) {
  60. clearTimeout(invalidateTimer);
  61. invalidateTimer = null;
  62. }
  63. };
  64. }, [queryClient]);
  65. }