From b70d91866c9f52e8fc3bbdef6fefb037154032dc Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Fri, 17 Apr 2026 21:40:07 +0800 Subject: [PATCH] fix(room-ws): try reconnect with existing token before requesting new On auto-reconnect (scheduleReconnect), attempt connection with the stored wsToken first. If the WS closes immediately (server rejected the token), fall back to fetching a fresh token and retrying. Only requests a new token when the existing one fails or when connect() is called manually with forceNewToken=true. --- src/lib/room-ws-client.ts | 54 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/lib/room-ws-client.ts b/src/lib/room-ws-client.ts index 9ba0476..dbc05d8 100644 --- a/src/lib/room-ws-client.ts +++ b/src/lib/room-ws-client.ts @@ -133,7 +133,7 @@ export class RoomWsClient { return new Set(this.subscribedProjects); } - async connect(): Promise { + async connect(forceNewToken = false): Promise { if (this.ws && this.status === 'open') { return; } @@ -141,28 +141,31 @@ export class RoomWsClient { this.shouldReconnect = true; this.setStatus('connecting'); - // Fetch a fresh token for each connection attempt (backend consumes token on use) - try { - const tokenResp = await fetch(`${this.baseUrl}/api/ws/token`, { - method: 'POST', - credentials: 'include', - }); - if (!tokenResp.ok) { - const text = await tokenResp.text().catch(() => ''); - console.error(`[RoomWs] Token fetch failed: ${tokenResp.status} ${tokenResp.statusText} — ${text}`); - throw new Error(`Token fetch failed: ${tokenResp.status}`); + // Fetch a fresh token unless we have a valid existing one and not forcing. + // When forceNewToken=false (reconnect path), try existing token first. + if (forceNewToken || !this.wsToken) { + try { + const tokenResp = await fetch(`${this.baseUrl}/api/ws/token`, { + method: 'POST', + credentials: 'include', + }); + if (!tokenResp.ok) { + const text = await tokenResp.text().catch(() => ''); + console.error(`[RoomWs] Token fetch failed: ${tokenResp.status} ${tokenResp.statusText} — ${text}`); + throw new Error(`Token fetch failed: ${tokenResp.status}`); + } + const tokenData = await tokenResp.json(); + this.wsToken = tokenData.data?.token || null; + if (!this.wsToken) { + console.error('[RoomWs] Token is empty — not logged in?'); + throw new Error('No WS token received'); + } + } catch (err) { + console.error('[RoomWs] Failed to fetch WS token:', err); + this.setStatus('error'); + this.callbacks.onError?.(err instanceof Error ? err : new Error(String(err))); + throw err; } - const tokenData = await tokenResp.json(); - this.wsToken = tokenData.data?.token || null; - if (!this.wsToken) { - console.error('[RoomWs] Token is empty — not logged in?'); - throw new Error('No WS token received'); - } - } catch (err) { - console.error('[RoomWs] Failed to fetch WS token:', err); - this.setStatus('error'); - this.callbacks.onError?.(err instanceof Error ? err : new Error(String(err))); - throw err; } const wsUrl = this.buildWsUrl(); @@ -172,6 +175,13 @@ export class RoomWsClient { // Guard: if ws is closed before handlers are set, skip if (this.ws.readyState === WebSocket.CLOSED || this.ws.readyState === WebSocket.CLOSING) { console.warn('[RoomWs] WebSocket closed immediately'); + // If we used an existing token and it was immediately rejected, retry with a new token + if (!forceNewToken && this.wsToken) { + console.debug('[RoomWs] Existing token rejected — fetching new token and retrying'); + const savedToken = this.wsToken; + this.wsToken = null; + return this.connect(true); + } return; }