Files
l-ui/frontend/src/api/websocketBridge.ts
T
Muse 277838d7ca ITEM6 B4: frontend reshaping — xray page retired, stats switches to settings, template read paths closed
- 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.
2026-10-22 23:40:54 +00:00

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]);
}