Merge remote-tracking branch 'origin/Wren' into PingChuan
This commit is contained in:
@@ -222,7 +222,12 @@ export function useAreaDatasetConfig(): UseAreaDatasetConfigReturn {
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('创建知识库绑定失败:', error);
|
||||
message.error('创建失败,请稍后重试');
|
||||
// 检查是否为403权限不足错误
|
||||
if (error?.response?.status === 403 || error?.status === 403 || error?.code === 403) {
|
||||
message.error('无权限操作:您没有创建知识库绑定的权限');
|
||||
} else {
|
||||
message.error('创建失败,请稍后重试');
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
setSubmitLoading(false);
|
||||
@@ -255,7 +260,12 @@ export function useAreaDatasetConfig(): UseAreaDatasetConfigReturn {
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('更新知识库绑定失败:', error);
|
||||
message.error('更新失败,请稍后重试');
|
||||
// 检查是否为403权限不足错误
|
||||
if (error?.response?.status === 403 || error?.status === 403 || error?.code === 403) {
|
||||
message.error('无权限操作:您没有编辑知识库绑定的权限');
|
||||
} else {
|
||||
message.error('更新失败,请稍后重试');
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
setSubmitLoading(false);
|
||||
@@ -287,7 +297,12 @@ export function useAreaDatasetConfig(): UseAreaDatasetConfigReturn {
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('删除知识库绑定失败:', error);
|
||||
message.error('删除失败,请稍后重试');
|
||||
// 检查是否为403权限不足错误
|
||||
if (error?.response?.status === 403 || error?.status === 403 || error?.code === 403) {
|
||||
message.error('无权限操作:您没有删除知识库绑定的权限');
|
||||
} else {
|
||||
message.error('删除失败,请稍后重试');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
/**
|
||||
* GET /api/v3/dify/area-datasets - 获取所有知识库绑定列表(管理员)
|
||||
* POST /api/v3/dify/area-datasets - 创建知识库绑定
|
||||
*
|
||||
* 权限说明:
|
||||
* GET: @require_permission_v2("dify:dataset:manage")
|
||||
* POST: @require_permission_v2("dify:dataset:manage")
|
||||
*/
|
||||
|
||||
import { type LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
|
||||
@@ -945,10 +945,18 @@ export default function RolePermissions() {
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 权限状态管理 ====================
|
||||
// 存储原始的、未映射的权限(用于保存时)
|
||||
const [originalRoutePermissionsMap, setOriginalRoutePermissionsMap] = useState<Map<number, ApiPermission[]>>(new Map());
|
||||
const [originalAllPermissions, setOriginalAllPermissions] = useState<ApiPermission[]>([]);
|
||||
|
||||
// 选择角色
|
||||
const handleSelectRole = async (role: RoleInfo) => {
|
||||
setSelectedRole(role);
|
||||
|
||||
// 动态导入权限映射工具
|
||||
const { mapPermissions } = await import('~/utils/permission-mapper');
|
||||
|
||||
// v3.0: 并行加载数据
|
||||
const [routesResult, rolePermissions, users] = await Promise.all([
|
||||
getRoleRoutesWithPermissions(role.id),
|
||||
@@ -958,26 +966,49 @@ export default function RolePermissions() {
|
||||
|
||||
const { routes: routesWithPerms, selectedRouteIds: routeIds } = routesResult;
|
||||
|
||||
// 构建 routePermissionsMap:从返回的路由中提取每个路由的可用 permissions
|
||||
const permMap = new Map<number, ApiPermission[]>();
|
||||
const extractPermissions = (routes: RouteInfo[]) => {
|
||||
// 构建原始权限映射(未映射的,用于保存)
|
||||
const originalPermMap = new Map<number, ApiPermission[]>();
|
||||
// 存储所有原始权限的列表
|
||||
const allOriginalPerms: ApiPermission[] = [];
|
||||
const extractOriginalPermissions = (routes: RouteInfo[]) => {
|
||||
routes.forEach(route => {
|
||||
if (route.permissions && route.permissions.length > 0) {
|
||||
permMap.set(route.id, route.permissions);
|
||||
originalPermMap.set(route.id, route.permissions);
|
||||
allOriginalPerms.push(...route.permissions);
|
||||
}
|
||||
if (route.children) {
|
||||
extractPermissions(route.children);
|
||||
extractOriginalPermissions(route.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
extractPermissions(routesWithPerms);
|
||||
extractOriginalPermissions(routesWithPerms);
|
||||
|
||||
// 从 getRolePermissions 结果中提取已分配的权限ID
|
||||
// 存储原始权限
|
||||
setOriginalRoutePermissionsMap(originalPermMap);
|
||||
setOriginalAllPermissions(allOriginalPerms);
|
||||
|
||||
// 构建映射后的权限映射(用于显示)
|
||||
const displayPermMap = new Map<number, ApiPermission[]>();
|
||||
const extractDisplayPermissions = (routes: RouteInfo[]) => {
|
||||
routes.forEach(route => {
|
||||
if (route.permissions && route.permissions.length > 0) {
|
||||
const mappedPermissions = mapPermissions(route.permissions);
|
||||
displayPermMap.set(route.id, mappedPermissions);
|
||||
}
|
||||
if (route.children) {
|
||||
extractDisplayPermissions(route.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
extractDisplayPermissions(routesWithPerms);
|
||||
|
||||
// 从 getRolePermissions 结果中提取已分配的权限ID(原始ID)
|
||||
const assignedPermissionIds = rolePermissions.map(p => p.permission_id);
|
||||
|
||||
setRoutePermissionsMap(permMap);
|
||||
// 存储状态
|
||||
setRoutePermissionsMap(displayPermMap); // 用于显示
|
||||
setSelectedRouteIds(routeIds);
|
||||
setSelectedPermissionIds(assignedPermissionIds); // 使用实际已分配的权限ID
|
||||
setSelectedPermissionIds(assignedPermissionIds); // 使用原始权限ID
|
||||
setExpandedRouteIds([]); // 重置展开状态
|
||||
setRoleUsers(users);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* 权限键映射工具
|
||||
* 用于处理数据库权限键与路由装饰器检查权限键不一致的情况
|
||||
*/
|
||||
|
||||
/**
|
||||
* 权限键映射表
|
||||
* key: 数据库中的权限键
|
||||
* value: 前端显示和实际生效的权限键
|
||||
*
|
||||
* 当前配置:不做映射,直接显示数据库权限键
|
||||
* 因为权限已经细分为CRUD操作,不需要合并显示
|
||||
*/
|
||||
const PERMISSION_KEY_MAP: Record<string, string> = {
|
||||
// 知识库绑定相关 - 直接显示数据库权限键,不映射
|
||||
// 'dify:bind:list': 'dify:bind:list',
|
||||
// 'dify:bind:create': 'dify:bind:create',
|
||||
// 'dify:bind:update': 'dify:bind:update',
|
||||
// 'dify:bind:delete': 'dify:bind:delete',
|
||||
};
|
||||
|
||||
/**
|
||||
* 反向映射表:实际权限键 -> 数据库权限键列表
|
||||
* 当前配置:不进行反向映射
|
||||
*/
|
||||
const REVERSE_PERMISSION_MAP: Record<string, string[]> = {
|
||||
// 'dify:bind:list': ['dify:bind:list'],
|
||||
};
|
||||
|
||||
/**
|
||||
* 将数据库权限键转换为显示权限键
|
||||
* @param permissionKey 数据库中的权限键
|
||||
* @returns 前端显示的权限键(无映射时返回原值)
|
||||
*/
|
||||
export function mapPermissionKey(permissionKey: string): string {
|
||||
return PERMISSION_KEY_MAP[permissionKey] || permissionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量转换权限键列表
|
||||
* @param permissionKeys 权限键数组
|
||||
* @returns 转换后的权限键数组(去重)
|
||||
*/
|
||||
export function mapPermissionKeys(permissionKeys: string[]): string[] {
|
||||
const mappedKeys = permissionKeys.map(key => mapPermissionKey(key));
|
||||
return [...new Set(mappedKeys)]; // 去重
|
||||
}
|
||||
|
||||
/**
|
||||
* 反向查找:根据显示权限键找到对应的数据库权限键
|
||||
* @param effectivePermissionKey 显示权限键
|
||||
* @returns 对应的数据库权限键列表(无映射时返回原值)
|
||||
*/
|
||||
export function reverseMapPermissionKey(permissionKey: string): string[] {
|
||||
// 查找所有映射到该权限键的原始权限键
|
||||
const originalKeys: string[] = [];
|
||||
for (const [key, mappedKey] of Object.entries(PERMISSION_KEY_MAP)) {
|
||||
if (mappedKey === permissionKey) {
|
||||
originalKeys.push(key);
|
||||
}
|
||||
}
|
||||
return originalKeys.length > 0 ? originalKeys : [permissionKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换权限对象:将数据库权限对象转换为显示权限对象
|
||||
*/
|
||||
export interface Permission {
|
||||
id: number;
|
||||
permission_key: string;
|
||||
display_name: string;
|
||||
api_method?: string;
|
||||
api_path?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function mapPermission(permission: Permission): Permission {
|
||||
const mappedKey = mapPermissionKey(permission.permission_key);
|
||||
|
||||
// 如果权限键被映射,更新显示名称(可根据业务逻辑调整)
|
||||
let displayName = permission.display_name;
|
||||
if (mappedKey !== permission.permission_key) {
|
||||
// 这里可以添加自定义的显示名称映射逻辑
|
||||
}
|
||||
|
||||
return {
|
||||
...permission,
|
||||
permission_key: mappedKey,
|
||||
display_name: displayName,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量转换权限列表
|
||||
* @param permissions 权限对象数组
|
||||
* @returns 转换后的权限对象数组(去重)
|
||||
*/
|
||||
export function mapPermissions(permissions: Permission[]): Permission[] {
|
||||
const mappedMap = new Map<string, Permission>();
|
||||
|
||||
permissions.forEach(permission => {
|
||||
const mapped = mapPermission(permission);
|
||||
|
||||
// 如果映射后的权限键已存在,合并(保留第一个)
|
||||
if (!mappedMap.has(mapped.permission_key)) {
|
||||
mappedMap.set(mapped.permission_key, mapped);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(mappedMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据显示的权限ID查找对应的数据库权限ID(用于保存)
|
||||
*/
|
||||
export function findDbPermissionIds(
|
||||
displayPermissionId: number,
|
||||
allMappedPermissions: Permission[],
|
||||
originalPermissions: Permission[]
|
||||
): number[] {
|
||||
const displayPerm = allMappedPermissions.find(p => p.id === displayPermissionId);
|
||||
if (!displayPerm) return [];
|
||||
|
||||
const dbKeys = reverseMapPermissionKey(displayPerm.permission_key);
|
||||
|
||||
const dbIds: number[] = [];
|
||||
for (const key of dbKeys) {
|
||||
const originalPerm = originalPermissions.find(p => p.permission_key === key);
|
||||
if (originalPerm) {
|
||||
dbIds.push(originalPerm.id);
|
||||
}
|
||||
}
|
||||
|
||||
return dbIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限是否受映射影响
|
||||
*/
|
||||
export function hasPermissionMapping(permissionKey: string): boolean {
|
||||
return PERMISSION_KEY_MAP[permissionKey] !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限映射的说明信息
|
||||
*/
|
||||
export function getPermissionMappingInfo(): string {
|
||||
return `权限映射说明:\n` +
|
||||
`当前未启用权限键映射,直接使用数据库权限键`;
|
||||
}
|
||||
Reference in New Issue
Block a user