Hide scrape and library admin controls from non-admin users.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
truewhile
2026-09-15 15:19:54 +08:00
parent c911a57467
commit c74f6be250
6 changed files with 75 additions and 65 deletions
+20 -22
View File
@@ -5,17 +5,17 @@ import { useAuthStore } from '../stores/auth'
/**
* usePermission hook - 检查用户是否拥有特定权限
*
*
* @param key - 权限键名
* @param options - 配置选项
* @param options.autoFetch - 是否在权限未加载时自动获取(默认 true)
* @returns boolean - 用户是否拥有该权限
*
*
* @example
* ```tsx
* function MyComponent() {
* const canEdit = usePermission('can_edit_media')
*
*
* if (canEdit) {
* return <EditButton />
* }
@@ -30,26 +30,24 @@ export function usePermission(
const { autoFetch = true } = options
// selector 订阅:store 任何无关字段变化不会触发本组件重渲染
const hasPermission = usePermissionStore((s) => s.hasPermission)
const isSuper = usePermissionStore((s) => s.isSuper)
const fetchPermissions = usePermissionStore((s) => s.fetchPermissions)
const tier = useAuthStore((state) => state.tier)
const role = useAuthStore((state) => state.user?.role)
const isAuthenticated = useAuthStore((state) => state.token !== null)
const hasSuperAccess = isSuper || tier === 'plus' || role === 'admin'
const isAdmin = role === 'admin'
// 权限未加载时自动获取;用 getState() 读最新快照而不是渲染闭包,
// 同一次 commit 内挂载的多个消费方也只会有一个发出请求(store 内还有 inflight 去重兜底)。
useEffect(() => {
if (!hasSuperAccess && isAuthenticated && autoFetch) {
if (!isAdmin && isAuthenticated && autoFetch) {
const { permissions, isLoading } = usePermissionStore.getState()
if (Object.keys(permissions).length === 0 && !isLoading) {
fetchPermissions()
}
}
}, [autoFetch, fetchPermissions, hasSuperAccess, isAuthenticated])
}, [autoFetch, fetchPermissions, isAdmin, isAuthenticated])
// 超级用户有所有权限
if (hasSuperAccess) {
// 管理员拥有全部能力;plus / is_super 不能用来展示刮削、整理、删除等管理入口
if (isAdmin) {
return true
}
@@ -63,18 +61,18 @@ export function usePermission(
/**
* usePermissions hook - 获取所有权限
*
*
* @returns 权限状态和检查函数
*
*
* @example
* ```tsx
* function MyComponent() {
* const { permissions, isSuper, check } = usePermissions()
*
*
* if (isSuper) {
* return <AdminPanel />
* }
*
*
* return (
* <div>
* {check('can_view_dashboard') && <Dashboard />}
@@ -87,15 +85,14 @@ export function usePermission(
export function usePermissions() {
// selector 订阅:只关注 permissions/isSuper/isLoading 变化
const permissions = usePermissionStore((s) => s.permissions)
const isSuper = usePermissionStore((s) => s.isSuper)
const isLoading = usePermissionStore((s) => s.isLoading)
const fetchPermissions = usePermissionStore((s) => s.fetchPermissions)
const tier = useAuthStore((state) => state.tier)
const role = useAuthStore((state) => state.user?.role)
const isAuthenticated = useAuthStore((state) => state.token !== null)
const isAdmin = role === 'admin'
const check = (key: string): boolean => {
if (isSuper || tier === 'plus' || role === 'admin') {
if (isAdmin) {
return true
}
return permissions[key] === true
@@ -103,7 +100,8 @@ export function usePermissions() {
return {
permissions,
isSuper: isSuper || tier === 'plus' || role === 'admin',
// Keep the field name for callers, but only admins are treated as full-access.
isSuper: isAdmin,
isLoading,
check,
refetch: fetchPermissions,
@@ -113,10 +111,10 @@ export function usePermissions() {
/**
* usePermissionMany hook - 批量检查多个权限
*
*
* @param keys - 权限键数组
* @returns 每个权限的布尔值映射
*
*
* @example
* ```tsx
* function MyComponent() {
@@ -125,7 +123,7 @@ export function usePermissions() {
* 'can_manage_users',
* 'can_access_settings',
* ])
*
*
* return (
* <div>
* {perms['can_edit_media'] && <EditButton />}
@@ -137,7 +135,7 @@ export function usePermissions() {
*/
export function usePermissionMany(keys: string[]): Record<string, boolean> {
const { check } = usePermissions()
return keys.reduce((acc, key) => {
acc[key] = check(key)
return acc