From b1abe8763310320b61860f2d382dbeaf1af5ebb0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 13:46:51 +0800 Subject: [PATCH 01/11] Update HardwareCheck model to include all standard fields --- Server/db/models/HardwareCheck.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) 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: () => ({}), From 7fd63f8004575118ebee471e18f88527eb8c4ce2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 13:47:30 +0800 Subject: [PATCH 02/11] Add option secret to Monitor for monitors that require authentication --- Server/db/models/Monitor.js | 3 +++ 1 file changed, 3 insertions(+) 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, From 0c321cf484455fec664b23da0682bbe03816e414 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 13:51:00 +0800 Subject: [PATCH 03/11] Add optional authorization config to HTTP request. Delegate handleHardware to handleHttp as they are identical --- Server/service/jobQueue.js | 1 - Server/service/networkService.js | 20 ++++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) 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..b4efab0f4 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, }; @@ -121,6 +126,7 @@ class NetworkService { httpResponse.code = code; httpResponse.status = false; httpResponse.message = this.http.STATUS_CODES[code] || "Network Error"; + return httpResponse; } httpResponse.status = true; @@ -155,7 +161,7 @@ class NetworkService { const pagespeedResponse = { monitorId: job.data._id, - type: "pagespeed", + type: job.data.type, responseTime, payload: response?.data, }; @@ -174,7 +180,9 @@ class NetworkService { return pagespeedResponse; } - 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 +202,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}`, From 10a438a67729a3dd2cc1dfc4564e2ee19aaaf090 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 13:51:31 +0800 Subject: [PATCH 04/11] Implement handling of hardwareChecks --- Server/service/statusService.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Server/service/statusService.js b/Server/service/statusService.js index c8089db98..694b0ed72 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, }); } From a8060a72ab937e1748e185bcb65381485eaa5be0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 13:51:50 +0800 Subject: [PATCH 05/11] Add validation for secret to Create and Edit Monitor validation --- Server/validation/joi.js | 2 ++ 1 file changed, 2 insertions(+) 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({ From e940a0d1519f3974e0f5b72a293e3f5551a34041 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 13:51:57 +0800 Subject: [PATCH 06/11] update tests --- Server/tests/services/networkService.test.js | 71 +++++++++++++++++--- Server/tests/services/statusService.test.js | 20 ++++++ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/Server/tests/services/networkService.test.js b/Server/tests/services/networkService.test.js index dbc2986ee..f27f0ef79 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 jas 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 () => { From b417b72450bbc434600ce5eb4ed2ff46a90b9452 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 14:47:54 +0800 Subject: [PATCH 07/11] Refactor requestPagespeed to simply call requestHttp with an updated job object --- Server/service/networkService.js | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index b4efab0f4..fa665e1ea 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -126,7 +126,6 @@ class NetworkService { httpResponse.code = code; httpResponse.status = false; httpResponse.message = this.http.STATUS_CODES[code] || "Network Error"; - return httpResponse; } httpResponse.status = true; @@ -153,31 +152,10 @@ 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: job.data.type, - 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 requestHardware(job) { From 29c83f5206ae691a7e2a4f091576dd637b0536c4 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 29 Oct 2024 14:48:19 +0800 Subject: [PATCH 08/11] Safely access payload values --- Server/service/statusService.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/service/statusService.js b/Server/service/statusService.js index 694b0ed72..64705e122 100644 --- a/Server/service/statusService.js +++ b/Server/service/statusService.js @@ -101,10 +101,10 @@ class StatusService { } if (type === "hardware") { - check.cpu = payload.cpu; - check.memory = payload.memory; - check.disk = payload.disk; - check.host = payload.host; + check.cpu = payload?.cpu ?? {}; + check.memory = payload?.memory ?? {}; + check.disk = payload?.disk ?? {}; + check.host = payload?.host ?? {}; } return check; }; From 92fc6ac4637f0c6f8a71f415eec3deba51c517ab Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 29 Oct 2024 22:24:07 +0000 Subject: [PATCH 09/11] chore(deps): update dependency mocha to v10.8.1 --- Server/package-lock.json | 8 ++++---- Server/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Server/package-lock.json b/Server/package-lock.json index 128fc6c5b..fefc83f9d 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -35,7 +35,7 @@ "c8": "10.1.2", "chai": "5.1.2", "esm": "3.2.25", - "mocha": "10.7.3", + "mocha": "10.8.1", "nodemon": "3.1.7", "prettier": "^3.3.3", "sinon": "19.0.2" @@ -4129,9 +4129,9 @@ } }, "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.1.tgz", + "integrity": "sha512-WxSpEWgF03HfgNKBuysfK40DUaOSVX5zxgLDoieMGO+zyE69iq2eQ1vBypvIJ5mOPKpuVAqWiTbt4Orj7L6wVw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/Server/package.json b/Server/package.json index d0aa1d0d1..512a1e01a 100644 --- a/Server/package.json +++ b/Server/package.json @@ -38,7 +38,7 @@ "c8": "10.1.2", "chai": "5.1.2", "esm": "3.2.25", - "mocha": "10.7.3", + "mocha": "10.8.1", "nodemon": "3.1.7", "prettier": "^3.3.3", "sinon": "19.0.2" From d4458e6cf17d777c6d682fab5743974ae3d11b5d Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 30 Oct 2024 01:06:12 +0000 Subject: [PATCH 10/11] fix(deps): update dependency recharts to v2.13.1 --- Client/package-lock.json | 8 ++++---- Client/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Client/package-lock.json b/Client/package-lock.json index cd1760e83..8d816f4dc 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -29,7 +29,7 @@ "react-router": "^6.23.0", "react-router-dom": "^6.23.1", "react-toastify": "^10.0.5", - "recharts": "2.13.0", + "recharts": "2.13.1", "redux-persist": "6.0.0", "vite-plugin-svgr": "^4.2.0" }, @@ -5518,9 +5518,9 @@ } }, "node_modules/recharts": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.0.tgz", - "integrity": "sha512-sbfxjWQ+oLWSZEWmvbq/DFVdeRLqqA6d0CDjKx2PkxVVdoXo16jvENCE+u/x7HxOO+/fwx//nYRwb8p8X6s/lQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.1.tgz", + "integrity": "sha512-87LdsmgK/MHLmWQfTC6yDysno2cOigi/+2KRCwy0D8NDu1IOdtTGS8lMovA0VIvJ7kf3zdp1IiwznHZWSPJhYw==", "license": "MIT", "dependencies": { "clsx": "^2.0.0", diff --git a/Client/package.json b/Client/package.json index 7e649e1f7..8fe31f8da 100644 --- a/Client/package.json +++ b/Client/package.json @@ -32,7 +32,7 @@ "react-router": "^6.23.0", "react-router-dom": "^6.23.1", "react-toastify": "^10.0.5", - "recharts": "2.13.0", + "recharts": "2.13.1", "redux-persist": "6.0.0", "vite-plugin-svgr": "^4.2.0" }, From b1d794771985f0e43a3763c8e8bacfead0e9723d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 30 Oct 2024 09:07:47 +0800 Subject: [PATCH 11/11] fix typo jas -> has --- Server/tests/services/networkService.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/tests/services/networkService.test.js b/Server/tests/services/networkService.test.js index f27f0ef79..4e48524e2 100644 --- a/Server/tests/services/networkService.test.js +++ b/Server/tests/services/networkService.test.js @@ -158,7 +158,7 @@ describe("Network Service", () => { expect(httpResult.responseTime).to.be.a("number"); expect(httpResult.status).to.be.true; }); - it("should return a response object if hardware successful and job jas a secret", async () => { + it("should return a response object if hardware successful and job has a secret", async () => { const job = { data: { url: "http://test.com",