68395f820c
Remove token from WebSocket URL query parameters in ChallengeWebSocketService
and GameApiService. Instead, send {"type":"auth","token":"..."} as the first
text frame after the connection opens, matching the new backend auth protocol.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { Observable } from 'rxjs';
|
|
import { GameStreamEvent, ErrorEvent } from '../models/game.models';
|
|
|
|
const WS_CONNECT_TIMEOUT_MS = 3000;
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class StreamHandlerService {
|
|
createGameStream(wsUrl: string, gameId: string, token: string): Observable<GameStreamEvent> {
|
|
return new Observable<GameStreamEvent>((observer) => {
|
|
const ws = new WebSocket(wsUrl);
|
|
let connected = false;
|
|
|
|
const emitErrorEvent = (message: string): void => {
|
|
const errorEvent: ErrorEvent = {
|
|
type: 'error',
|
|
error: { code: 'STREAM_ERROR', message },
|
|
};
|
|
observer.next(errorEvent);
|
|
};
|
|
|
|
const failAndComplete = (reason: string): void => {
|
|
console.warn(`[StreamHandler] WebSocket failed for ${gameId}: ${reason}`);
|
|
emitErrorEvent(reason);
|
|
observer.complete();
|
|
};
|
|
|
|
const connectionTimeoutId = setTimeout(() => {
|
|
if (!connected) {
|
|
ws.close();
|
|
failAndComplete('WebSocket connection timed out — falling back to polling');
|
|
}
|
|
}, WS_CONNECT_TIMEOUT_MS);
|
|
|
|
ws.onopen = () => {
|
|
connected = true;
|
|
clearTimeout(connectionTimeoutId);
|
|
console.log(`[StreamHandler] WebSocket connected for ${gameId}`);
|
|
ws.send(JSON.stringify({ type: 'auth', token }));
|
|
};
|
|
|
|
ws.onmessage = (message) => {
|
|
const payload = typeof message.data === 'string' ? message.data : '';
|
|
if (!payload.trim()) return;
|
|
try {
|
|
const event = JSON.parse(payload) as GameStreamEvent;
|
|
observer.next(event);
|
|
} catch {
|
|
// ignore malformed frames
|
|
}
|
|
};
|
|
|
|
ws.onerror = () => {
|
|
clearTimeout(connectionTimeoutId);
|
|
if (!connected) {
|
|
failAndComplete('WebSocket connection error — falling back to polling');
|
|
}
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
clearTimeout(connectionTimeoutId);
|
|
if (connected) {
|
|
observer.complete();
|
|
}
|
|
};
|
|
|
|
return () => {
|
|
ws.close();
|
|
};
|
|
});
|
|
}
|
|
}
|