33/**
44 * 前端管理员页的权限包装器。
55 *
6- * 做的事 :
6+ * 行为 :
77 * - 未登录:跳 /login
8- * - 登录但不是 admin :渲染 403 提示
9- * - 是 admin :渲染 children
8+ * - 登录但不满足 required 角色 :渲染 403 提示
9+ * - 通过 :渲染 children
1010 *
11- * 注意这个只是 UX 层的保护——真正的安全由后端 @SaCheckRole("admin") 兜底。
12- * 用户只要能绕过这里也拿不到数据,后端直接返回 401/403。
11+ * required 取值:
12+ * "admin" → roles 包含 admin(superadmin 也通过,因为他们 roles 里也会带 admin)
13+ * "superadmin" → roles 必须包含 superadmin
14+ *
15+ * 这个只是 UX 层保护——真正的安全由后端 @SaCheckRole(...) 兜底,绕过 UI
16+ * 也拿不到数据。
1317 */
1418
1519import { useEffect } from "react" ;
1620import { useRouter } from "next/navigation" ;
1721import { useAuth } from "@/lib/use-auth" ;
1822import type { ReactNode } from "react" ;
1923
20- export function AdminGuard ( { children } : { children : ReactNode } ) {
24+ interface Props {
25+ children : ReactNode ;
26+ /** 默认 "admin"(事件管理等通用后台页);用户管理页传 "superadmin" */
27+ required ?: "admin" | "superadmin" ;
28+ }
29+
30+ export function AdminGuard ( { children, required = "admin" } : Props ) {
2131 const { user, status } = useAuth ( ) ;
2232 const router = useRouter ( ) ;
2333
@@ -43,18 +53,24 @@ export function AdminGuard({ children }: { children: ReactNode }) {
4353
4454 if ( status === "unauthenticated" ) return null ;
4555
46- const isAdmin = user ?. roles ?. includes ( "admin" ) ?? false ;
47- if ( ! isAdmin ) {
56+ const roles = user ?. roles ?? [ ] ;
57+ const passes = required === "superadmin"
58+ ? roles . includes ( "superadmin" )
59+ : roles . includes ( "admin" ) ; // superadmin 在 seed 里也会带 admin,所以这里一起通过
60+ if ( ! passes ) {
4861 return (
4962 < main className = "pt-32 pb-16 min-h-screen flex items-center justify-center px-6" >
5063 < div className = "max-w-lg border border-[#CC0000] p-8 text-center" >
5164 < div className = "font-mono text-[10px] uppercase tracking-[0.3em] text-[#CC0000] mb-3" >
5265 403 · Forbidden
5366 </ div >
54- < h1 className = "font-serif text-2xl font-black mb-3" > 你不是管理员</ h1 >
67+ < h1 className = "font-serif text-2xl font-black mb-3" >
68+ { required === "superadmin" ? "需要超级管理员权限" : "你不是管理员" }
69+ </ h1 >
5570 < p className = "text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed" >
56- 管理员界面仅对 < code > roles</ code > 包含 < code > admin</ code > { " " }
57- 的账号开放。 如果你认为这是误报,联系站点维护者。
71+ { required === "superadmin"
72+ ? "此页面仅对 superadmin 开放。如果你认为这是误报,联系站点维护者。"
73+ : "管理员界面仅对 roles 包含 admin 的账号开放。如果你认为这是误报,联系站点维护者。" }
5874 </ p >
5975 </ div >
6076 </ main >
0 commit comments