mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-16 17:46:08 +00:00
d2ba6c9fc2
onOutbounds wrote the raw WebSocket payload straight into the outboundsTraffic cache, unlike the sibling onNodes/onInbounds handlers which first check Array.isArray. A malformed non-array push (for example an object) would land in the cache with staleTime Infinity; consumers that call .find()/.map() on the outbounds list would then throw and crash the Outbounds tab. Add the same Array.isArray guard so a bad push is ignored.
66 lines
2.0 KiB
TypeScript
66 lines
2.0 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useQueryClient } from '@tanstack/react-query';
|
|
|
|
import { getSharedWebSocketClient } from '@/api/websocket';
|
|
import { keys } from '@/api/queryKeys';
|
|
import { isRecentLocalInvalidate } from '@/api/invalidationTracker';
|
|
|
|
type Handler = (payload: unknown) => void;
|
|
|
|
let invalidateTimer: number | null = null;
|
|
|
|
export function useWebSocketBridge() {
|
|
const queryClient = useQueryClient();
|
|
|
|
useEffect(() => {
|
|
const client = getSharedWebSocketClient();
|
|
|
|
const onInvalidate: Handler = (payload) => {
|
|
const p = payload as { type?: string } | undefined;
|
|
if (!p || (p.type !== 'inbounds' && p.type !== 'clients')) return;
|
|
if (invalidateTimer != null) clearTimeout(invalidateTimer);
|
|
invalidateTimer = window.setTimeout(() => {
|
|
invalidateTimer = null;
|
|
if (isRecentLocalInvalidate()) return;
|
|
if (p.type === 'inbounds') {
|
|
queryClient.invalidateQueries({ queryKey: ['inbounds'] });
|
|
} else {
|
|
queryClient.invalidateQueries({ queryKey: ['clients'] });
|
|
}
|
|
}, 200);
|
|
};
|
|
|
|
const onOutbounds: Handler = (payload) => {
|
|
if (!Array.isArray(payload)) return;
|
|
queryClient.setQueryData(keys.xray.outboundsTraffic(), payload);
|
|
};
|
|
|
|
const onNodes: Handler = (payload) => {
|
|
if (!Array.isArray(payload)) return;
|
|
queryClient.setQueryData(keys.nodes.list(), payload);
|
|
};
|
|
|
|
const onInbounds: Handler = (payload) => {
|
|
if (!Array.isArray(payload)) return;
|
|
queryClient.setQueryData(keys.inbounds.slim(), payload);
|
|
};
|
|
|
|
client.on('invalidate', onInvalidate);
|
|
client.on('outbounds', onOutbounds);
|
|
client.on('nodes', onNodes);
|
|
client.on('inbounds', onInbounds);
|
|
client.connect();
|
|
|
|
return () => {
|
|
client.off('invalidate', onInvalidate);
|
|
client.off('outbounds', onOutbounds);
|
|
client.off('nodes', onNodes);
|
|
client.off('inbounds', onInbounds);
|
|
if (invalidateTimer != null) {
|
|
clearTimeout(invalidateTimer);
|
|
invalidateTimer = null;
|
|
}
|
|
};
|
|
}, [queryClient]);
|
|
}
|