mirror of
https://github.com/massbug/judge4c.git
synced 2025-07-03 23:30:50 +00:00
feat(auth): add protected layout with role-based access control
This commit is contained in:
parent
0695dd2f61
commit
19fac9b3d6
@ -1,5 +1,4 @@
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { ProblemEditLayout } from "@/features/admin/ui/layouts/problem-edit-layout";
|
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -13,7 +12,7 @@ const Layout = async ({ children, params }: LayoutProps) => {
|
|||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ProblemEditLayout>{children}</ProblemEditLayout>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
BarChart3,
|
BarChart3,
|
||||||
Target,
|
Target,
|
||||||
Activity,
|
Activity,
|
||||||
|
GraduationCapIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
@ -197,21 +198,26 @@ export default async function DashboardPage() {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
|
{
|
||||||
|
label: "管理员管理",
|
||||||
|
href: "/dashboard/management",
|
||||||
|
icon: Target,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "用户管理",
|
label: "用户管理",
|
||||||
href: "/dashboard/usermanagement/guest",
|
href: "/dashboard/usermanagement/guest",
|
||||||
icon: Users,
|
icon: Users,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "教师管理",
|
||||||
|
href: "/dashboard/usermanagement/teacher",
|
||||||
|
icon: GraduationCapIcon,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "题目管理",
|
label: "题目管理",
|
||||||
href: "/dashboard/usermanagement/problem",
|
href: "/dashboard/usermanagement/problem",
|
||||||
icon: BookOpen,
|
icon: BookOpen,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "管理员设置",
|
|
||||||
href: "/dashboard/management",
|
|
||||||
icon: Target,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
case "TEACHER":
|
case "TEACHER":
|
||||||
@ -240,7 +246,7 @@ export default async function DashboardPage() {
|
|||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
label: "学生管理",
|
label: "用户管理",
|
||||||
href: "/dashboard/usermanagement/guest",
|
href: "/dashboard/usermanagement/guest",
|
||||||
icon: Users,
|
icon: Users,
|
||||||
},
|
},
|
||||||
@ -250,7 +256,7 @@ export default async function DashboardPage() {
|
|||||||
icon: BookOpen,
|
icon: BookOpen,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "统计分析",
|
label: "完成情况",
|
||||||
href: "/dashboard/teacher/dashboard",
|
href: "/dashboard/teacher/dashboard",
|
||||||
icon: BarChart3,
|
icon: BarChart3,
|
||||||
},
|
},
|
||||||
@ -281,12 +287,12 @@ export default async function DashboardPage() {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
{ label: "开始做题", href: "/problemset", icon: BookOpen },
|
|
||||||
{
|
{
|
||||||
label: "我的进度",
|
label: "我的进度",
|
||||||
href: "/dashboard/student/dashboard",
|
href: "/dashboard/student/dashboard",
|
||||||
icon: TrendingUp,
|
icon: TrendingUp,
|
||||||
},
|
},
|
||||||
|
{ label: "开始做题", href: "/problemset", icon: BookOpen },
|
||||||
{ label: "个人设置", href: "/dashboard/management", icon: Target },
|
{ label: "个人设置", href: "/dashboard/management", icon: Target },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -361,7 +367,7 @@ export default async function DashboardPage() {
|
|||||||
<CardDescription>常用功能快速访问</CardDescription>
|
<CardDescription>常用功能快速访问</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
<div className="grid gap-4 md:grid-cols-4">
|
||||||
{config.actions.map((action, index) => (
|
{config.actions.map((action, index) => (
|
||||||
<Link key={index} href={action.href}>
|
<Link key={index} href={action.href}>
|
||||||
<Button variant="outline" className="w-full justify-start">
|
<Button variant="outline" className="w-full justify-start">
|
||||||
|
15
src/app/(protected)/layout.tsx
Normal file
15
src/app/(protected)/layout.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { ProtectedLayout } from "@/features/dashboard/layouts/protected-layout";
|
||||||
|
|
||||||
|
interface LayoutProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout = ({ children }: LayoutProps) => {
|
||||||
|
return (
|
||||||
|
<ProtectedLayout roles={["ADMIN", "TEACHER", "GUEST"]}>
|
||||||
|
{children}
|
||||||
|
</ProtectedLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
@ -33,8 +33,8 @@ const adminData = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
navSecondary: [
|
navSecondary: [
|
||||||
{ title: "帮助", url: "/", icon: LifeBuoy },
|
{ title: "帮助", url: `${siteConfig.url.repo.github}/issues`, icon: LifeBuoy },
|
||||||
{ title: "反馈", url: siteConfig.url.repo.github, icon: Send },
|
{ title: "反馈", url: `${siteConfig.url.repo.github}/pulls`, icon: Send },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,24 +16,28 @@ import { NavMain } from "@/components/nav-main";
|
|||||||
import { NavUser } from "@/components/nav-user";
|
import { NavUser } from "@/components/nav-user";
|
||||||
import { NavProjects } from "@/components/nav-projects";
|
import { NavProjects } from "@/components/nav-projects";
|
||||||
import { NavSecondary } from "@/components/nav-secondary";
|
import { NavSecondary } from "@/components/nav-secondary";
|
||||||
import { Command, LifeBuoy, Send, SquareTerminal } from "lucide-react";
|
import { Command, LifeBuoy, Send, Shield } from "lucide-react";
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
{
|
{
|
||||||
title: "页面",
|
title: "管理面板",
|
||||||
url: "#",
|
url: "/dashboard",
|
||||||
icon: SquareTerminal,
|
icon: Shield,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "主页",
|
title: "我的进度",
|
||||||
url: "/dashboard/student/dashboard",
|
url: "/dashboard/student/dashboard",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "题目集",
|
title: "开始做题",
|
||||||
url: "/problemset",
|
url: "/problemset",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "个人设置",
|
||||||
|
url: "/dashboard/management",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -71,12 +75,12 @@ const data = {
|
|||||||
navSecondary: [
|
navSecondary: [
|
||||||
{
|
{
|
||||||
title: "帮助",
|
title: "帮助",
|
||||||
url: "/",
|
url: `${siteConfig.url.repo.github}/issues`,
|
||||||
icon: LifeBuoy,
|
icon: LifeBuoy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "反馈",
|
title: "反馈",
|
||||||
url: siteConfig.url.repo.github,
|
url: `${siteConfig.url.repo.github}/pulls`,
|
||||||
icon: Send,
|
icon: Send,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import { Command, LifeBuoy, Send, Shield } from "lucide-react";
|
||||||
Command,
|
|
||||||
LifeBuoy,
|
|
||||||
PieChart,
|
|
||||||
Send,
|
|
||||||
SquareTerminal,
|
|
||||||
} from "lucide-react";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
@ -26,9 +20,9 @@ import { NavSecondary } from "@/components/nav-secondary";
|
|||||||
const data = {
|
const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
{
|
{
|
||||||
title: "教师管理",
|
title: "管理面板",
|
||||||
url: "#",
|
url: "/dashboard",
|
||||||
icon: SquareTerminal,
|
icon: Shield,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -36,47 +30,25 @@ const data = {
|
|||||||
url: "/dashboard/usermanagement/guest",
|
url: "/dashboard/usermanagement/guest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "题库管理",
|
title: "题目管理",
|
||||||
url: "/dashboard/usermanagement/problem",
|
url: "/dashboard/usermanagement/problem",
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "统计分析",
|
|
||||||
url: "#",
|
|
||||||
icon: PieChart,
|
|
||||||
items: [
|
|
||||||
{
|
{
|
||||||
title: "完成情况",
|
title: "完成情况",
|
||||||
url: "/dashboard/teacher/dashboard",
|
url: "/dashboard/teacher/dashboard",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: "错题统计",
|
|
||||||
// url: "/dashboard/teacher/dashboard",
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: "设置",
|
|
||||||
// url: "#",
|
|
||||||
// icon: Settings2,
|
|
||||||
// items: [
|
|
||||||
// {
|
|
||||||
// title: "语言",
|
|
||||||
// url: "#",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
navSecondary: [
|
navSecondary: [
|
||||||
{
|
{
|
||||||
title: "帮助",
|
title: "帮助",
|
||||||
url: "/",
|
url: `${siteConfig.url.repo.github}/issues`,
|
||||||
icon: LifeBuoy,
|
icon: LifeBuoy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "反馈",
|
title: "反馈",
|
||||||
url: siteConfig.url.repo.github,
|
url: `${siteConfig.url.repo.github}/pulls`,
|
||||||
icon: Send,
|
icon: Send,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -25,7 +25,7 @@ export const ProtectedLayout = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user || !roles.includes(user.role)) {
|
if (!user || !roles.includes(user.role)) {
|
||||||
redirect("unauthorized");
|
redirect("/unauthorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
@ -251,7 +251,7 @@ export function UserTable(props: UserTableProps) {
|
|||||||
if (isProblem) {
|
if (isProblem) {
|
||||||
// 如果是problem类型,跳转到编辑路由,使用displayId
|
// 如果是problem类型,跳转到编辑路由,使用displayId
|
||||||
const problem = item as Problem;
|
const problem = item as Problem;
|
||||||
router.push(`/admin/problems/${problem.displayId}/edit`);
|
router.push(`/dashboard/admin/problems/${problem.id}/edit`);
|
||||||
} else {
|
} else {
|
||||||
// 如果是用户类型,打开编辑弹窗
|
// 如果是用户类型,打开编辑弹窗
|
||||||
setEditingUser(item);
|
setEditingUser(item);
|
||||||
|
Loading…
Reference in New Issue
Block a user