Create websocket exception filter

landing-page-layout
MiguelMLorente 2025-02-04 23:34:23 +01:00
parent e323623007
commit 5d55631812
8 changed files with 111 additions and 1 deletions

View File

@ -1,4 +1,4 @@
import { Injectable, Logger } from '@nestjs/common';
import { Injectable, Logger, UseFilters } from '@nestjs/common';
import {
ConnectedSocket,
OnGatewayConnection,
@ -10,6 +10,8 @@ import { PlayerService } from './players/player.service';
import { GameService } from 'src/games/game.service';
import { Socket } from 'socket.io';
import { ClientEvent, CreateLobbyEvent, JoinLobbyEvent } from 'interface';
import { WsExceptionFilter } from './websocket-exception-filter';
import { PlayerNotFoundException } from './exceptions';
@WebSocketGateway({ cors: true })
@Injectable()
@ -24,6 +26,7 @@ export class AppService implements OnGatewayConnection {
this.playerService.createPlayer(client.id);
}
@UseFilters(new WsExceptionFilter([PlayerNotFoundException]))
@SubscribeMessage(ClientEvent.CREATE_LOBBY)
handleCreateLobby(
@ConnectedSocket() client: Socket,

29
app/src/exceptions.ts Normal file
View File

@ -0,0 +1,29 @@
import { WsException } from '@nestjs/websockets';
import { ErrorCode } from 'interface';
export abstract class WebSocketException extends WsException {
public readonly errorCode: ErrorCode;
constructor(message: string, errorCode: ErrorCode) {
super(message);
this.errorCode = errorCode;
}
}
export class PlayerNotFoundException extends WebSocketException {
constructor(playerId: string) {
super(
'Unable to find player from ID: ' + playerId,
ErrorCode.PLAYER_NOT_FOUND,
);
}
}
export class GameNotFoundException extends WebSocketException {
constructor(gameId: string) {
super(
'Unable to find game from ID: ' + gameId,
ErrorCode.GAME_NOT_FOUND,
);
}
}

View File

@ -1,5 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { Player } from './player';
import { PlayerNotFoundException } from 'src/exceptions';
@Injectable()
export class PlayerService {
@ -17,6 +18,10 @@ export class PlayerService {
getPlayer(socketId: string) {
this.logger.log(this.players.get(socketId).userName);
const player = this.players.get(socketId);
if (!player) {
throw new PlayerNotFoundException(socketId);
}
return this.players.get(socketId);
}
}

View File

@ -0,0 +1,44 @@
import { ArgumentsHost, Catch, Logger } from '@nestjs/common';
import { BaseWsExceptionFilter } from '@nestjs/websockets';
import { GameNotFoundException, PlayerNotFoundException, WebSocketException } from './exceptions';
import { Socket } from 'socket.io';
import { ClientEvent } from 'interface';
import { emitCreateLobbyError } from 'interface';
@Catch(PlayerNotFoundException, GameNotFoundException)
export class WsExceptionFilter extends BaseWsExceptionFilter<WebSocketException> {
private readonly logger = new Logger(BaseWsExceptionFilter.name);
private readonly caughtExceptions: (typeof WebSocketException)[];
constructor(caughtExceptions: (typeof WebSocketException)[]) {
super();
this.caughtExceptions = caughtExceptions;
}
catch(exception: WebSocketException, host: ArgumentsHost) {
const socket = host.switchToWs().getClient() as Socket;
const method = host.switchToWs().getPattern();
const data: object = host.switchToWs().getData();
this.logger.log(
`Caught exception: ${exception}
for request to ${method}
from client ${socket.id}
with input ${data}`,
);
if (
!this.caughtExceptions.find(
(caughtException) => exception instanceof caughtException,
)
) {
this.logger.fatal("Uncaught exception", exception)
super.catch(exception, host);
}
switch (method) {
case ClientEvent.CREATE_LOBBY:
emitCreateLobbyError(socket, { error: exception.errorCode });
break;
}
}
}

View File

@ -1,4 +1,5 @@
export * from "./constants/TrackType";
export * from "./server-events/CreateLobbyError";
export * from "./server-events/ServerError";
export * from "./server-events/ServerEvent";
export * from "./server-events/UpdateLobbyEvent";

View File

@ -0,0 +1,22 @@
import { Socket as ServerSocket } from "socket.io";
import { Socket as ClientSocket } from "socket.io-client";
import { ErrorCode, ServerError } from "./ServerError";
export type CreateLobbyError = {
error: ErrorCode.PLAYER_NOT_FOUND | ErrorCode.GAME_NOT_FOUND;
};
export const emitCreateLobbyError = (
socket: ServerSocket,
payload: CreateLobbyError,
) => {
socket.emit(ServerError.CREATE_LOBBY_ERROR, payload);
};
export const attachHandlerToCreateLobbyError = (
socket: ClientSocket,
handler: (payload: CreateLobbyError) => void,
): (() => void) => {
socket.on(ServerError.CREATE_LOBBY_ERROR, handler);
return () => socket.off(ServerError.CREATE_LOBBY_ERROR);
};

View File

@ -2,3 +2,8 @@ export enum ServerError {
CREATE_LOBBY_ERROR = "create-lobby-error",
JOIN_LOBBY_ERROR = "join-lobby-error",
}
export enum ErrorCode {
PLAYER_NOT_FOUND = "player-not-found",
GAME_NOT_FOUND = "game-not-found",
}

View File

@ -8,6 +8,7 @@ const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
const socket = io("http://localhost:3010");
socket.on("exception", console.log);
root.render(
<React.StrictMode>
<App socket={socket} />