- Delete the xray settings page (routes/menu/title), geodata UI/service, XraySettingController/Service and routing-sync write paths; the stored xrayTemplateConfig row loses its last readers (GetXrayConfigTemplate now returns the embedded factory template; B1 already deleted the row). - Settings GeneralTab gains the four trafficStatsSwitches switches (settings.trafficStatsSwitches, B1) with the dual warning copy; entity AllSetting carries the key end to end (TestTrafficStatsSwitchesPlumbing). - Tag-directory consumers (panelOutbound, mtproto egress, node/host outbound pickers) move from POST /xray/ to GET /server/outboundDirectory. - Shared Mux/Sockopt forms and balancer helpers relocated to lib/xray; converter preserves sniffing ips/domains exclusions into the panel sidecar instead of dropping them (B2 gap closed by retention). - Freedom final-rules DB fix seeders retired; getDefaultJsonConfig endpoint removed (no in-repo consumer); locales pruned to referenced keys (pages.xray 331 -> 68) plus the five new settings keys.
59 lines
1.8 KiB
TypeScript
59 lines
1.8 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 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('nodes', onNodes);
|
|
client.on('inbounds', onInbounds);
|
|
client.connect();
|
|
|
|
return () => {
|
|
client.off('invalidate', onInvalidate);
|
|
client.off('nodes', onNodes);
|
|
client.off('inbounds', onInbounds);
|
|
if (invalidateTimer != null) {
|
|
clearTimeout(invalidateTimer);
|
|
invalidateTimer = null;
|
|
}
|
|
};
|
|
}, [queryClient]);
|
|
}
|