107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
|
|
import {
|
|
Alert,
|
|
Box,
|
|
CircularProgress,
|
|
Stack,
|
|
Typography,
|
|
} from "@mui/material";
|
|
import { usePathname } from "next/navigation";
|
|
import type { ReactNode } from "react";
|
|
|
|
import { Header } from "@/components/header";
|
|
import { permissionForPath } from "@/lib/permissions";
|
|
import { useAccessStore } from "@/store/accessStore";
|
|
import { useAuthStore } from "@/store/authStore";
|
|
|
|
export const RoutePermissionGuard = ({
|
|
children,
|
|
authenticated,
|
|
accessDeniedReason = null,
|
|
}: {
|
|
children: ReactNode;
|
|
authenticated: boolean;
|
|
accessDeniedReason?: "account" | "project" | null;
|
|
}) => {
|
|
const pathname = usePathname();
|
|
const permissions = useAccessStore((state) => state.permissions);
|
|
const loading = useAccessStore((state) => state.loading);
|
|
const sessionExpired = useAuthStore((state) => state.sessionExpired);
|
|
const requiredPermission = permissionForPath(pathname);
|
|
|
|
if (!authenticated || sessionExpired) {
|
|
return (
|
|
<Box sx={{ p: 3 }}>
|
|
<Alert severity="warning">
|
|
<Stack spacing={0.5}>
|
|
<Typography variant="body1">登录状态已失效</Typography>
|
|
<Typography variant="body2">
|
|
正在跳转到登录页面…
|
|
</Typography>
|
|
</Stack>
|
|
</Alert>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (accessDeniedReason) {
|
|
const accountPending = accessDeniedReason === "account";
|
|
|
|
return (
|
|
<Box sx={{ minHeight: "100vh", bgcolor: "background.default" }}>
|
|
<Header accessMode={accessDeniedReason} />
|
|
<Box
|
|
component="main"
|
|
sx={{
|
|
width: "100%",
|
|
maxWidth: 840,
|
|
mx: "auto",
|
|
px: { xs: 2, sm: 3 },
|
|
py: { xs: 4, md: 8 },
|
|
}}
|
|
>
|
|
<Alert severity="warning" icon={<LockOutlinedIcon />}>
|
|
<Stack spacing={0.5}>
|
|
<Typography fontWeight={700}>
|
|
{accountPending ? "账号尚未获得访问权限" : "无权访问当前项目"}
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
{accountPending
|
|
? "身份认证已成功,但当前账号尚未由系统管理员启用。请联系系统管理员完成授权后重试。"
|
|
: "当前账号无法访问所选项目。请选择其他已授权项目,或联系系统管理员调整项目权限。"}
|
|
</Typography>
|
|
</Stack>
|
|
</Alert>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (requiredPermission && loading) {
|
|
return (
|
|
<Box sx={{ minHeight: 320, display: "grid", placeItems: "center" }}>
|
|
<CircularProgress size={28} />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (requiredPermission && !permissions.includes(requiredPermission)) {
|
|
return (
|
|
<Box sx={{ p: { xs: 2, md: 4 } }}>
|
|
<Alert severity="error" icon={<LockOutlinedIcon />}>
|
|
<Stack spacing={0.5}>
|
|
<Typography fontWeight={700}>无权访问此功能</Typography>
|
|
<Typography variant="body2">
|
|
当前项目角色缺少权限:{requiredPermission}
|
|
</Typography>
|
|
</Stack>
|
|
</Alert>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return children;
|
|
};
|