♻️ refactor: Replace useQuery with Zustand store for subscription and node data management

This commit is contained in:
web
2025-09-17 02:20:54 -07:00
parent bdd53b1551
commit c6dd0b63f2
28 changed files with 448 additions and 359 deletions
+113
View File
@@ -0,0 +1,113 @@
import { filterNodeList, queryNodeTag } from '@/services/admin/server';
import { create } from 'zustand';
interface NodeState {
// Data
nodes: API.Node[];
tags: string[];
// Actions
fetchNodes: () => Promise<void>;
fetchTags: () => Promise<void>;
// Getters
getNodeById: (nodeId: number) => API.Node | undefined;
isProtocolUsedInNodes: (serverId: number, protocolType: string) => boolean;
isServerReferencedByNodes: (serverId: number) => boolean;
getNodesByTag: (tag: string) => API.Node[];
getNodesWithoutTags: () => API.Node[];
getNodeTags: () => string[];
getAllAvailableTags: () => string[];
}
export const useNodeStore = create<NodeState>((set, get) => ({
// Initial state
nodes: [],
tags: [],
// Actions
fetchNodes: async () => {
try {
const { data } = await filterNodeList({ page: 1, size: 999999999 });
set({ nodes: data?.data?.list || [] });
} catch (error) {
// Handle error silently
}
},
fetchTags: async () => {
try {
const { data } = await queryNodeTag();
set({ tags: data?.data?.tags || [] });
} catch (error) {
// Handle error silently
}
},
// Getters
getNodeById: (nodeId: number) => {
return get().nodes.find((n) => n.id === nodeId);
},
isProtocolUsedInNodes: (serverId: number, protocolType: string) => {
return get().nodes.some(
(node) => node.server_id === serverId && node.protocol === protocolType,
);
},
isServerReferencedByNodes: (serverId: number) => {
return get().nodes.some((node) => node.server_id === serverId);
},
getNodesByTag: (tag: string) => {
return get().nodes.filter((node) => (node.tags || []).includes(tag));
},
getNodesWithoutTags: () => {
return get().nodes.filter((node) => (node.tags || []).length === 0);
},
getNodeTags: () => {
return Array.from(
new Set(
get()
.nodes.flatMap((node) => (Array.isArray(node.tags) ? node.tags : []))
.filter(Boolean),
),
) as string[];
},
getAllAvailableTags: () => {
const nodeExtractedTags = get().getNodeTags();
const allApiTags = get().tags;
return Array.from(new Set([...allApiTags, ...nodeExtractedTags])).filter(Boolean);
},
}));
export const useNode = () => {
const store = useNodeStore();
// Auto-fetch nodes and tags
if (store.nodes.length === 0) {
store.fetchNodes();
}
if (store.tags.length === 0) {
store.fetchTags();
}
return {
nodes: store.nodes,
tags: store.tags,
fetchNodes: store.fetchNodes,
fetchTags: store.fetchTags,
getNodeById: store.getNodeById,
isProtocolUsedInNodes: store.isProtocolUsedInNodes,
isServerReferencedByNodes: store.isServerReferencedByNodes,
getNodesByTag: store.getNodesByTag,
getNodesWithoutTags: store.getNodesWithoutTags,
getNodeTags: store.getNodeTags,
getAllAvailableTags: store.getAllAvailableTags,
};
};
export default useNodeStore;
+94
View File
@@ -0,0 +1,94 @@
import { filterServerList } from '@/services/admin/server';
import { create } from 'zustand';
interface ServerState {
// Data
servers: API.Server[];
// Actions
fetchServers: () => Promise<void>;
// Getters
getServerById: (serverId: number) => API.Server | undefined;
getServerName: (serverId?: number) => string;
getServerAddress: (serverId?: number) => string;
getServerEnabledProtocols: (serverId: number) => API.Protocol[];
getProtocolPort: (serverId?: number, protocol?: string) => string;
getAvailableProtocols: (serverId?: number) => Array<{ protocol: string; port: number }>;
}
export const useServerStore = create<ServerState>((set, get) => ({
// Initial state
servers: [],
// Actions
fetchServers: async () => {
try {
const { data } = await filterServerList({ page: 1, size: 999999999 });
set({ servers: data?.data?.list || [] });
} catch (error) {
// Handle error silently
}
},
// Getters
getServerById: (serverId: number) => {
return get().servers.find((s) => s.id === serverId);
},
getServerName: (serverId?: number) => {
if (!serverId) return '—';
const server = get().servers.find((s) => s.id === serverId);
return server?.name ?? `#${serverId}`;
},
getServerAddress: (serverId?: number) => {
if (!serverId) return '—';
const server = get().servers.find((s) => s.id === serverId);
return server?.address ?? '—';
},
getServerEnabledProtocols: (serverId: number) => {
const server = get().servers.find((s) => s.id === serverId);
return server?.protocols?.filter((p) => p.enable !== false) || [];
},
getProtocolPort: (serverId?: number, protocol?: string) => {
if (!serverId || !protocol) return '—';
const enabledProtocols = get().getServerEnabledProtocols(serverId);
const protocolConfig = enabledProtocols.find((p) => p.type === protocol);
return protocolConfig?.port ? String(protocolConfig.port) : '—';
},
getAvailableProtocols: (serverId?: number) => {
if (!serverId) return [];
return get()
.getServerEnabledProtocols(serverId)
.map((p) => ({
protocol: p.type,
port: p.port,
}));
},
}));
export const useServer = () => {
const store = useServerStore();
// Auto-fetch servers
if (store.servers.length === 0) {
store.fetchServers();
}
return {
servers: store.servers,
fetchServers: store.fetchServers,
getServerById: store.getServerById,
getServerName: store.getServerName,
getServerAddress: store.getServerAddress,
getServerEnabledProtocols: store.getServerEnabledProtocols,
getProtocolPort: store.getProtocolPort,
getAvailableProtocols: store.getAvailableProtocols,
};
};
export default useServerStore;
+58
View File
@@ -0,0 +1,58 @@
import { getSubscribeList } from '@/services/admin/subscribe';
import { create } from 'zustand';
interface SubscribeState {
// Data
subscribes: API.SubscribeItem[];
// Actions
fetchSubscribes: () => Promise<void>;
// Getters
getSubscribeName: (subscribeId?: number) => string;
getSubscribeById: (subscribeId: number) => API.SubscribeItem | undefined;
}
export const useSubscribeStore = create<SubscribeState>((set, get) => ({
// Initial state
subscribes: [],
// Actions
fetchSubscribes: async () => {
try {
const { data } = await getSubscribeList({ page: 1, size: 999999999 });
set({ subscribes: data?.data?.list || [] });
} catch (error) {
// Handle error silently
}
},
// Getters
getSubscribeName: (subscribeId?: number) => {
if (!subscribeId) return 'Unknown';
const subscribe = get().subscribes.find((s) => s.id === subscribeId);
return subscribe?.name ?? `Subscribe ${subscribeId}`;
},
getSubscribeById: (subscribeId: number) => {
return get().subscribes.find((s) => s.id === subscribeId);
},
}));
export const useSubscribe = () => {
const store = useSubscribeStore();
// Auto-fetch subscribes
if (store.subscribes.length === 0) {
store.fetchSubscribes();
}
return {
subscribes: store.subscribes,
fetchSubscribes: store.fetchSubscribes,
getSubscribeName: store.getSubscribeName,
getSubscribeById: store.getSubscribeById,
};
};
export default useSubscribeStore;