feat: 系统优化 - ESLint、Tailwind、前端健壮性、后端工程化、运维可观测性

- 前端: ESLint+Prettier配置、Tailwind v4配置、ErrorBoundary、全局AuthLoader优化、ReactQuery分层
- 后端: MinIO凭证移除、Docker统一为govai品牌、zerolog日志封装、错误码枚举、文件上传校验、单元测试(13项全通过)
- 运维: 健康检查增强(PG/Redis ping)、Prometheus指标(/metrics端点)、多租户tenant包、RateLimit nil防御
- 移动: citation_prompt.txt → internal/assets/
This commit is contained in:
selfrelease
2026-06-23 10:48:22 +08:00
parent 91f4fac23c
commit 65dc805eb5
28 changed files with 1414 additions and 174 deletions
+40 -16
View File
@@ -2,16 +2,42 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useRef, useEffect } from "react";
import { useRouter, usePathname } from "next/navigation";
import { useAuthStore } from "@/stores/auth";
import { TooltipProvider } from "@/components/ui/tooltip";
// 公开路由(无需鉴权)
const PUBLIC_PATHS = ["/login", "/register"];
function AuthLoader({ children }: { children: React.ReactNode }) {
const fetchUser = useAuthStore((s) => s.fetchUser);
const isLoading = useAuthStore((s) => s.isLoading);
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
const router = useRouter();
const pathname = usePathname();
const isPublic = PUBLIC_PATHS.some((p) => pathname === p || pathname.startsWith(p + "/"));
useEffect(() => {
fetchUser();
}, [fetchUser]);
if (!isPublic) {
fetchUser();
}
}, [fetchUser, isPublic]);
// 公开路由:直接渲染,不阻塞
if (isPublic) {
return <>{children}</>;
}
// 鉴权路由:未登录则跳转
if (!isLoading && !isAuthenticated) {
router.push("/login");
return (
<div className="flex h-screen items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary" />
</div>
);
}
if (isLoading) {
return (
@@ -24,23 +50,21 @@ function AuthLoader({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
const createQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
refetchOnWindowFocus: false,
retry: 1,
refetchOnMount: false,
},
},
});
export function Providers({ children }: { children: React.ReactNode }) {
const queryClientRef = useRef<QueryClient>(null);
if (!queryClientRef.current) {
queryClientRef.current = createQueryClient();
queryClientRef.current = new QueryClient({
defaultOptions: {
queries: {
// 高频变更数据(审计、用户列表)
staleTime: 30 * 1000,
gcTime: 5 * 60 * 1000,
refetchOnWindowFocus: false,
retry: 1,
refetchOnMount: "always",
},
},
});
}
return (