88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
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<string, WsMessage> = 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);
|
||
}
|
||
}
|