feat: add ws-proxy

This commit is contained in:
2025-05-22 14:33:24 +08:00
parent 1e2f891f31
commit 28a5d82e52
8 changed files with 438 additions and 190 deletions

View File

@@ -0,0 +1,84 @@
import { nanoid } from 'nanoid';
import { WebSocket } from 'ws';
import { logger } from '../logger.ts';
class WsMessage {
ws: WebSocket;
user?: string;
constructor({ ws, user }: WssMessageOptions) {
this.ws = ws;
this.user = user;
}
async sendData(data: 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,
});
logger.info('ws-proxy sendData', message);
this.ws.send(message);
return new Promise((resolve) => {
const timer = setTimeout(() => {
resolve({
code: 500,
message: 'timeout',
});
}, timeout);
this.ws.once('message', (event: Buffer) => {
const eventData = event.toString();
if (!eventData) {
return;
}
const data = JSON.parse(eventData);
if (data.id === id) {
resolve(data.data);
clearTimeout(timer);
}
});
});
}
}
type WssMessageOptions = {
ws: WebSocket;
user?: string;
};
export class WsProxyManager {
wssMap: Map<string, WsMessage> = new Map();
constructor() {}
getId(id: string, user?: string) {
return id + '/' + user;
}
register(id: string, opts?: { ws: WebSocket; user: string }) {
const _id = this.getId(id, opts?.user || '');
if (this.wssMap.has(_id)) {
const value = this.wssMap.get(_id);
if (value) {
value.ws.close();
}
}
const value = new WsMessage({ ws: opts?.ws, user: opts?.user });
this.wssMap.set(_id, value);
}
unregister(id: string, user?: string) {
const _id = this.getId(id, user || '');
const value = this.wssMap.get(_id);
if (value) {
value.ws.close();
}
this.wssMap.delete(_id);
}
getIds() {
return Array.from(this.wssMap.keys());
}
get(id: string, user?: string) {
if (user) {
const _id = this.getId(id, user);
return this.wssMap.get(_id);
}
return this.wssMap.get(id);
}
}