diff --git a/Server/db/models/HardwareCheck.js b/Server/db/models/HardwareCheck.js index 5e7f53329..c307b9804 100644 --- a/Server/db/models/HardwareCheck.js +++ b/Server/db/models/HardwareCheck.js @@ -36,11 +36,31 @@ const HardwareCheckSchema = mongoose.Schema( type: mongoose.Schema.Types.ObjectId, ref: "Monitor", immutable: true, + index: true, }, + status: { type: Boolean, index: true, }, + + responseTime: { + type: Number, + }, + + statusCode: { + type: Number, + index: true, + }, + + message: { + type: String, + }, + expiry: { + type: Date, + default: Date.now, + expires: 60 * 60 * 24 * 30, // 30 days + }, cpu: { type: cpuSchema, default: () => ({}), diff --git a/Server/db/models/Monitor.js b/Server/db/models/Monitor.js index c4dcb649c..111c48d03 100644 --- a/Server/db/models/Monitor.js +++ b/Server/db/models/Monitor.js @@ -61,6 +61,9 @@ const MonitorSchema = mongoose.Schema( ref: "Notification", }, ], + secret: { + type: String, + }, }, { timestamps: true, diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index fcc9cc2b1..6d46052f1 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -163,7 +163,6 @@ class JobQueue { // Get the current status const networkResponse = await this.networkService.getStatus(job); // Handle status change - const { monitor, statusChanged, prevStatus } = await this.statusService.updateStatus(networkResponse); diff --git a/Server/service/networkService.js b/Server/service/networkService.js index efdd2e865..fa665e1ea 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -1,5 +1,4 @@ import { errorMessages, successMessages } from "../utils/messages.js"; - /** * Constructs a new NetworkService instance. * @@ -94,6 +93,7 @@ class NetworkService { * @param {Object} job.data - The data object within the job. * @param {string} job.data.url - The URL to send the HTTP GET request to. * @param {string} job.data._id - The monitor ID for the HTTP request. + * @param {string} [job.data.secret] - Secret for authorization if provided. * @returns {Promise} An object containing the HTTP response details. * @property {string} monitorId - The monitor ID for the HTTP request. * @property {string} type - The type of request, which is "http". @@ -105,13 +105,18 @@ class NetworkService { */ async requestHttp(job) { const url = job.data.url; + const config = {}; + + job.data.secret !== undefined && + (config.headers = { Authorization: `Bearer ${job.data.secret}` }); + const { response, responseTime, error } = await this.timeRequest(() => - this.axios.get(url) + this.axios.get(url, config) ); const httpResponse = { monitorId: job.data._id, - type: "http", + type: job.data.type, responseTime, payload: response?.data, }; @@ -147,34 +152,15 @@ class NetworkService { */ async requestPagespeed(job) { const url = job.data.url; - const { response, responseTime, error } = await this.timeRequest(() => - this.axios.get( - `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance` - ) - ); - - const pagespeedResponse = { - monitorId: job.data._id, - type: "pagespeed", - responseTime, - payload: response?.data, - }; - - if (error) { - const code = error.response?.status || this.NETWORK_ERROR; - pagespeedResponse.code = code; - pagespeedResponse.status = false; - pagespeedResponse.message = this.http.STATUS_CODES[code] || "Network Error"; - return pagespeedResponse; - } - - pagespeedResponse.status = true; - pagespeedResponse.code = response.status; - pagespeedResponse.message = this.http.STATUS_CODES[response.status]; - return pagespeedResponse; + const updatedJob = { ...job }; + const pagespeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`; + updatedJob.data.url = pagespeedUrl; + return this.requestHttp(updatedJob); } - async requestHandleHardware(job) {} + async requestHardware(job) { + return this.requestHttp(job); + } /** * Gets the status of a job based on its type and returns the appropriate response. @@ -194,7 +180,7 @@ class NetworkService { case this.TYPE_PAGESPEED: return await this.requestPagespeed(job); case this.TYPE_HARDWARE: - return await this.requestHandleHardware(job); + return await this.requestHardware(job); default: this.logger.error({ message: `Unsupported type: ${job.data.type}`, diff --git a/Server/service/statusService.js b/Server/service/statusService.js index c8089db98..64705e122 100644 --- a/Server/service/statusService.js +++ b/Server/service/statusService.js @@ -99,6 +99,13 @@ class StatusService { check.performance = (categories.performance?.score || 0) * 100; check.audits = { cls, si, fcp, lcp, tbt }; } + + if (type === "hardware") { + check.cpu = payload?.cpu ?? {}; + check.memory = payload?.memory ?? {}; + check.disk = payload?.disk ?? {}; + check.host = payload?.host ?? {}; + } return check; }; @@ -121,6 +128,7 @@ class StatusService { http: this.db.createCheck, ping: this.db.createCheck, pagespeed: this.db.createPageSpeedCheck, + hardware: this.db.createHardwareCheck, }; const operation = operationMap[networkResponse.type]; const check = this.buildCheck(networkResponse); @@ -130,7 +138,7 @@ class StatusService { message: error.message, service: this.SERVICE_NAME, method: "insertCheck", - details: `Error inserting check for monitor: ${networkResponse.monitorId}`, + details: `Error inserting check for monitor: ${networkResponse?.monitorId}`, stack: error.stack, }); } diff --git a/Server/tests/services/networkService.test.js b/Server/tests/services/networkService.test.js index dbc2986ee..4e48524e2 100644 --- a/Server/tests/services/networkService.test.js +++ b/Server/tests/services/networkService.test.js @@ -2,7 +2,6 @@ import sinon from "sinon"; import NetworkService from "../../service/networkService.js"; import { expect } from "chai"; import http from "http"; -import exp from "constants"; describe("Network Service", () => { let axios, ping, logger, networkService; @@ -74,7 +73,7 @@ describe("Network Service", () => { }); describe("requestHttp", () => { it("should return a response object if http successful", async () => { - const job = { data: { url: "http://test.com", _id: "123" } }; + const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); expect(httpResult.type).to.equal("http"); @@ -87,7 +86,7 @@ describe("Network Service", () => { networkService.timeRequest = sinon .stub() .resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123" } }; + const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); expect(httpResult.type).to.equal("http"); @@ -101,7 +100,7 @@ describe("Network Service", () => { networkService.timeRequest = sinon .stub() .resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123" } }; + const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); expect(httpResult.type).to.equal("http"); @@ -113,7 +112,7 @@ describe("Network Service", () => { describe("requestPagespeed", () => { it("should return a response object if pagespeed successful", async () => { - const job = { data: { url: "http://test.com", _id: "123" } }; + const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); expect(pagespeedResult.type).to.equal("pagespeed"); @@ -126,7 +125,7 @@ describe("Network Service", () => { networkService.timeRequest = sinon .stub() .resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123" } }; + const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); expect(pagespeedResult.type).to.equal("pagespeed"); @@ -140,7 +139,7 @@ describe("Network Service", () => { networkService.timeRequest = sinon .stub() .resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123" } }; + const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); expect(pagespeedResult.type).to.equal("pagespeed"); @@ -150,6 +149,60 @@ describe("Network Service", () => { }); }); + describe("requestHardware", () => { + it("should return a response object if hardware successful", async () => { + const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; + const httpResult = await networkService.requestHardware(job); + expect(httpResult.monitorId).to.equal("123"); + expect(httpResult.type).to.equal("hardware"); + expect(httpResult.responseTime).to.be.a("number"); + expect(httpResult.status).to.be.true; + }); + it("should return a response object if hardware successful and job has a secret", async () => { + const job = { + data: { + url: "http://test.com", + _id: "123", + type: "hardware", + secret: "my_secret", + }, + }; + const httpResult = await networkService.requestHardware(job); + expect(httpResult.monitorId).to.equal("123"); + expect(httpResult.type).to.equal("hardware"); + expect(httpResult.responseTime).to.be.a("number"); + expect(httpResult.status).to.be.true; + }); + it("should return a response object if hardware unsuccessful", async () => { + const error = new Error("Test error"); + error.response = { status: 404 }; + networkService.timeRequest = sinon + .stub() + .resolves({ response: null, responseTime: 1, error }); + const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; + const httpResult = await networkService.requestHardware(job); + expect(httpResult.monitorId).to.equal("123"); + expect(httpResult.type).to.equal("hardware"); + expect(httpResult.responseTime).to.be.a("number"); + expect(httpResult.status).to.be.false; + expect(httpResult.code).to.equal(404); + }); + it("should return a response object if hardware unsuccessful with unknown code", async () => { + const error = new Error("Test error"); + error.response = {}; + networkService.timeRequest = sinon + .stub() + .resolves({ response: null, responseTime: 1, error }); + const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; + const httpResult = await networkService.requestHardware(job); + expect(httpResult.monitorId).to.equal("123"); + expect(httpResult.type).to.equal("hardware"); + expect(httpResult.responseTime).to.be.a("number"); + expect(httpResult.status).to.be.false; + expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); + }); + }); + describe("getStatus", () => { beforeEach(() => { networkService.requestPing = sinon.stub(); @@ -179,10 +232,10 @@ describe("Network Service", () => { expect(networkService.requestHttp.notCalled).to.be.true; expect(networkService.requestPagespeed.calledOnce).to.be.true; }); - it("should call requestHandleHardware if type is hardware", () => { + it("should call requestHardware if type is hardware", () => { networkService.getStatus({ data: { type: "hardware" } }); + expect(networkService.requestHardware.calledOnce).to.be.true; expect(networkService.requestPing.notCalled).to.be.true; - expect(networkService.requestHttp.notCalled).to.be.true; expect(networkService.requestPagespeed.notCalled).to.be.true; }); it("should log an error if an unknown type is provided", () => { diff --git a/Server/tests/services/statusService.test.js b/Server/tests/services/statusService.test.js index df74ef715..19b0edf19 100644 --- a/Server/tests/services/statusService.test.js +++ b/Server/tests/services/statusService.test.js @@ -173,6 +173,26 @@ describe("StatusService", () => { tbt: 0, }); }); + it("should build a check for hardware type", () => { + const check = statusService.buildCheck({ + monitorId: "test", + type: "hardware", + status: true, + responseTime: 100, + code: 200, + message: "Test message", + payload: { cpu: "cpu", memory: "memory", disk: "disk", host: "host" }, + }); + expect(check.monitorId).to.equal("test"); + expect(check.status).to.be.true; + expect(check.statusCode).to.equal(200); + expect(check.responseTime).to.equal(100); + expect(check.message).to.equal("Test message"); + expect(check.cpu).to.equal("cpu"); + expect(check.memory).to.equal("memory"); + expect(check.disk).to.equal("disk"); + expect(check.host).to.equal("host"); + }); }); describe("insertCheck", () => { it("should log an error if one is thrown", async () => { diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 0b85a4557..cd69a959d 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -228,6 +228,7 @@ const createMonitorBodyValidation = joi.object({ usage_disk: joi.number(), }), notifications: joi.array().items(joi.object()), + secret: joi.string(), }); const editMonitorBodyValidation = joi.object({ @@ -235,6 +236,7 @@ const editMonitorBodyValidation = joi.object({ description: joi.string(), interval: joi.number(), notifications: joi.array().items(joi.object()), + secret: joi.string(), }); const pauseMonitorParamValidation = joi.object({