From 5200cf4c38bffb618263118179473b28113bdc3b Mon Sep 17 00:00:00 2001 From: abearxiong Date: Thu, 5 Feb 2026 14:08:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=A8=E6=88=B7=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E9=94=AE=E7=9A=84=E5=88=86=E9=9A=94=E7=AC=A6=EF=BC=8C?= =?UTF-8?q?=E4=BB=8E=20'-'=20=E6=9B=B4=E6=94=B9=E4=B8=BA=20'--'=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E4=BF=9D=E6=8C=81=E4=B8=80=E8=87=B4=E6=80=A7=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=20WebSocket=20=E8=BF=9E=E6=8E=A5=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/html/studio-app-list/index.ts | 6 +++--- src/modules/ws-proxy/index.ts | 2 +- src/modules/ws-proxy/manager.ts | 23 +++++++++++++++++++++-- src/modules/ws-proxy/proxy.ts | 12 ++++++------ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/modules/html/studio-app-list/index.ts b/src/modules/html/studio-app-list/index.ts index 364ea31..3a7cd0d 100644 --- a/src/modules/html/studio-app-list/index.ts +++ b/src/modules/html/studio-app-list/index.ts @@ -3,11 +3,11 @@ export const createStudioAppListHtml = (opts: StudioOpts) => { const user = opts.user!; const userAppKey = opts?.userAppKey; let showUserAppKey = userAppKey; - if (showUserAppKey && showUserAppKey.startsWith(user + '-')) { - showUserAppKey = showUserAppKey.replace(user + '-', ''); + if (showUserAppKey && showUserAppKey.startsWith(user + '--')) { + showUserAppKey = showUserAppKey.replace(user + '--', ''); } const pathApps = opts?.appIds?.map(appId => { - const shortAppId = appId.replace(opts!.user + '-', '') + const shortAppId = appId.replace(opts!.user + '--', '') return { appId, shortAppId, diff --git a/src/modules/ws-proxy/index.ts b/src/modules/ws-proxy/index.ts index 4b886e8..f545aee 100644 --- a/src/modules/ws-proxy/index.ts +++ b/src/modules/ws-proxy/index.ts @@ -20,7 +20,7 @@ export const wssFun: WebSocketListenerFun = async (req, res) => { return; } const user = loginUser?.tokenUser?.username; - const userApp = user + '-' + id; + const userApp = user + '--' + id; logger.debug('注册 ws 连接', userApp); const wsMessage = wsProxyManager.get(userApp); if (wsMessage) { diff --git a/src/modules/ws-proxy/manager.ts b/src/modules/ws-proxy/manager.ts index 1c706f8..3997bc9 100644 --- a/src/modules/ws-proxy/manager.ts +++ b/src/modules/ws-proxy/manager.ts @@ -2,6 +2,7 @@ import { nanoid } from 'nanoid'; import { WebSocket } from 'ws'; import { logger } from '../logger.ts'; import { EventEmitter } from 'eventemitter3'; +import { set } from 'zod'; class WsMessage { ws: WebSocket; @@ -82,7 +83,13 @@ type WssMessageOptions = { }; export class WsProxyManager { wssMap: Map = new Map(); - constructor() { } + PING_INTERVAL = 30000; // 30 秒检查一次连接状态 + constructor(opts?: { pingInterval?: number }) { + if (opts?.pingInterval) { + this.PING_INTERVAL = opts.pingInterval; + } + this.checkConnceted(); + } register(id: string, opts?: { ws: WebSocket; user: string }) { if (this.wssMap.has(id)) { const value = this.wssMap.get(id); @@ -91,7 +98,7 @@ export class WsProxyManager { value.destroy(); } } - const [username, appId] = id.split('-'); + 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 }); @@ -114,4 +121,16 @@ export class WsProxyManager { get(id: string) { return this.wssMap.get(id); } + checkConnceted() { + const that = this; + setTimeout(() => { + that.wssMap.forEach((value, key) => { + if (value.ws.readyState !== WebSocket.OPEN) { + logger.debug('ws not connected, unregister', key); + that.unregister(key); + } + }); + that.checkConnceted(); + }, this.PING_INTERVAL); + } } diff --git a/src/modules/ws-proxy/proxy.ts b/src/modules/ws-proxy/proxy.ts index 82f428f..823f3e6 100644 --- a/src/modules/ws-proxy/proxy.ts +++ b/src/modules/ws-proxy/proxy.ts @@ -32,7 +32,7 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt if (!userAppKey) { if (isAdmin) { // 获取所有的管理员的应用列表 - const ids = wsProxyManager.getIds(user + '-'); + const ids = wsProxyManager.getIds(user + '--'); const html = createStudioAppListHtml({ user, appIds: ids, userAppKey }); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); @@ -42,8 +42,8 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt return false; } } - if (!userAppKey.includes('-')) { - userAppKey = user + '-' + userAppKey; + if (!userAppKey.includes('--')) { + userAppKey = user + '--' + userAppKey; } // TODO: 如果不是管理员,是否需要添加其他人可以访问的逻辑? @@ -51,12 +51,12 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt opts?.createNotFoundPage?.('没有访问应用权限'); return false; } - if (!userAppKey.startsWith(user + '-')) { - userAppKey = user + '-' + userAppKey; + if (!userAppKey.startsWith(user + '--')) { + userAppKey = user + '--' + userAppKey; } logger.debug('data', data); const client = wsProxyManager.get(userAppKey); - const ids = wsProxyManager.getIds(user + '-'); + const ids = wsProxyManager.getIds(user + '--'); if (!client) { if (isAdmin) { const html = createStudioAppListHtml({ user, appIds: ids, userAppKey });