From 190d9dd2b1ae829db40391e2fd5816c9293cb74b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:45:27 +0000 Subject: [PATCH] gameProvider --- .../infrastructure/network/GameProvider.ts | 78 +++++++++++++++++++ .../infrastructure/network/PortProvider.ts | 1 + server/src/types/network.ts | 4 +- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 server/src/service/infrastructure/network/GameProvider.ts diff --git a/server/src/service/infrastructure/network/GameProvider.ts b/server/src/service/infrastructure/network/GameProvider.ts new file mode 100644 index 000000000..e2ace7433 --- /dev/null +++ b/server/src/service/infrastructure/network/GameProvider.ts @@ -0,0 +1,78 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { GameStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { GameDig } from "gamedig"; +import { AppError } from "@/utils/AppError.js"; +import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js"; +import { ILogger } from "@/utils/logger.js"; + +type GameDigType = typeof GameDig; + +const SERVICE_NAME = "GameProvider"; +export class GameProvider implements IStatusProvider { + readonly type = "game"; + constructor( + private logger: ILogger, + private gameDig: GameDigType + ) {} + + supports(type: MonitorType): boolean { + return type === "game"; + } + + async handle(monitor: Monitor): Promise> { + const { url, port, gameId, id, teamId, type } = monitor; + + // Clean the host in case a full URL was passed + const host = url?.replace(/^https?:\/\//, "").split(/[/?#:]/)[0] || ""; + try { + const state = await this.gameDig + .query({ + type: gameId ?? "unknown", + host: host, + port: port ?? 0, + }) + .catch((error: unknown) => { + this.logger.warn({ + message: error instanceof Error ? error.message : String(error), + service: SERVICE_NAME, + method: "handle", + details: { host, port, gameId }, + }); + return undefined; + }); + + if (!state) { + return { + monitorId: id, + teamId: teamId, + type: type, + status: false, + code: NETWORK_ERROR, + message: "No response from game server", + responseTime: 0, + payload: null, + }; + } + + return { + monitorId: id, + teamId: teamId, + type: type, + status: true, + code: 200, + message: "Success", + responseTime: state.ping ?? 0, + payload: state, + }; + } catch (err: unknown) { + const originalMessage = err instanceof Error ? err.message : String(err); + throw new AppError({ + message: originalMessage || "Error performing game server check", + service: SERVICE_NAME, + method: "handle", + details: { url: monitor.url, port: monitor.port, gameId: monitor.gameId }, + }); + } + } +} diff --git a/server/src/service/infrastructure/network/PortProvider.ts b/server/src/service/infrastructure/network/PortProvider.ts index 7c1c96ce4..66fefd0fe 100644 --- a/server/src/service/infrastructure/network/PortProvider.ts +++ b/server/src/service/infrastructure/network/PortProvider.ts @@ -51,6 +51,7 @@ export class PortProvider implements IStatusProvider { }); }); if (error) { + console.log(error); const errorMessage = error instanceof Error ? error.message : "Port check failed"; return { monitorId: monitor.id, diff --git a/server/src/types/network.ts b/server/src/types/network.ts index 083193e06..93ddc4ac9 100644 --- a/server/src/types/network.ts +++ b/server/src/types/network.ts @@ -14,6 +14,8 @@ import type { MonitorStatus, } from "@/types/index.js"; +import type { QueryResult } from "gamedig"; + export interface MonitorStatusResponse< T = | HttpStatusPayload @@ -100,7 +102,7 @@ export interface PortStatusPayload { success: boolean; } -export type GameStatusPayload = Record; +export type GameStatusPayload = QueryResult; export interface GrpcStatusPayload { grpcStatusCode: number;