From 80603c9a4214012c7092cf7869c0e96eaa507da8 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:30:06 +0000 Subject: [PATCH] port provider --- .../infrastructure/network/PortProvider.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 server/src/service/infrastructure/network/PortProvider.ts diff --git a/server/src/service/infrastructure/network/PortProvider.ts b/server/src/service/infrastructure/network/PortProvider.ts new file mode 100644 index 000000000..bacbd19ff --- /dev/null +++ b/server/src/service/infrastructure/network/PortProvider.ts @@ -0,0 +1,98 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { PortStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { AppError } from "@/utils/AppError.js"; +import { NETWORK_ERROR, timeRequest } from "@/service/infrastructure/network/utils.js"; +import * as net from "net"; +type NetType = typeof net; + +const SERVICE_NAME = "PortProvider"; + +export class PortProvider implements IStatusProvider { + readonly type = "port"; + + constructor(private net: NetType) {} + + supports(type: MonitorType): boolean { + return type === "port"; + } + + async handle(monitor: Monitor): Promise> { + try { + const { url, port } = monitor; + if (!url || !port) { + throw new Error("URL and port are required for port monitoring"); + } + + const { responseTime, error } = await timeRequest(async () => { + return new Promise((resolve, reject) => { + const socket = this.net.createConnection( + { + host: url, + port, + }, + () => { + socket.end(); + socket.destroy(); + resolve({ success: true }); + } + ); + + socket.setTimeout(5000); + socket.on("timeout", () => { + socket.destroy(); + reject(new Error("Connection timeout")); + }); + + socket.on("error", (err: unknown) => { + socket.destroy(); + reject(err); + }); + }); + }); + if (error) { + const errorMessage = error instanceof Error ? error.message : "Port check failed"; + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: NETWORK_ERROR, + message: errorMessage, + responseTime: responseTime, + timings: undefined, + payload: { success: false }, + jsonPath: monitor.jsonPath, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + extracted: undefined, + }; + } + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: true, + code: 200, + message: "Port check successful", + responseTime: responseTime, + timings: undefined, + payload: { success: true }, + jsonPath: monitor.jsonPath, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + extracted: undefined, + }; + } catch (err: unknown) { + const originalMessage = err instanceof Error ? err.message : String(err); + throw new AppError({ + message: originalMessage || "Error performing port check", + status: 500, + service: SERVICE_NAME, + method: "requestPort", + details: { url: monitor.url, port: monitor.port }, + }); + } + } +}