Files
beige/frontend/src/components/AuthGuard.tsx
yangqianqian 25f86b2f4a 存量前端:生图分组/灯箱/重生控件/导入文案/驳回横幅+SSE+总核销表
- ImageStrategyGroup/ImageLightbox/RegenControls/ImportTextModal/
  RejectReasonBanner 新建组件
- tasks images/text/new + history + 配置/布局/SSE hooks 存量改动
- 总核销表进度更新

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:17:53 +08:00

78 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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<string, UserRole[]> = {
'/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 (
<div className="flex items-center justify-center min-h-screen bg-surface-secondary">
<div className="text-text-secondary text-sm"></div>
</div>
);
}
return <>{children}</>;
}