import { Router, Request, Response, NextFunction } from 'express'; import path from 'path'; import { PluginRouterRegistry, PluginRequestHandler, PluginApiRouteDefinition, PluginPageDefinition, PluginHttpRequest, PluginHttpResponse, HttpMethod, MemoryStaticFile, } from './types'; /** * 包装 Express Request 为 PluginHttpRequest */ function wrapRequest (req: Request): PluginHttpRequest { return { path: req.path, method: req.method, query: req.query as Record, body: req.body, headers: req.headers as Record, params: req.params, raw: req, }; } /** * 包装 Express Response 为 PluginHttpResponse */ function wrapResponse (res: Response): PluginHttpResponse { const wrapped: PluginHttpResponse = { status (code: number) { res.status(code); return wrapped; }, json (data: unknown) { res.json(data); }, send (data: string | Buffer) { res.send(data); }, setHeader (name: string, value: string) { res.setHeader(name, value); return wrapped; }, sendFile (filePath: string) { res.sendFile(filePath); }, redirect (url: string) { res.redirect(url); }, raw: res, }; return wrapped; } /** * 插件路由注册器实现 * 为每个插件创建独立的路由注册器,收集路由定义 */ /** 内存静态路由定义 */ interface MemoryStaticRoute { urlPath: string; files: MemoryStaticFile[]; } export class PluginRouterRegistryImpl implements PluginRouterRegistry { private apiRoutes: PluginApiRouteDefinition[] = []; private apiNoAuthRoutes: PluginApiRouteDefinition[] = []; private pageDefinitions: PluginPageDefinition[] = []; private staticRoutes: Array<{ urlPath: string; localPath: string; }> = []; private memoryStaticRoutes: MemoryStaticRoute[] = []; constructor ( private readonly pluginId: string, private readonly pluginPath: string ) { } // ==================== API 路由注册 ==================== api (method: HttpMethod, routePath: string, handler: PluginRequestHandler): void { this.apiRoutes.push({ method, path: routePath, handler }); } get (routePath: string, handler: PluginRequestHandler): void { this.api('get', routePath, handler); } post (routePath: string, handler: PluginRequestHandler): void { this.api('post', routePath, handler); } put (routePath: string, handler: PluginRequestHandler): void { this.api('put', routePath, handler); } delete (routePath: string, handler: PluginRequestHandler): void { this.api('delete', routePath, handler); } // ==================== 无认证 API 路由注册 ==================== apiNoAuth (method: HttpMethod, routePath: string, handler: PluginRequestHandler): void { this.apiNoAuthRoutes.push({ method, path: routePath, handler }); } getNoAuth (routePath: string, handler: PluginRequestHandler): void { this.apiNoAuth('get', routePath, handler); } postNoAuth (routePath: string, handler: PluginRequestHandler): void { this.apiNoAuth('post', routePath, handler); } putNoAuth (routePath: string, handler: PluginRequestHandler): void { this.apiNoAuth('put', routePath, handler); } deleteNoAuth (routePath: string, handler: PluginRequestHandler): void { this.apiNoAuth('delete', routePath, handler); } // ==================== 页面注册 ==================== page (pageDef: PluginPageDefinition): void { this.pageDefinitions.push(pageDef); } pages (pageDefs: PluginPageDefinition[]): void { this.pageDefinitions.push(...pageDefs); } // ==================== 静态资源 ==================== static (urlPath: string, localPath: string): void { // 如果是相对路径,则相对于插件目录 const absolutePath = path.isAbsolute(localPath) ? localPath : path.join(this.pluginPath, localPath); this.staticRoutes.push({ urlPath, localPath: absolutePath }); } staticOnMem (urlPath: string, files: MemoryStaticFile[]): void { this.memoryStaticRoutes.push({ urlPath, files }); } // ==================== 构建路由 ==================== /** * 构建 Express Router(用于 API 路由) * 注意:静态资源路由不在此处挂载,由 webui-backend 直接在不需要鉴权的路径下处理 */ buildApiRouter (): Router { const router = Router(); // 注册 API 路由 for (const route of this.apiRoutes) { const handler = this.wrapHandler(route.handler); switch (route.method) { case 'get': router.get(route.path, handler); break; case 'post': router.post(route.path, handler); break; case 'put': router.put(route.path, handler); break; case 'delete': router.delete(route.path, handler); break; case 'patch': router.patch(route.path, handler); break; case 'all': router.all(route.path, handler); break; } } return router; } /** * 包装处理器,添加错误处理和请求/响应包装 */ private wrapHandler (handler: PluginRequestHandler): (req: Request, res: Response, next: NextFunction) => void { return async (req: Request, res: Response, next: NextFunction) => { try { const wrappedReq = wrapRequest(req); const wrappedRes = wrapResponse(res); await handler(wrappedReq, wrappedRes, next); } catch (error: any) { console.error(`[Plugin: ${this.pluginId}] Route error:`, error); if (!res.headersSent) { res.status(500).json({ code: -1, message: `Plugin error: ${error.message || 'Unknown error'}`, }); } } }; } // ==================== 查询方法 ==================== /** * 检查是否有注册的 API 路由(需要认证) */ hasApiRoutes (): boolean { return this.apiRoutes.length > 0; } /** * 检查是否有注册的无认证 API 路由 */ hasApiNoAuthRoutes (): boolean { return this.apiNoAuthRoutes.length > 0; } /** * 构建无认证 Express Router(用于 /plugin/{pluginId}/api/ 路径) */ buildApiNoAuthRouter (): Router { const router = Router(); for (const route of this.apiNoAuthRoutes) { const handler = this.wrapHandler(route.handler); switch (route.method) { case 'get': router.get(route.path, handler); break; case 'post': router.post(route.path, handler); break; case 'put': router.put(route.path, handler); break; case 'delete': router.delete(route.path, handler); break; case 'patch': router.patch(route.path, handler); break; case 'all': router.all(route.path, handler); break; } } return router; } /** * 检查是否有注册的静态资源路由 */ hasStaticRoutes (): boolean { return this.staticRoutes.length > 0 || this.memoryStaticRoutes.length > 0; } /** * 检查是否有注册的页面 */ hasPages (): boolean { return this.pageDefinitions.length > 0; } /** * 获取所有注册的页面定义 */ getPages (): PluginPageDefinition[] { return [...this.pageDefinitions]; } /** * 获取插件 ID */ getPluginId (): string { return this.pluginId; } /** * 获取插件路径 */ getPluginPath (): string { return this.pluginPath; } /** * 获取所有注册的静态路由 */ getStaticRoutes (): Array<{ urlPath: string; localPath: string; }> { return [...this.staticRoutes]; } /** * 获取所有注册的内存静态路由 */ getMemoryStaticRoutes (): MemoryStaticRoute[] { return [...this.memoryStaticRoutes]; } /** * 清空路由(用于插件卸载) */ clear (): void { this.apiRoutes = []; this.apiNoAuthRoutes = []; this.pageDefinitions = []; this.staticRoutes = []; this.memoryStaticRoutes = []; } }