import { AsyncLocalStorage } from 'async_hooks' import { ShrimpCode, IShrimpResponse, IModuleInfo, IShrimpOperator, IMenuItem, ILabel, } from "../IShrimpType" import { IPasswordHasher, bcryptHasher } from '../PasswordHasher' import type { ShrimpModule } from "./ShrimpModule" import type { ShrimpCrud } from "./ShrimpCrud" export class Shrimp { root: Shrimp = this modules: Map = new Map() /** 全局 CRUD 注册表,用于自动解析 relation */ crudRegistry: Map> = new Map() /** * 密码哈希器,默认 bcryptHasher(lazy import bcrypt)。 * 消费方可在启动时替换:`Shrimp.passwordHasher = plainHasher` */ static passwordHasher: IPasswordHasher = bcryptHasher /** 请求级操作者上下文 */ static asyncLocalStorage = new AsyncLocalStorage() get operator(): IShrimpOperator | null { return Shrimp.asyncLocalStorage.getStore() ?? null } async call(path: string[], ...args: any[]): Promise { let module = this.modules.get(path[0]) if (module == null) { throw new Error(`Module "${path[0]}" not found`) } return await module.call(path.slice(1), ...args) } checkEvent(powers: Set, path: string[]): boolean { let module = this.modules.get(path[0]) if (module == null) return false return module.checkEvent(powers, path.slice(1)) } getModuleInfo(powers: Set): IModuleInfo[] { const list: IModuleInfo[] = [] this.collectModuleInfo(this.modules, powers, undefined, list) return list } private collectModuleInfo(modules: Map, powers: Set, parent: string | undefined, out: IModuleInfo[]) { modules.forEach(module => { if (!powers.has(module.power.key)) return const info: IModuleInfo = { moduleKey: module.moduleKey, events: module.getEventInfo(powers), label: module.label, fields: module.getFields(), } if (module.icon) info.icon = module.icon if (parent) info.parent = parent out.push(info) if (module.modules.size > 0) { this.collectModuleInfo(module.modules, powers, module.moduleKey, out) } }) } success(data: T, tip?: ILabel, log?: string): IShrimpResponse { const r: IShrimpResponse = { code: ShrimpCode.OK, data } if (tip) r.tip = tip if (log) r.log = log return r } fail(code: ShrimpCode, tip?: ILabel, log?: string): IShrimpResponse { const r: IShrimpResponse = { code } if (tip) r.tip = tip if (log) r.log = log return r } async allPower(): Promise<{ key: string; label: ILabel }[]> { const powers: { key: string; label: ILabel }[] = [] const collect = (module: any) => { powers.push({ key: module.power.key, label: module.power.label }) powers.push(...module.power.children) module.modules.forEach(collect) } this.root.modules.forEach(collect) const map = new Map() powers.forEach(p => map.set(p.key, p.label)) return Array.from(map.entries()).map(([key, label]) => ({ key, label })) } /** 菜单树,默认平铺所有模块;子类可覆盖为分组结构 */ getMenuTree(): IMenuItem[] { const items: IMenuItem[] = [] this.modules.forEach(module => { items.push({ name: module.moduleKey, label: module.label, moduleKey: module.moduleKey }) }) return items } /** 根据权限过滤菜单树 */ filterMenuTree(fullMenuTree: IMenuItem[], rolePower: string[]): IMenuItem[] { const allowedModules = new Set(rolePower.map(p => p.split('.')[0])) const filter = (nodes: IMenuItem[]): IMenuItem[] => { const result: IMenuItem[] = [] for (const node of nodes) { if (node.moduleKey) { if (allowedModules.has(node.moduleKey)) { result.push(node) } continue } if (!node.children || node.children.length === 0) { continue } const filteredChildren = filter(node.children) if (filteredChildren.length === 0) continue result.push({ ...node, children: filteredChildren }) } return result } return filter(fullMenuTree) } }