diff --git a/server/src/service/infrastructure/globalPingService.ts b/server/src/service/infrastructure/globalPingService.ts index 6050cc5eb..e261c7b19 100644 --- a/server/src/service/infrastructure/globalPingService.ts +++ b/server/src/service/infrastructure/globalPingService.ts @@ -1,5 +1,5 @@ import type { GeoContinent, GeoCheckResult, GeoCheckTimings, GeoCheckLocation } from "@/types/geoCheck.js"; -import { supportsGeoCheck } from "@/types/monitor.js"; +import { supportsGeoCheck, type HttpStatusCode } from "@/types/monitor.js"; import { MonitorType } from "@/types/index.js"; import type { ILogger } from "@/utils/logger.js"; import got from "got"; @@ -62,7 +62,7 @@ interface GlobalPingProbeResult { export interface IGlobalPingService { readonly serviceName: string; createMeasurement(monitorType: MonitorType, url: string, locations: GeoContinent[]): Promise; - pollForResults(measurementId: string, timeoutMs?: number, customUpCodes?: number[]): Promise; + pollForResults(measurementId: string, timeoutMs?: number, customUpCodes?: HttpStatusCode[]): Promise; } export class GlobalPingService implements IGlobalPingService { @@ -119,7 +119,11 @@ export class GlobalPingService implements IGlobalPingService { } } - async pollForResults(measurementId: string, timeoutMs: number = MAX_POLL_TIMEOUT_MS, customUpCodes: number[] = []): Promise { + async pollForResults( + measurementId: string, + timeoutMs: number = MAX_POLL_TIMEOUT_MS, + customUpCodes: HttpStatusCode[] = [] + ): Promise { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { @@ -174,7 +178,7 @@ export class GlobalPingService implements IGlobalPingService { return []; } - private transformResults(probeResults: GlobalPingProbeResult[], customUpCodes: number[] = []): GeoCheckResult[] { + private transformResults(probeResults: GlobalPingProbeResult[], customUpCodes: HttpStatusCode[] = []): GeoCheckResult[] { const successfulResults: GeoCheckResult[] = []; for (const probeResult of probeResults) { diff --git a/server/src/service/infrastructure/network/HttpProvider.ts b/server/src/service/infrastructure/network/HttpProvider.ts index 2ae177819..971c4e6c2 100644 --- a/server/src/service/infrastructure/network/HttpProvider.ts +++ b/server/src/service/infrastructure/network/HttpProvider.ts @@ -31,14 +31,15 @@ export class HttpProvider implements IStatusProvider { private handleHttpError(error: unknown, monitor: Monitor): MonitorStatusResponse { if (error instanceof HTTPError || error instanceof RequestError) { - const isCustomUp = error instanceof HTTPError && (monitor.customUpCodes ?? []).includes(error.response?.statusCode); + const statusCode = error.response?.statusCode; + const statusUp = statusCode !== undefined && monitor.customUpCodes.includes(statusCode); return { monitorId: monitor.id, teamId: monitor.teamId, type: monitor.type, - status: isCustomUp, - code: error.response?.statusCode ?? NETWORK_ERROR, + status: statusUp, + code: statusCode ?? NETWORK_ERROR, message: error.message, responseTime: error.timings?.phases?.total ?? 0, timings: error.timings, @@ -104,11 +105,12 @@ export class HttpProvider implements IStatusProvider { } const matchResult = this.advancedMatcher.validate(payload, monitor); + const statusUp = monitor.customUpCodes.includes(response.statusCode) || response.ok; return { monitorId: monitor.id, teamId: monitor.teamId, type: monitor.type, - status: ((monitor.customUpCodes ?? []).includes(response.statusCode) || response.ok) && matchResult.ok, + status: statusUp && matchResult.ok, code: response.statusCode, message: matchResult.ok ? (response.statusMessage ?? "OK") : matchResult.message, responseTime: response.timings.phases.total ?? 0, diff --git a/server/src/types/monitor.ts b/server/src/types/monitor.ts index 415b7dff0..3c543616c 100644 --- a/server/src/types/monitor.ts +++ b/server/src/types/monitor.ts @@ -2,6 +2,11 @@ import type { CheckSnapshot } from "@/types/check.js"; export type { CheckSnapshot } from "@/types/check.js"; import type { GeoContinent, GroupedGeoCheck } from "@/types/geoCheck.js"; export type { GeoContinent } from "@/types/geoCheck.js"; +import http from "node:http"; + +export const HttpStatusCodes = Object.keys(http.STATUS_CODES).map(Number); +export const HttpStatusCodeSet = new Set(HttpStatusCodes); +export type HttpStatusCode = number; export const MonitorTypes = ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc", "websocket", "dns", "unknown"] as const; export type MonitorType = (typeof MonitorTypes)[number]; @@ -60,7 +65,7 @@ export interface Monitor { uptimePercentage?: number; notifications: string[]; tags: string[]; - customUpCodes: number[]; + customUpCodes: HttpStatusCode[]; secret?: string; cpuAlertThreshold: number; cpuAlertCounter: number; diff --git a/server/src/validation/monitorValidation.ts b/server/src/validation/monitorValidation.ts index 22a1a241e..2d70830b7 100644 --- a/server/src/validation/monitorValidation.ts +++ b/server/src/validation/monitorValidation.ts @@ -1,10 +1,9 @@ import { z } from "zod"; import { booleanCoercion, dnsHostnameRegex, dnsServerValidation } from "./shared.js"; import { GeoContinents } from "@/types/geoCheck.js"; -import { DnsRecordTypes, MonitorMatchMethods, MonitorStatuses, MonitorTypes, PageSpeedStrategies } from "@/types/monitor.js"; -import http from "node:http"; +import { DnsRecordTypes, HttpStatusCodeSet, MonitorMatchMethods, MonitorStatuses, MonitorTypes, PageSpeedStrategies } from "@/types/monitor.js"; -const httpStatusCode = z.number().refine((code) => code.toString() in http.STATUS_CODES, { message: "Must be a valid HTTP status code" }); +const httpStatusCode = z.number().refine((code) => HttpStatusCodeSet.has(code), { message: "Must be a valid HTTP status code" }); export const getMonitorByIdParamValidation = z.object({ monitorId: z.string().min(1, "Monitor ID is required"), diff --git a/server/test/unit/providers/network/httpProvider.test.ts b/server/test/unit/providers/network/httpProvider.test.ts index afd3710d9..9252493e0 100644 --- a/server/test/unit/providers/network/httpProvider.test.ts +++ b/server/test/unit/providers/network/httpProvider.test.ts @@ -56,6 +56,7 @@ const makeMonitor = (overrides?: Partial): Monitor => useAdvancedMatching: false, matchMethod: undefined, expectedValue: undefined, + customUpCodes: [], ...overrides, }) as Monitor;