diff --git a/Server/db/models/Monitor.js b/Server/db/models/Monitor.js index d44566a97..904c71b9f 100644 --- a/Server/db/models/Monitor.js +++ b/Server/db/models/Monitor.js @@ -28,12 +28,15 @@ const MonitorSchema = mongoose.Schema( type: { type: String, required: true, - enum: ["http", "ping", "pagespeed", "hardware", "docker"], + enum: ["http", "ping", "pagespeed", "hardware", "docker", "port"], }, url: { type: String, required: true, }, + port: { + type: Number, + }, isActive: { type: Boolean, default: true, diff --git a/Server/index.js b/Server/index.js index 8f7d845ac..f53872a2c 100644 --- a/Server/index.js +++ b/Server/index.js @@ -44,6 +44,7 @@ import axios from "axios"; import ping from "ping"; import http from "http"; import Docker from "dockerode"; +import net from "net"; // Email service and dependencies import EmailService from "./service/emailService.js"; @@ -129,7 +130,7 @@ const startApp = async () => { nodemailer, logger ); - const networkService = new NetworkService(axios, ping, logger, http, Docker); + const networkService = new NetworkService(axios, ping, logger, http, Docker, net); const statusService = new StatusService(db, logger); const notificationService = new NotificationService(emailService, db, logger); const jobQueue = await JobQueue.createJobQueue( diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 0de66aa00..03bd6fef9 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -8,15 +8,17 @@ const SERVICE_NAME = "NetworkService"; * @param {Object} ping - The ping utility for network checks. * @param {Object} logger - The logger instance for logging. * @param {Object} http - The HTTP utility for network operations. + * @param {Object} net - The net utility for network operations. */ class NetworkService { static SERVICE_NAME = SERVICE_NAME; - constructor(axios, ping, logger, http, Docker) { + constructor(axios, ping, logger, http, Docker, net) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; this.TYPE_PAGESPEED = "pagespeed"; this.TYPE_HARDWARE = "hardware"; this.TYPE_DOCKER = "docker"; + this.TYPE_PORT = "port"; this.SERVICE_NAME = SERVICE_NAME; this.NETWORK_ERROR = 5000; this.PING_ERROR = 5001; @@ -25,6 +27,7 @@ class NetworkService { this.logger = logger; this.http = http; this.Docker = Docker; + this.net = net; } /** @@ -265,6 +268,60 @@ class NetworkService { } } + async requestPort(job) { + try { + const { url, port } = job.data; + const { response, responseTime, error } = await this.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) => { + socket.destroy(); + reject(err); + }); + }); + }); + + const portResponse = { + monitorId: job.data._id, + type: job.data.type, + responseTime, + }; + + if (error) { + portResponse.status = false; + portResponse.code = this.NETWORK_ERROR; + portResponse.message = errorMessages.PORT_FAIL; + return portResponse; + } + + portResponse.status = response.success; + portResponse.code = 200; + portResponse.message = successMessages.PORT_SUCCESS; + return portResponse; + } catch (error) { + error.service = this.SERVICE_NAME; + error.method = "requestTCP"; + throw error; + } + } + /** * Handles unsupported job types by throwing an error with details. * @@ -300,6 +357,9 @@ class NetworkService { return await this.requestHardware(job); case this.TYPE_DOCKER: return await this.requestDocker(job); + case this.TYPE_PORT: + return await this.requestPort(job); + default: return this.handleUnsupportedType(type); } diff --git a/Server/service/statusService.js b/Server/service/statusService.js index 96013ae7f..5138b73ce 100644 --- a/Server/service/statusService.js +++ b/Server/service/statusService.js @@ -143,6 +143,7 @@ class StatusService { pagespeed: this.db.createPageSpeedCheck, hardware: this.db.createHardwareCheck, docker: this.db.createCheck, + port: this.db.createCheck, }; const operation = operationMap[networkResponse.type]; diff --git a/Server/utils/messages.js b/Server/utils/messages.js index 2329754c9..f2b29ccb8 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -59,6 +59,9 @@ const errorMessages = { // Docker DOCKER_FAIL: "Failed to fetch Docker container information", DOCKER_NOT_FOUND: "Docker container not found", + + // Port + PORT_FAIL: "Failed to connect to port", }; const successMessages = { @@ -129,6 +132,9 @@ const successMessages = { // Docker DOCKER_SUCCESS: "Docker container status fetched successfully", + + // Port + PORT_SUCCESS: "Port connected successfully", }; export { errorMessages, successMessages }; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index e22aac1a6..86e1fa7a6 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -199,6 +199,7 @@ const createMonitorBodyValidation = joi.object({ description: joi.string().required(), type: joi.string().required(), url: joi.string().required(), + port: joi.number(), isActive: joi.boolean(), interval: joi.number(), thresholds: joi.object().keys({