Some checks failed
Admin CI / build-and-push-web (push) Failing after 6m11s
Add new verifications module including API endpoints for retrieving and reviewing user identity verifications, along with corresponding admin page, routing, and sidebar navigation entry.
270 lines
6.4 KiB
TypeScript
270 lines
6.4 KiB
TypeScript
import { createBrowserRouter, Navigate, useLocation } from 'react-router-dom'
|
||
import { Suspense, lazy } from 'react'
|
||
import { useAuthStore, ROLES } from '@/stores/authStore'
|
||
|
||
// 路由守卫组件
|
||
export function AuthGuard({ children }: { children: React.ReactNode }) {
|
||
const { isAuthenticated, isLoading } = useAuthStore()
|
||
const location = useLocation()
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex h-screen items-center justify-center">
|
||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
return <Navigate to="/login" state={{ from: location }} replace />
|
||
}
|
||
|
||
return <>{children}</>
|
||
}
|
||
|
||
export function RoleGuard({
|
||
children,
|
||
roles
|
||
}: {
|
||
children: React.ReactNode
|
||
roles: string[]
|
||
}) {
|
||
const { hasAnyRole, isAuthenticated, isLoading } = useAuthStore()
|
||
const location = useLocation()
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex h-screen items-center justify-center">
|
||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
return <Navigate to="/login" state={{ from: location }} replace />
|
||
}
|
||
|
||
if (!hasAnyRole(roles)) {
|
||
return <Navigate to="/403" replace />
|
||
}
|
||
|
||
return <>{children}</>
|
||
}
|
||
|
||
export function AdminGuard({ children }: { children: React.ReactNode }) {
|
||
return (
|
||
<RoleGuard roles={[ROLES.SUPER_ADMIN, ROLES.ADMIN]}>
|
||
{children}
|
||
</RoleGuard>
|
||
)
|
||
}
|
||
|
||
export function ModeratorGuard({ children }: { children: React.ReactNode }) {
|
||
return (
|
||
<RoleGuard roles={[ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.MODERATOR]}>
|
||
{children}
|
||
</RoleGuard>
|
||
)
|
||
}
|
||
|
||
// 公开路由守卫(已登录用户重定向到dashboard)
|
||
export function PublicGuard({ children }: { children: React.ReactNode }) {
|
||
const { isAuthenticated, isLoading } = useAuthStore()
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex h-screen items-center justify-center">
|
||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (isAuthenticated) {
|
||
return <Navigate to="/dashboard" replace />
|
||
}
|
||
|
||
return <>{children}</>
|
||
}
|
||
|
||
// 加载指示器组件
|
||
function PageLoader() {
|
||
return (
|
||
<div className="flex h-full items-center justify-center p-8">
|
||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
// 懒加载页面组件
|
||
const Login = lazy(() => import('@/pages/Login'))
|
||
const Dashboard = lazy(() => import('@/pages/Dashboard'))
|
||
const Users = lazy(() => import('@/pages/Users'))
|
||
const Roles = lazy(() => import('@/pages/Roles'))
|
||
const Posts = lazy(() => import('@/pages/Posts'))
|
||
const Comments = lazy(() => import('@/pages/Comments'))
|
||
const Groups = lazy(() => import('@/pages/Groups'))
|
||
const Channels = lazy(() => import('@/pages/Channels'))
|
||
const Reports = lazy(() => import('@/pages/Reports'))
|
||
const MaterialSubjects = lazy(() => import('@/pages/MaterialSubjects'))
|
||
const Materials = lazy(() => import('@/pages/Materials'))
|
||
const Verifications = lazy(() => import('@/pages/Verifications'))
|
||
const Forbidden = lazy(() => import('@/pages/Forbidden'))
|
||
const NotFound = lazy(() => import('@/pages/NotFound'))
|
||
|
||
// 布局组件
|
||
import MainLayout from '@/components/Layout/MainLayout'
|
||
|
||
// 创建路由
|
||
export const router = createBrowserRouter([
|
||
{
|
||
path: '/login',
|
||
element: (
|
||
<PublicGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Login />
|
||
</Suspense>
|
||
</PublicGuard>
|
||
),
|
||
},
|
||
{
|
||
path: '/',
|
||
element: (
|
||
<AuthGuard>
|
||
<MainLayout />
|
||
</AuthGuard>
|
||
),
|
||
children: [
|
||
{
|
||
index: true,
|
||
element: <Navigate to="/dashboard" replace />,
|
||
},
|
||
{
|
||
path: 'dashboard',
|
||
element: (
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Dashboard />
|
||
</Suspense>
|
||
),
|
||
},
|
||
{
|
||
path: 'users',
|
||
element: (
|
||
<AdminGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Users />
|
||
</Suspense>
|
||
</AdminGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'roles',
|
||
element: (
|
||
<AdminGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Roles />
|
||
</Suspense>
|
||
</AdminGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'posts',
|
||
element: (
|
||
<ModeratorGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Posts />
|
||
</Suspense>
|
||
</ModeratorGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'comments',
|
||
element: (
|
||
<ModeratorGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Comments />
|
||
</Suspense>
|
||
</ModeratorGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'groups',
|
||
element: (
|
||
<ModeratorGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Groups />
|
||
</Suspense>
|
||
</ModeratorGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'reports',
|
||
element: (
|
||
<ModeratorGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Reports />
|
||
</Suspense>
|
||
</ModeratorGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'channels',
|
||
element: (
|
||
<AdminGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Channels />
|
||
</Suspense>
|
||
</AdminGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'materials/subjects',
|
||
element: (
|
||
<AdminGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<MaterialSubjects />
|
||
</Suspense>
|
||
</AdminGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'materials',
|
||
element: (
|
||
<AdminGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Materials />
|
||
</Suspense>
|
||
</AdminGuard>
|
||
),
|
||
},
|
||
{
|
||
path: 'verifications',
|
||
element: (
|
||
<AdminGuard>
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Verifications />
|
||
</Suspense>
|
||
</AdminGuard>
|
||
),
|
||
},
|
||
],
|
||
},
|
||
{
|
||
path: '/403',
|
||
element: (
|
||
<Suspense fallback={<PageLoader />}>
|
||
<Forbidden />
|
||
</Suspense>
|
||
),
|
||
},
|
||
{
|
||
path: '*',
|
||
element: (
|
||
<Suspense fallback={<PageLoader />}>
|
||
<NotFound />
|
||
</Suspense>
|
||
),
|
||
},
|
||
])
|
||
|
||
export default router
|