import { WSSManager } from './wss.ts'; import { App, Route } from '@kevisual/router' import { WebSocketReq, ListenProcessParams } from '@kevisual/router' import { EventEmitter } from 'eventemitter3'; import { customAlphabet } from 'nanoid'; const letter = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; const customId = customAlphabet(letter, 16); /** * 实时注册的代码模块 * * 别人通过 WebSocket 连接到此模块,发送路由列表和请求数据 */ export class LiveCode { wssManager: WSSManager; app: App; emitter: EventEmitter; constructor(app: App) { this.wssManager = new WSSManager({ heartbeatInterval: 6 * 5000 }); this.app = app; this.emitter = new EventEmitter(); console.log('[LiveCode] 模块已初始化'); } async conn(req: WebSocketReq) { const { ws, emitter, id } = req; const that = this; // @ts-ignore let wid = ws.data?.wid; if (!wid) { const _id = this.wssManager.addConnection(req, { userId: id }); // @ts-ignore ws.data.wid = _id; emitter.once('close--' + id, () => { that.wssManager.closeConnection(_id); this.deinitAppRoutes(_id); }); console.log('[LiveCode]新的 WebSocket 连接已打开', _id); const res = await that.init(_id); if (res.code === 200) { console.log('[LiveCode]初始化路由列表完成'); that.initAppRoutes(res.data?.list || [], _id); } else { console.error('[LiveCode]初始化路由列表失败:', res?.message); } return this; } that.onMessage(req); return this; } getWss(id: string) { return this.wssManager.getConnection(id) } async init(id: string): Promise<{ code: number, message?: string, data?: any }> { return this.sendData({ message: { path: 'router', key: 'list', } }, id); } sendData(data: ListenProcessParams, id: string): Promise<{ code: number, message?: string, data?: any }> { const reqId = customId() const wss = this.getWss(id); if (!wss) { return Promise.resolve({ code: 500, message: '连接不存在或已关闭' }); } const emitter = this.emitter; const wsReq = wss.wsReq; try { wsReq.ws.send(JSON.stringify({ type: 'router', id: reqId, data: data })); } catch (error) { console.error('[LiveCode]发送数据失败:', error); return Promise.resolve({ code: 500, message: '发送数据失败' }); } return new Promise((resolve) => { const timeout = setTimeout(() => { resolve({ code: 500, message: '请求超时' }); emitter.off(reqId, listenOnce); }, 5000); const listenOnce = (resData: any) => { clearTimeout(timeout); resolve(resData); emitter.off(reqId, listenOnce); } emitter.once(reqId, listenOnce); }); } onMessage(req: WebSocketReq) { const { data } = req; if (data?.id) { // console.log('LiveCode 收到消息:', data); this.emitter.emit(data.id, data.data); } else { console.warn('[LiveCode] 未知的消息格式', data); } } initAppRoutes(list: Route[], wid: string) { for (const route of list) { const path = route.path || ''; const id = route.id || ''; if (path.startsWith('router') || path.startsWith('auth') || path.startsWith('admin-autu') || path.startsWith('call')) { continue; } // console.log('注册路由:', route.path, route.description, route.metadata, route.id); this.app.route({ path: route.id, key: route.key, description: route.description, metadata: { ...route.metadata, source: 'livecode', liveCodeId: wid }, middleware: ['auth'], }).define(async (ctx) => { const { token, cookie, ...rest } = ctx.query; const tokenUser = ctx.state.tokernUser; const res = await this.sendData({ message: { id: route.id, payload: rest, }, context: { state: { tokenUser } } }, wid); // console.log('路由响应数据:', res); ctx.forward(res) }).addTo(this.app, { // override: false, // // @ts-ignore // overwrite: false }); } } deinitAppRoutes(wid: string) { const routesToRemove = this.app.routes.filter(route => route.metadata?.liveCodeId === wid); for (const route of routesToRemove) { this.app.removeById(route.id); } } }