♻️ 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
+10 -41
View File
@@ -1,8 +1,8 @@
'use client';
import { filterServerList, queryNodeTag } from '@/services/admin/server';
import { useNode } from '@/store/node';
import { useServer } from '@/store/server';
import { zodResolver } from '@hookform/resolvers/zod';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import {
Form,
@@ -40,8 +40,6 @@ export type ProtocolName =
| 'tuic'
| 'anytls';
type ServerRow = API.Server;
const buildSchema = (t: ReturnType<typeof useTranslations>) =>
z.object({
name: z.string().trim().min(1, t('errors.nameRequired')),
@@ -103,38 +101,12 @@ export default function NodeForm(props: {
const serverId = form.watch('server_id');
const { data } = useQuery({
enabled: open,
queryKey: ['filterServerListAll'],
queryFn: async () => {
const { data } = await filterServerList({ page: 1, size: 999999999 });
return data?.data?.list || [];
},
});
const servers: ServerRow[] = data as ServerRow[];
const { servers, getAvailableProtocols } = useServer();
const { tags } = useNode();
const { data: tagsData } = useQuery({
enabled: open,
queryKey: ['queryNodeTag'],
queryFn: async () => {
const { data } = await queryNodeTag();
return data?.data?.tags || [];
},
});
const existingTags: string[] = tagsData as string[];
const existingTags: string[] = tags || [];
const currentServer = useMemo(() => servers?.find((s) => s.id === serverId), [servers, serverId]);
const availableProtocols = useMemo(() => {
if (!currentServer?.protocols) return [];
return currentServer.protocols
.filter((p) => p.enable !== false)
.map((p) => ({
protocol: p.type,
port: p.port,
}));
}, [currentServer]);
const availableProtocols = getAvailableProtocols(serverId);
useEffect(() => {
if (initialValues) {
@@ -176,12 +148,11 @@ export default function NodeForm(props: {
fieldsToFill.push('address');
}
const protocols =
(selectedServer.protocols as Array<{ type: ProtocolName; port?: number }>) || [];
const protocols = getAvailableProtocols(id);
const firstProtocol = protocols[0];
if (firstProtocol && (!currentValues.protocol || autoFilledFields.has('protocol'))) {
form.setValue('protocol', firstProtocol.type, { shouldDirty: false });
form.setValue('protocol', firstProtocol.protocol, { shouldDirty: false });
fieldsToFill.push('protocol');
if (!currentValues.port || currentValues.port === 0 || autoFilledFields.has('port')) {
@@ -203,7 +174,7 @@ export default function NodeForm(props: {
const protocol = (nextProto || '') as ProtocolName | '';
form.setValue('protocol', protocol);
if (!protocol || !currentServer) {
if (!protocol || !serverId) {
removeAutoFilledField('protocol');
return;
}
@@ -214,9 +185,7 @@ export default function NodeForm(props: {
removeAutoFilledField('protocol');
if (!currentValues.port || currentValues.port === 0 || isPortAutoFilled) {
const protocolData = (
currentServer.protocols as Array<{ type: ProtocolName; port?: number }>
)?.find((p) => p.type === protocol);
const protocolData = availableProtocols.find((p) => p.protocol === protocol);
if (protocolData) {
const port = protocolData.port || 0;
+16 -25
View File
@@ -5,12 +5,12 @@ import {
createNode,
deleteNode,
filterNodeList,
filterServerList,
resetSortWithNode,
toggleNodeStatus,
updateNode,
} from '@/services/admin/server';
import { useQuery } from '@tanstack/react-query';
import { useNode } from '@/store/node';
import { useServer } from '@/store/server';
import { Badge } from '@workspace/ui/components/badge';
import { Button } from '@workspace/ui/components/button';
import { Switch } from '@workspace/ui/components/switch';
@@ -25,24 +25,9 @@ export default function NodesPage() {
const ref = useRef<ProTableActions>(null);
const [loading, setLoading] = useState(false);
const { data: servers = [] } = useQuery({
queryKey: ['filterServerListAll'],
queryFn: async () => {
const { data } = await filterServerList({ page: 1, size: 999999999 });
return data?.data?.list || [];
},
});
const getServerName = (id?: number) =>
id ? (servers.find((s) => s.id === id)?.name ?? `#${id}`) : '—';
const getServerOriginAddr = (id?: number) =>
id ? (servers.find((s) => s.id === id)?.address ?? '—') : '—';
const getProtocolOriginPort = (id?: number, proto?: string) => {
if (!id || !proto) return '—';
const hit = servers.find((s) => s.id === id)?.protocols?.find((p) => (p as any).type === proto);
const p = (hit as any)?.port as number | undefined;
return typeof p === 'number' ? String(p) : '—';
};
// Use our zustand store for server data
const { getServerName, getServerAddress, getProtocolPort } = useServer();
const { fetchNodes } = useNode();
return (
<ProTable<API.Node, { search: string }>
@@ -69,6 +54,7 @@ export default function NodesPage() {
await createNode(body);
toast.success(t('created'));
ref.current?.refresh();
fetchNodes();
setLoading(false);
return true;
} catch (e) {
@@ -90,6 +76,7 @@ export default function NodesPage() {
await toggleNodeStatus({ id: row.original.id, enable: v });
toast.success(v ? t('enabled_on') : t('enabled_off'));
ref.current?.refresh();
fetchNodes();
}}
/>
),
@@ -106,14 +93,14 @@ export default function NodesPage() {
id: 'server_id',
header: t('server'),
cell: ({ row }) => (
<div className='flex flex-wrap gap-2'>
<div className='space-y-1'>
<Badge variant='outline'>
{getServerName(row.original.server_id)} ·{' '}
{getServerOriginAddr(row.original.server_id)}
{getServerName(row.original.server_id)} : {getServerAddress(row.original.server_id)}
</Badge>
<br />
<Badge variant='outline'>
{row.original.protocol || '—'} ·{' '}
{getProtocolOriginPort(row.original.server_id, row.original.protocol)}
{row.original.protocol || '—'} :{' '}
{getProtocolPort(row.original.server_id, row.original.protocol)}
</Badge>
</div>
),
@@ -163,6 +150,7 @@ export default function NodesPage() {
await updateNode(body);
toast.success(t('updated'));
ref.current?.refresh();
fetchNodes();
setLoading(false);
return true;
} catch (e) {
@@ -180,6 +168,7 @@ export default function NodesPage() {
await deleteNode({ id: row.id } as any);
toast.success(t('deleted'));
ref.current?.refresh();
fetchNodes();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
@@ -195,6 +184,7 @@ export default function NodesPage() {
});
toast.success(t('copied'));
ref.current?.refresh();
fetchNodes();
}}
>
{t('copy')}
@@ -211,6 +201,7 @@ export default function NodesPage() {
await Promise.all(rows.map((r) => deleteNode({ id: r.id } as any)));
toast.success(t('deleted'));
ref.current?.refresh();
fetchNodes();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}