100 lines
3.1 KiB
TypeScript
100 lines
3.1 KiB
TypeScript
import { trpc } from "@/lib/trpc";
|
|
import { UNAUTHED_ERR_MSG } from '@shared/const';
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { httpBatchLink, httpLink, splitLink, TRPCClientError } from "@trpc/client";
|
|
import { createRoot } from "react-dom/client";
|
|
import superjson from "superjson";
|
|
import App from "./App";
|
|
import { getLoginUrl } from "./const";
|
|
import "./index.css";
|
|
|
|
// 禁用 HMR 失败时的自动页面刷新,保留热更新功能
|
|
if (import.meta.hot) {
|
|
// 阻止 HMR 失败时的完整页面刷新
|
|
import.meta.hot.on('vite:beforeFullReload', (payload) => {
|
|
console.log('[HMR] Preventing full reload, change detected:', payload);
|
|
// 返回 false 阻止刷新(但这个钩子实际上不能阻止,只是通知)
|
|
});
|
|
|
|
// 当 HMR 更新失败时,不刷新页面,只在控制台显示警告
|
|
import.meta.hot.on('vite:error', (err) => {
|
|
console.warn('[HMR] Update failed, manual refresh may be needed:', err);
|
|
});
|
|
|
|
// 接受所有模块更新,防止因某些模块不支持 HMR 而触发刷新
|
|
import.meta.hot.accept(() => {
|
|
console.log('[HMR] Module updated');
|
|
});
|
|
}
|
|
|
|
const queryClient = new QueryClient();
|
|
|
|
const redirectToLoginIfUnauthorized = (error: unknown) => {
|
|
if (!(error instanceof TRPCClientError)) return;
|
|
if (typeof window === "undefined") return;
|
|
|
|
const isUnauthorized = error.message === UNAUTHED_ERR_MSG;
|
|
|
|
if (!isUnauthorized) return;
|
|
|
|
window.location.href = getLoginUrl();
|
|
};
|
|
|
|
queryClient.getQueryCache().subscribe(event => {
|
|
if (event.type === "updated" && event.action.type === "error") {
|
|
const error = event.query.state.error;
|
|
redirectToLoginIfUnauthorized(error);
|
|
console.error("[API Query Error]", error);
|
|
}
|
|
});
|
|
|
|
queryClient.getMutationCache().subscribe(event => {
|
|
if (event.type === "updated" && event.action.type === "error") {
|
|
const error = event.mutation.state.error;
|
|
redirectToLoginIfUnauthorized(error);
|
|
console.error("[API Mutation Error]", error);
|
|
}
|
|
});
|
|
|
|
const trpcLinkOptions = {
|
|
url: "/api/trpc",
|
|
transformer: superjson,
|
|
fetch(input: RequestInfo | URL, init?: RequestInit) {
|
|
// 给每个请求加时间戳,防止 NPS / 反向代理缓存 GET 响应
|
|
let url: string;
|
|
if (input instanceof URL) {
|
|
input.searchParams.set('_t', Date.now().toString());
|
|
url = input.toString();
|
|
} else if (typeof input === 'string') {
|
|
const sep = input.includes('?') ? '&' : '?';
|
|
url = `${input}${sep}_t=${Date.now()}`;
|
|
} else {
|
|
url = input as string;
|
|
}
|
|
return globalThis.fetch(url, {
|
|
...(init ?? {}),
|
|
credentials: "include",
|
|
});
|
|
},
|
|
} as const;
|
|
|
|
const trpcClient = trpc.createClient({
|
|
links: [
|
|
splitLink({
|
|
condition(op) {
|
|
return op.type === "mutation";
|
|
},
|
|
true: httpLink(trpcLinkOptions),
|
|
false: httpBatchLink(trpcLinkOptions),
|
|
}),
|
|
],
|
|
});
|
|
|
|
createRoot(document.getElementById("root")!).render(
|
|
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
|
<QueryClientProvider client={queryClient}>
|
|
<App />
|
|
</QueryClientProvider>
|
|
</trpc.Provider>
|
|
);
|