useGeodata.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { keepPreviousData, useMutation, useQuery } from '@tanstack/react-query';
  2. import { z } from 'zod';
  3. import { keys } from '@/api/queryKeys';
  4. import {
  5. GeoCategoryPageSchema,
  6. GeoEntryPageSchema,
  7. GeoFileSchema,
  8. GeodataTokenIssueSchema,
  9. } from '@/generated/zod';
  10. import type { GeoCategoryPage, GeoEntryPage, GeoFile, GeodataTokenIssue } from '@/generated/types';
  11. import { HttpUtil } from '@/utils';
  12. import { parseMsg } from '@/utils/zodValidate';
  13. const GeoFileListSchema = z.array(GeoFileSchema);
  14. const GeodataTokenIssueListSchema = z.array(GeodataTokenIssueSchema);
  15. const EMPTY_CATEGORY_PAGE: GeoCategoryPage = { total: 0, items: [] };
  16. const EMPTY_ENTRY_PAGE: GeoEntryPage = { total: 0, items: [] };
  17. export type GeoTokenKind = 'ip' | 'domain';
  18. export interface ValidateGeoTokensInput {
  19. tokens: string[];
  20. kind: GeoTokenKind;
  21. }
  22. async function fetchGeodataFiles(): Promise<GeoFile[]> {
  23. const msg = await HttpUtil.get('/panel/api/xray/geodata/files', undefined, { silent: true });
  24. if (!msg?.success) throw new Error(msg?.msg || 'Failed to fetch geodata files');
  25. const validated = parseMsg(msg, GeoFileListSchema, 'xray/geodata/files');
  26. return Array.isArray(validated.obj) ? validated.obj : [];
  27. }
  28. async function fetchGeodataCategories(file: string, query: string): Promise<GeoCategoryPage> {
  29. const msg = await HttpUtil.get(
  30. '/panel/api/xray/geodata/categories',
  31. { file, q: query },
  32. { silent: true },
  33. );
  34. if (!msg?.success) throw new Error(msg?.msg || 'Failed to fetch geodata categories');
  35. const validated = parseMsg(msg, GeoCategoryPageSchema, 'xray/geodata/categories');
  36. return validated.obj ?? EMPTY_CATEGORY_PAGE;
  37. }
  38. async function fetchGeodataEntries(
  39. file: string,
  40. code: string,
  41. query: string,
  42. offset: number,
  43. limit: number,
  44. ): Promise<GeoEntryPage> {
  45. const msg = await HttpUtil.get(
  46. '/panel/api/xray/geodata/entries',
  47. { file, code, q: query, offset, limit },
  48. { silent: true },
  49. );
  50. if (!msg?.success) throw new Error(msg?.msg || 'Failed to fetch geodata entries');
  51. const validated = parseMsg(msg, GeoEntryPageSchema, 'xray/geodata/entries');
  52. return validated.obj ?? EMPTY_ENTRY_PAGE;
  53. }
  54. export function useGeodataFiles(enabled: boolean) {
  55. return useQuery({
  56. queryKey: keys.xray.geodata.files(),
  57. queryFn: fetchGeodataFiles,
  58. enabled,
  59. staleTime: 5 * 60 * 1000,
  60. });
  61. }
  62. export function useGeodataCategories(file: string | undefined, query: string, enabled: boolean) {
  63. return useQuery({
  64. queryKey: keys.xray.geodata.categories(file ?? '', query),
  65. queryFn: () => fetchGeodataCategories(file ?? '', query),
  66. enabled: enabled && !!file,
  67. staleTime: 5 * 60 * 1000,
  68. placeholderData: keepPreviousData,
  69. });
  70. }
  71. export function useGeodataEntries(
  72. file: string | undefined,
  73. code: string | undefined,
  74. query: string,
  75. offset: number,
  76. limit: number,
  77. enabled: boolean,
  78. ) {
  79. return useQuery({
  80. queryKey: keys.xray.geodata.entries(file ?? '', code ?? '', query, offset, limit),
  81. queryFn: () => fetchGeodataEntries(file ?? '', code ?? '', query, offset, limit),
  82. enabled: enabled && !!file && !!code,
  83. placeholderData: keepPreviousData,
  84. });
  85. }
  86. export function useValidateGeoTokens() {
  87. return useMutation<GeodataTokenIssue[], Error, ValidateGeoTokensInput>({
  88. mutationFn: async ({ tokens, kind }) => {
  89. const msg = await HttpUtil.post(
  90. '/panel/api/xray/geodata/validate',
  91. { tokens: tokens.join(','), kind },
  92. { silent: true },
  93. );
  94. if (!msg?.success) throw new Error(msg?.msg || 'Failed to validate geodata tokens');
  95. const validated = parseMsg(msg, GeodataTokenIssueListSchema, 'xray/geodata/validate');
  96. return Array.isArray(validated.obj) ? validated.obj : [];
  97. },
  98. });
  99. }