- P1: server.ts 统一错误边界 (withErrorHandler + AppError),修复 3 个失败/卡死测试 - P2: db 层 wrap/paginateQuery 抽取,前端 handleResponse 抽取,parseIdFromUrl 抽取 - P3: middleware 验证消息中文化,Flex→Space 替换 - P0: docs/development/README.md 新增已知设计决策章节 - P3-11 setup 拆分已尝试回退(@testing-library/react preload 依赖无法拆分) - P3-13 config 层测试从本次变更移除
147 lines
4.7 KiB
TypeScript
147 lines
4.7 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
import type {
|
|
CreateProviderRequest,
|
|
Provider,
|
|
ProviderListResponse,
|
|
ProviderOptionsResponse,
|
|
ProviderResponse,
|
|
ProviderTestResponse,
|
|
ProviderTestResultResponse,
|
|
UpdateProviderRequest,
|
|
} from "../../shared/api";
|
|
|
|
import { handleResponse, handleVoidResponse } from "../utils/api";
|
|
|
|
const PROVIDERS_KEY = ["providers"] as const;
|
|
const MODELS_KEY = ["models"] as const;
|
|
|
|
export async function createProvider(data: CreateProviderRequest): Promise<Provider> {
|
|
const response = await fetch("/api/providers", {
|
|
body: JSON.stringify(data),
|
|
headers: { "Content-Type": "application/json" },
|
|
method: "POST",
|
|
});
|
|
return handleResponse(response, (data) => (data as ProviderResponse).provider);
|
|
}
|
|
|
|
export async function deleteProvider(id: string): Promise<void> {
|
|
const response = await fetch(`/api/providers/${id}`, { method: "DELETE" });
|
|
return handleVoidResponse(response);
|
|
}
|
|
|
|
export async function fetchProvider(id: string): Promise<Provider> {
|
|
const response = await fetch(`/api/providers/${id}`);
|
|
return handleResponse(response, (data) => (data as ProviderResponse).provider);
|
|
}
|
|
|
|
export async function fetchProviderList(params: {
|
|
keyword?: string;
|
|
page?: number;
|
|
pageSize?: number;
|
|
}): Promise<ProviderListResponse> {
|
|
const searchParams = new URLSearchParams();
|
|
if (params.page) searchParams.set("page", String(params.page));
|
|
if (params.pageSize) searchParams.set("pageSize", String(params.pageSize));
|
|
if (params.keyword) searchParams.set("keyword", params.keyword);
|
|
const qs = searchParams.toString();
|
|
const url = `/api/providers${qs ? `?${qs}` : ""}`;
|
|
const response = await fetch(url);
|
|
if (!response.ok) {
|
|
const body = (await response.json().catch(() => null)) as null | { error?: string };
|
|
throw new Error(body?.error ?? `HTTP ${response.status}`);
|
|
}
|
|
return response.json() as Promise<ProviderListResponse>;
|
|
}
|
|
|
|
export async function fetchProviderOptions(): Promise<ProviderOptionsResponse> {
|
|
const response = await fetch("/api/providers/options");
|
|
if (!response.ok) {
|
|
const body = (await response.json().catch(() => null)) as null | { error?: string };
|
|
throw new Error(body?.error ?? `HTTP ${response.status}`);
|
|
}
|
|
return response.json() as Promise<ProviderOptionsResponse>;
|
|
}
|
|
|
|
export async function testProviderConfig(data: CreateProviderRequest): Promise<ProviderTestResponse> {
|
|
const response = await fetch("/api/providers/test", {
|
|
body: JSON.stringify(data),
|
|
headers: { "Content-Type": "application/json" },
|
|
method: "POST",
|
|
});
|
|
if (!response.ok) {
|
|
const body = (await response.json().catch(() => null)) as null | { error?: string };
|
|
throw new Error(body?.error ?? `HTTP ${response.status}`);
|
|
}
|
|
const result = (await response.json()) as ProviderTestResultResponse;
|
|
return result.providerTestResponse;
|
|
}
|
|
|
|
export async function updateProvider(id: string, data: UpdateProviderRequest): Promise<Provider> {
|
|
const response = await fetch(`/api/providers/${id}`, {
|
|
body: JSON.stringify(data),
|
|
headers: { "Content-Type": "application/json" },
|
|
method: "PATCH",
|
|
});
|
|
return handleResponse(response, (data) => (data as ProviderResponse).provider);
|
|
}
|
|
|
|
export function useCreateProvider() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: createProvider,
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: PROVIDERS_KEY });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDeleteProvider() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: deleteProvider,
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: PROVIDERS_KEY });
|
|
void queryClient.invalidateQueries({ queryKey: MODELS_KEY });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useProvider(id: string) {
|
|
return useQuery({
|
|
enabled: !!id,
|
|
queryFn: () => fetchProvider(id),
|
|
queryKey: [...PROVIDERS_KEY, "detail", id],
|
|
});
|
|
}
|
|
|
|
export function useProviderList(params: { keyword?: string; page?: number; pageSize?: number }) {
|
|
return useQuery({
|
|
queryFn: () => fetchProviderList(params),
|
|
queryKey: [...PROVIDERS_KEY, "list", params],
|
|
});
|
|
}
|
|
|
|
export function useProviderOptions() {
|
|
return useQuery({
|
|
queryFn: fetchProviderOptions,
|
|
queryKey: [...PROVIDERS_KEY, "options"],
|
|
});
|
|
}
|
|
|
|
export function useTestProviderConfig() {
|
|
return useMutation({
|
|
mutationFn: testProviderConfig,
|
|
});
|
|
}
|
|
|
|
export function useUpdateProvider() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (args: { data: UpdateProviderRequest; id: string }) => updateProvider(args.id, args.data),
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: PROVIDERS_KEY });
|
|
},
|
|
});
|
|
}
|