import { nanoid } from 'nanoid'; import { WebSocket } from 'ws'; import { logger } from '../logger.ts'; import { EventEmitter } from 'eventemitter3'; class WsMessage { ws: WebSocket; user?: string; emitter: EventEmitter;; constructor({ ws, user }: WssMessageOptions) { this.ws = ws; this.user = user; this.emitter = new EventEmitter(); } async sendResponse(data: any) { if (data.id) { this.emitter.emit(data.id, data?.data); } } async sendData(data: any, context?: any, opts?: { timeout?: number }) { if (this.ws.readyState !== WebSocket.OPEN) { return { code: 500, message: 'WebSocket is not open' }; } const timeout = opts?.timeout || 10 * 6 * 1000; // 10 minutes const id = nanoid(); const message = JSON.stringify({ id, type: 'proxy', data: { message: data, context: context || {}, }, }); logger.info('ws-proxy sendData', message); this.ws.send(message); const msg = { path: data?.path, key: data?.key, id: data?.id }; return new Promise((resolve) => { const timer = setTimeout(() => { resolve({ code: 500, message: `运行超时,执行的id: ${id},参数是${JSON.stringify(msg)}`, }); }, timeout); this.emitter.once(id, (data: any) => { resolve(data); clearTimeout(timer); }); }); } } type WssMessageOptions = { ws: WebSocket; user?: string; }; export class WsProxyManager { wssMap: Map = new Map(); constructor() { } register(id: string, opts?: { ws: WebSocket; user: string }) { if (this.wssMap.has(id)) { const value = this.wssMap.get(id); if (value) { value.ws.close(); } } const [username, appId] = id.split('-'); const url = new URL(`/${username}/v1/${appId}`, 'https://kevisual.cn/'); console.log('WsProxyManager register', id, '访问地址', url.toString()); const value = new WsMessage({ ws: opts?.ws, user: opts?.user }); this.wssMap.set(id, value); } unregister(id: string) { const value = this.wssMap.get(id); if (value) { value.ws.close(); } this.wssMap.delete(id); } getIds(beginWith?: string) { if (beginWith) { return Array.from(this.wssMap.keys()).filter(key => key.startsWith(beginWith)); } return Array.from(this.wssMap.keys()); } get(id: string) { return this.wssMap.get(id); } }