Files
ai-agent-admin/web/apps/web-ele/src/router/access.ts
T

241 lines
6.7 KiB
TypeScript

import type {
ComponentRecordType,
GenerateMenuAndRoutesOptions,
RouteRecordStringComponent,
} from '@vben/types';
import { generateAccessible } from '@vben/access';
import { preferences } from '@vben/preferences';
import { getAllMenusApi } from '#/api/core/menu';
import { BasicLayout, IFrameView } from '#/layouts';
import { useAppContextStore } from '#/store/app-context';
import { isLightMenuName } from './light-menu';
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
const LEGACY_COMPONENT_MAP: Record<string, string> = {
'/online-dev/page-render/index': '/_core/page-render/index',
'online-dev/page-render/index': '/_core/page-render/index',
};
function normalizeViewPath(path: string) {
const mappedPath = LEGACY_COMPONENT_MAP[path] || path;
const normalizedPath = mappedPath.replace(/^(\.\/|\.\.\/)+/, '');
const viewPath = normalizedPath.startsWith('/')
? normalizedPath
: `/${normalizedPath}`;
return viewPath.replace(/^\/views/, '');
}
function normalizeRouteComponent(component?: string) {
if (!component) return component;
return LEGACY_COMPONENT_MAP[component] || component;
}
function hasPageComponent(
component: string | undefined,
pageMap: ComponentRecordType,
layoutMap: ComponentRecordType,
) {
if (!component) return true;
if (layoutMap[component]) return true;
const normalizedPath = normalizeViewPath(component);
const pageKey = normalizedPath.endsWith('.vue')
? normalizedPath
: `${normalizedPath}.vue`;
return Boolean(pageMap[pageKey]);
}
function normalizePageMap(pageMap: ComponentRecordType) {
return Object.fromEntries(
Object.entries(pageMap).map(([key, value]) => [
normalizeViewPath(key),
value,
]),
) as ComponentRecordType;
}
function isLightMenuRoute(route: { name?: string }) {
return isLightMenuName(route.name);
}
function createLightAiMenuRoute(
route: Pick<RouteRecordStringComponent, 'component' | 'name' | 'path'> & {
meta: RouteRecordStringComponent['meta'];
},
): RouteRecordStringComponent {
return route as RouteRecordStringComponent;
}
const LIGHT_AI_PLATFORM_MENU_ROUTES = [
createLightAiMenuRoute({
component: '/_core/agent-chat/index',
meta: {
fullPathKey: true,
icon: 'lucide:code',
order: 60,
title: 'menu-title.codex',
},
name: 'CodexAgentChat',
path: '/agent-chat/codex',
}),
createLightAiMenuRoute({
component: '/ai-platform/model/index',
meta: {
fullPathKey: true,
icon: 'lucide:settings',
order: 50,
title: 'menu-title.modelConfig',
},
name: 'AIModelConfig',
path: '/ai-platform/model',
}),
createLightAiMenuRoute({
component: '/ai-platform/workflow-runs/index',
meta: {
fullPathKey: true,
icon: 'lucide:history',
order: 40,
title: 'menu-title.workflowRunHistory',
},
name: 'AIWorkflowRuns',
path: '/ai-platform/workflow-runs',
}),
];
function appendLightAiPlatformMenus(routes: RouteRecordStringComponent[]) {
return routes.map((route) => {
const children = route.children
? appendLightAiPlatformMenus(route.children)
: undefined;
if (route.name !== 'AIPlatform') {
return children ? { ...route, children } : route;
}
const existingNames = new Set(children?.map((item) => item.name));
const missingRoutes = LIGHT_AI_PLATFORM_MENU_ROUTES.filter(
(item) => !existingNames.has(item.name),
);
const nextChildren = [...(children || []), ...missingRoutes].sort(
(a, b) => Number(a.meta?.order || 0) - Number(b.meta?.order || 0),
);
return { ...route, children: nextChildren };
});
}
function filterAvailableRoutes<
T extends {
children?: T[];
component?: string;
name?: string;
path?: string;
query?: unknown;
},
>(
routes: T[],
pageMap: ComponentRecordType,
layoutMap: ComponentRecordType,
): T[] {
return routes
.map((route) => {
const children = route.children
? filterAvailableRoutes(route.children, pageMap, layoutMap)
: undefined;
const routeAllowed = isLightMenuRoute(route);
if (!routeAllowed && !children?.length) {
return undefined;
}
if (
!route.component &&
route.children &&
!children?.length
) {
return undefined;
}
if (!routeAllowed && children?.length) {
const groupRoute = { ...route, children };
delete groupRoute.component;
return groupRoute;
}
const component = normalizeRouteComponent(route.component);
if (!hasPageComponent(component, pageMap, layoutMap)) {
if (!children?.length) return undefined;
const groupRoute = { ...route, children };
delete groupRoute.component;
return groupRoute;
}
const nextRoute =
component && component !== route.component
? { ...route, component }
: route;
return children ? { ...nextRoute, children } : nextRoute;
})
.filter(Boolean) as T[];
}
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
const pageMap: ComponentRecordType = import.meta.glob([
'../views/_core/account-settings/index.vue',
'../views/_core/agent-chat/**/*.vue',
'../views/_core/announcement/index.vue',
'../views/_core/announcement/list.vue',
'../views/_core/authentication/login.vue',
'../views/_core/fallback/**/*.vue',
'../views/_core/menu/index.vue',
'../views/_core/message/index.vue',
'../views/_core/page-render/index.vue',
'../views/_core/permission/index.vue',
'../views/_core/role/index.vue',
'../views/_core/user/index.vue',
'../views/ai-platform/agent/editor/index.vue',
'../views/ai-platform/agent/index.vue',
'../views/ai-platform/knowledge/detail/index.vue',
'../views/ai-platform/knowledge/index.vue',
'../views/ai-platform/model/index.vue',
'../views/ai-platform/workflow/editor/index.vue',
'../views/ai-platform/workflow/index.vue',
'../views/ai-platform/workflow-runs/index.vue',
]);
const layoutMap: ComponentRecordType = {
BasicLayout,
IFrameView,
Layout: BasicLayout,
'Layout.vue': BasicLayout,
'/Layout.vue': BasicLayout,
};
const routePageMap = normalizePageMap(pageMap);
return await generateAccessible(preferences.app.accessMode, {
...options,
fetchMenuListAsync: async () => {
const appContextStore = useAppContextStore();
const menus = appContextStore.appCode
? await getAllMenusApi(appContextStore.appCode)
: await getAllMenusApi();
const lightMenus = filterAvailableRoutes(menus, routePageMap, layoutMap);
return appendLightAiPlatformMenus(lightMenus);
},
forbiddenComponent,
layoutMap,
pageMap,
});
}
export { generateAccess };