'use client'; import { createContext, useContext, useState, useEffect, useCallback } from 'react'; import { oidcClient } from '@/lib/auth/oidc-client'; import { TokenManager } from '@/lib/auth/token-manager'; import { useTRPC } from '@fenghuo/client'; import { useQuery } from '@tanstack/react-query'; import { SystemPermission, PermissionUtils, SystemRole, SystemRoleConfig, type PermissionMeta, UserWithRelations, userWithRelationsSelect } from '@fenghuo/common'; import { useToken } from '@/components/providers/token-provider'; import { Prisma, Role } from '@fenghuo/db/index'; // 导出权限相关类型和工具 export { SystemPermission, PermissionUtils, SystemRole, SystemRoleConfig, type PermissionMeta } from '@fenghuo/common'; // 扩展用户接口 export interface User extends Omit { roles: string[]; // 角色名称数组 permissions?: SystemPermission[]; // 使用新的权限系统 } interface AuthProviderProps { children: React.ReactNode; } interface AuthContextType { // 用户状态 user: User | null; isLoading: boolean; isAuthenticated: boolean; // 权限检查方法 hasRole: (role: string) => boolean; hasPermission: (permission: SystemPermission) => boolean; hasAnyRole: (roles: string[]) => boolean; hasAnyPermission: (permissions: SystemPermission[]) => boolean; hasAllPermissions: (permissions: SystemPermission[]) => boolean; isSuperAdmin: () => boolean; isAdmin: () => boolean; // 认证操作 login: (username: string, password: string) => Promise; register: (userData: Prisma.UserCreateArgs) => Promise; logout: () => Promise; getAccessToken: () => Promise; loadUser: () => Promise; } const AuthContext = createContext(null); export default function AuthProvider({ children }: AuthProviderProps) { const { refreshToken, clearTokens } = useToken(); const [userId, setUserId] = useState(null); const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isAuthenticated, setIsAuthenticated] = useState(false); const trpc = useTRPC(); // 修改 tRPC 查询,包含角色权限 const { data: backendUser, error: userError, isLoading: isUserLoading } = useQuery({ ...trpc.user.findFirst.queryOptions({ where: { id: userId! }, select: userWithRelationsSelect }), enabled: !!userId, retry: false, }) as { data: UserWithRelations | null, error: Error | null, isLoading: boolean }; // 更新用户状态当后端用户数据加载完成时 useEffect(() => { if (backendUser) { const userRoles = backendUser.roles?.map(r => r.slug) || []; // 传递完整的角色数据给权限计算函数 const userPermissions = getUserPermissions(backendUser.roles || []); const appUser: User = { ...backendUser, roles: userRoles, permissions: userPermissions, }; setUser(appUser); } else if (userError) { console.error('获取用户数据失败:', userError); setUser(null); setIsAuthenticated(false); TokenManager.clearTokens(); } }, [backendUser, userError]); // 加载用户信息 const loadUser = useCallback(async () => { setIsLoading(true); try { const accessToken = await TokenManager.getValidAccessToken(); if (accessToken) { // 只获取 OIDC 用户信息来获得用户 ID const oidcUser = await oidcClient.getUserInfo(accessToken); setUserId(oidcUser.sub); setIsAuthenticated(true); // 触发 QueryProvider 刷新 token refreshToken(); } else { setUserId(null); setUser(null); setIsAuthenticated(false); setIsLoading(false); } } catch (error) { console.error('加载用户信息失败:', error); setUserId(null); setUser(null); setIsAuthenticated(false); TokenManager.clearTokens(); setIsLoading(false); } }, [refreshToken]); // 当 tRPC 查询完成时更新 loading 状态 useEffect(() => { if (userId && !isUserLoading) { setIsLoading(false); } }, [userId, isUserLoading]); // 初始化时加载用户信息 useEffect(() => { loadUser(); }, [loadUser]); // 登录功能 const login = useCallback(async (username: string, password: string): Promise => { setIsLoading(true); try { // 调用 OIDC 服务登录 const tokenResponse = await oidcClient.loginWithPassword(username, password); // 保存令牌 TokenManager.saveTokens(tokenResponse); // 获取用户信息并设置 userId,让 tRPC 查询处理剩余逻辑 const oidcUser = await oidcClient.getUserInfo(tokenResponse.access_token); setUserId(oidcUser.sub); setIsAuthenticated(true); // 登录成功后直接刷新token context await refreshToken(); } catch (error) { console.error('登录失败:', error); throw error; } finally { setIsLoading(false); } }, [refreshToken]); // 注册功能 const register = useCallback(async (userData: Prisma.UserCreateArgs): Promise => { setIsLoading(true); try { await oidcClient.register(userData); } catch (error) { console.error('注册失败:', error); throw error; } finally { setIsLoading(false); } }, []); // 退出登录功能 const logout = useCallback(async () => { setIsLoading(true); try { const accessToken = TokenManager.getAccessToken(); if (accessToken) { await oidcClient.logout(accessToken); } } catch (error) { console.error('退出登录失败:', error); } finally { TokenManager.clearTokens(); setUserId(null); setUser(null); setIsAuthenticated(false); setIsLoading(false); // 退出时清除token clearTokens(); } }, [clearTokens]); // 获取访问令牌 const getAccessToken = useCallback(async () => { return await TokenManager.getValidAccessToken(); }, []); // 权限检查方法 - 使用新的权限系统 const hasRole = useCallback((role: string): boolean => { return user?.roles.includes(role) || false; }, [user]); const hasPermission = useCallback((permission: SystemPermission): boolean => { if (!user?.permissions) return false; return PermissionUtils.hasPermission(user.permissions, permission); }, [user]); const hasAnyRole = useCallback((roles: string[]): boolean => { return roles.some(role => hasRole(role)); }, [hasRole]); const hasAnyPermission = useCallback((permissions: SystemPermission[]): boolean => { if (!user?.permissions) return false; return PermissionUtils.hasAnyPermission(user.permissions, permissions); }, [user]); const hasAllPermissions = useCallback((permissions: SystemPermission[]): boolean => { if (!user?.permissions) return false; return PermissionUtils.hasAllPermissions(user.permissions, permissions); }, [user]); // 检查是否为超级管理员 const isSuperAdmin = useCallback((): boolean => { return hasPermission(SystemPermission.SUPER_ADMIN); }, [hasPermission]); // 检查是否为管理员级别 const isAdmin = useCallback((): boolean => { return hasRole(SystemRole.SUPER_ADMIN) || hasRole(SystemRole.ADMIN); }, [hasRole]); const contextValue: AuthContextType = { user, isLoading, isAuthenticated, hasRole, hasPermission, hasAnyRole, hasAnyPermission, hasAllPermissions, isSuperAdmin, isAdmin, login, register, logout, getAccessToken, loadUser, }; return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; } // 计算用户权限 - 基于数据库角色权限 const getUserPermissions = (roles: Role[]): SystemPermission[] => { const permissions = new Set(); roles.forEach((role) => { if (!role.permissions || !Array.isArray(role.permissions)) { console.warn('角色权限数据格式错误:', role); return; } role.permissions.forEach(permission => { if (PermissionUtils.isValidPermission(permission)) { permissions.add(permission as SystemPermission); } else { console.warn('无效的权限代码:', permission); } }); }); return Array.from(permissions); }; // 权限检查工具函数 - 更新为使用新的权限系统 export const checkPermission = (userPermissions: SystemPermission[], requiredPermissions?: SystemPermission[]): boolean => { if (!requiredPermissions || requiredPermissions.length === 0) return true; return PermissionUtils.hasAnyPermission(userPermissions, requiredPermissions); }; // 检查角色权限的工具函数 export const checkRole = (userRoles: string[], requiredRoles?: string[]): boolean => { if (!requiredRoles || requiredRoles.length === 0) return true; return requiredRoles.some(role => userRoles.includes(role)); }; // 过滤菜单项的工具函数 - 支持角色和权限检查 export const filterMenuItems = ( items: T[], user: User | null ): T[] => { if (!user) return []; return items.filter(item => { // 检查角色要求 if (item.roles && !checkRole(user.roles, item.roles)) { return false; } // 检查权限要求 if (item.permissions && user.permissions && !checkPermission(user.permissions, item.permissions)) { return false; } return true; }); };