fix(auth): keep logout and project recovery on access denial
This commit is contained in:
@@ -1,10 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
|
||||
import { Alert, Box, CircularProgress, Stack, Typography } from "@mui/material";
|
||||
import LogoutIcon from "@mui/icons-material/Logout";
|
||||
import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
|
||||
import {
|
||||
Alert,
|
||||
AppBar,
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Stack,
|
||||
Toolbar,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { usePathname } from "next/navigation";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { Title } from "@/components/title";
|
||||
import { permissionForPath } from "@/lib/permissions";
|
||||
import { useAccessStore } from "@/store/accessStore";
|
||||
import { useAuthStore } from "@/store/authStore";
|
||||
@@ -12,11 +25,17 @@ import { useAuthStore } from "@/store/authStore";
|
||||
export const RoutePermissionGuard = ({
|
||||
children,
|
||||
authenticated,
|
||||
accessDenied = false,
|
||||
accessDeniedReason = null,
|
||||
userName,
|
||||
onLogout,
|
||||
onChooseAnotherProject,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
authenticated: boolean;
|
||||
accessDenied?: boolean;
|
||||
accessDeniedReason?: "account" | "project" | null;
|
||||
userName?: string | null;
|
||||
onLogout?: () => void;
|
||||
onChooseAnotherProject?: () => void;
|
||||
}) => {
|
||||
const pathname = usePathname();
|
||||
const permissions = useAccessStore((state) => state.permissions);
|
||||
@@ -39,17 +58,94 @@ export const RoutePermissionGuard = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (accessDenied) {
|
||||
if (accessDeniedReason) {
|
||||
const displayName = userName?.trim() || "已登录账号";
|
||||
const accountPending = accessDeniedReason === "account";
|
||||
|
||||
return (
|
||||
<Box sx={{ p: { xs: 2, md: 4 } }}>
|
||||
<Alert severity="warning" icon={<LockOutlinedIcon />}>
|
||||
<Stack spacing={0.5}>
|
||||
<Typography fontWeight={700}>账号尚未获得访问权限</Typography>
|
||||
<Typography variant="body2">
|
||||
身份认证已成功,但当前账号尚未由系统管理员启用,或无权访问所选项目。请联系系统管理员完成授权后重试。
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Alert>
|
||||
<Box sx={{ minHeight: "100vh", bgcolor: "background.default" }}>
|
||||
<AppBar
|
||||
color="inherit"
|
||||
elevation={0}
|
||||
position="static"
|
||||
sx={{ borderBottom: 1, borderColor: "divider" }}
|
||||
>
|
||||
<Toolbar sx={{ gap: 2, justifyContent: "space-between" }}>
|
||||
<Title />
|
||||
<Stack direction="row" spacing={1.5} alignItems="center">
|
||||
<Avatar sx={{ width: 32, height: 32, bgcolor: "primary.main" }}>
|
||||
{displayName.slice(0, 1).toUpperCase()}
|
||||
</Avatar>
|
||||
<Box sx={{ display: { xs: "none", sm: "block" } }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
当前登录账号
|
||||
</Typography>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
{displayName}
|
||||
</Typography>
|
||||
</Box>
|
||||
{!accountPending && (
|
||||
<Button
|
||||
aria-label="切换项目"
|
||||
color="inherit"
|
||||
onClick={onChooseAnotherProject}
|
||||
sx={{
|
||||
gap: { xs: 0, sm: 1 },
|
||||
minWidth: { xs: 40, sm: "auto" },
|
||||
px: { xs: 1, sm: 2 },
|
||||
"&:active": { transform: "translateY(1px)" },
|
||||
}}
|
||||
variant="outlined"
|
||||
>
|
||||
<SwapHorizIcon fontSize="small" />
|
||||
<Box component="span" sx={{ display: { xs: "none", sm: "inline" } }}>
|
||||
切换项目
|
||||
</Box>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
aria-label="退出登录"
|
||||
color="inherit"
|
||||
onClick={onLogout}
|
||||
sx={{
|
||||
gap: { xs: 0, sm: 1 },
|
||||
minWidth: { xs: 40, sm: "auto" },
|
||||
px: { xs: 1, sm: 2 },
|
||||
"&:active": { transform: "translateY(1px)" },
|
||||
}}
|
||||
variant="outlined"
|
||||
>
|
||||
<LogoutIcon fontSize="small" />
|
||||
<Box component="span" sx={{ display: { xs: "none", sm: "inline" } }}>
|
||||
退出登录
|
||||
</Box>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user