'use client'; /** * AuthGuard — 路由守卫 * - 未登录 → /login * - 越权(组长访问 /config,非管理员等)→ /dashboard * 扒 banana AuthGuard 范式,全新重建 */ import { useEffect } from 'react'; import { useRouter, usePathname } from 'next/navigation'; import { useAuthStore } from '@/stores/authStore'; import { UserRole } from '@/types/index'; // 路由 → 最低所需角色 const ROUTE_ROLES: Record = { '/config': ['admin'], '/review': ['admin', 'supervisor'], '/history': ['admin', 'supervisor', 'operator'], '/tasks/new': ['admin', 'supervisor', 'operator'], '/fission': ['admin', 'supervisor', 'operator'], '/dashboard': ['admin', 'supervisor', 'operator'], }; function getRequiredRoles(pathname: string): UserRole[] | null { // 精确匹配或前缀匹配(如 /tasks/[id]/text) if (ROUTE_ROLES[pathname]) return ROUTE_ROLES[pathname]; for (const [prefix, roles] of Object.entries(ROUTE_ROLES)) { if (pathname.startsWith(prefix + '/')) return roles; } return null; // 公开路由(/login 等) } interface AuthGuardProps { children: React.ReactNode; } export function AuthGuard({ children }: AuthGuardProps) { const router = useRouter(); const pathname = usePathname(); const { isAuthenticated, role, isLoading, fetchMe, endLoading } = useAuthStore(); useEffect(() => { // 首屏校验:有 token 且 store 未初始化 → 拉 me;无 token → 结束 loading 让守卫放行判断 const token = typeof window !== 'undefined' ? localStorage.getItem('clover_token') : null; if (token && !isAuthenticated && isLoading) { fetchMe(); } else if (!token && isLoading) { endLoading(); } }, [isAuthenticated, isLoading, fetchMe, endLoading]); useEffect(() => { if (isLoading) return; // 公开路由直接放行 if (pathname === '/login') return; if (!isAuthenticated) { router.replace('/login'); return; } const required = getRequiredRoles(pathname); if (required && role && !required.includes(role)) { router.replace('/dashboard'); } }, [isAuthenticated, role, isLoading, pathname, router]); if (isLoading) { return (
加载中…
); } return <>{children}; }