From 628f3a3d12727da77c4ccd316719ba64ccd32a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sun, 11 Aug 2024 16:15:30 +0300 Subject: [PATCH 01/33] chore: create openapi.json with openapi 3.1.0 format > Objects >> info >> servers --- Server/openapi.json | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Server/openapi.json diff --git a/Server/openapi.json b/Server/openapi.json new file mode 100644 index 000000000..635dfe0ad --- /dev/null +++ b/Server/openapi.json @@ -0,0 +1,35 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "BluWave Uptime", + "summary": "BlueWave Uptime OpenAPI Specifications", + "description": "BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.", + "contact": { + "name": "API Support", + "url": "mailto:hello@bluewavelabs.ca", + "email": "hello@bluewavelabs.ca" + }, + "license": { + "name": "AGPLv3", + "url": "https://github.com/bluewave-labs/bluewave-uptime/tree/HEAD/LICENSE" + }, + "version": "1.0" + }, + "servers": [ + { + "url": "https://bluewavelabs.ca", + "description": "Remote Development Server" + }, + { + "url": "http://localhost:{PORT}", + "description": "Local Development Server", + "variables": { + "PORT": { + "description": "The Data Set API is accessible via https and http", + "enum": ["5000", "3000", "8080"], + "default": "5000" + } + } + } + ] +} From 0d2366cb3fa05c9577a6e058bf134229955bc122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sun, 11 Aug 2024 18:52:19 +0300 Subject: [PATCH 02/33] chore: update contact email in openapi.json --- Server/openapi.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index 635dfe0ad..8e219b5ae 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -6,8 +6,8 @@ "description": "BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.", "contact": { "name": "API Support", - "url": "mailto:hello@bluewavelabs.ca", - "email": "hello@bluewavelabs.ca" + "url": "mailto:support@bluewavelabs.ca", + "email": "support@bluewavelabs.ca" }, "license": { "name": "AGPLv3", From adbbdd405c01854e0c47b5a63a91eda93f5b4d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sun, 11 Aug 2024 19:03:02 +0300 Subject: [PATCH 03/33] chore(openapi): dynamic API path variables on servers --- Server/openapi.json | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index 8e219b5ae..8d3fff916 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -17,17 +17,29 @@ }, "servers": [ { - "url": "https://bluewavelabs.ca", - "description": "Remote Development Server" + "url": "https://bluewavelabs.ca/{API_PATH}", + "description": "Remote Development Server", + "variables": { + "API_PATH": { + "description": "API Base Path", + "enum": ["api/v1", "api/v1.1", "api/v2"], + "default": "api/v1" + } + } }, { - "url": "http://localhost:{PORT}", + "url": "http://localhost:{PORT}/{API_PATH}", "description": "Local Development Server", "variables": { "PORT": { - "description": "The Data Set API is accessible via https and http", + "description": "API Port", "enum": ["5000", "3000", "8080"], "default": "5000" + }, + "API_PATH": { + "description": "API Base Path", + "enum": ["api/v1", "api/v1.1", "api/v2"], + "default": "api/v1" } } } From 1541fd20fc4934769692bf7b3e8311938f61da29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Mon, 12 Aug 2024 21:39:10 +0300 Subject: [PATCH 04/33] chore(openapi): define the endpoint, method and description on auth routes > Common Responses - 200 - 422 - 500 --- Server/openapi.json | 320 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 319 insertions(+), 1 deletion(-) diff --git a/Server/openapi.json b/Server/openapi.json index 8d3fff916..e66c38294 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -43,5 +43,323 @@ } } } - ] + ], + "tags": [ + { + "name": "auth", + "description": "Authentication" + } + ], + "paths": { + "/auth/register": { + "post": { + "tags": ["auth"], + "description": "Register a new user", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/login": { + "post": { + "tags": ["auth"], + "description": "Login with credentials", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/logout": { + "post": { + "tags": ["auth"], + "description": "Logout", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/user/:userId": { + "put": { + "tags": ["auth"], + "description": "Change user informations", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + }, + "delete": { + "tags": ["auth"], + "description": "Delete user", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/users/admin": { + "get": { + "tags": ["auth"], + "description": "Checks to see if an admin account exists", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/users": { + "get": { + "tags": ["auth"], + "description": "Get all users", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/invite": { + "post": { + "tags": ["auth"], + "description": "Invite people to application", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/invite/verify": { + "post": { + "tags": ["auth"], + "description": "Verify the invite", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/recovery/request": { + "post": { + "tags": ["auth"], + "description": "Request a recovery token", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/recovery/validate": { + "post": { + "tags": ["auth"], + "description": "Validate recovery token", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + }, + "/auth/recovery/reset": { + "post": { + "tags": ["auth"], + "description": "Password reset", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": {} + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": {} + } + } + } + } + } + } } From 16eb578a5563f2ab289e167fb8178cce2cfea9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Mon, 12 Aug 2024 21:40:15 +0300 Subject: [PATCH 05/33] fix(openapi): update typo in title from 'BluWave uptime' to 'BlueWave Uptime' --- Server/openapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/openapi.json b/Server/openapi.json index e66c38294..36f18a73f 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -1,7 +1,7 @@ { "openapi": "3.1.0", "info": { - "title": "BluWave Uptime", + "title": "BlueWave Uptime", "summary": "BlueWave Uptime OpenAPI Specifications", "description": "BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.", "contact": { From e28530cdc7bd2de9cf6689358d00e312626034f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Tue, 13 Aug 2024 00:14:29 +0300 Subject: [PATCH 06/33] chore(openapi): add ErrorResponse and SuccessResponse schema components to auth routes --- Server/openapi.json | 245 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 209 insertions(+), 36 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index 36f18a73f..b0b7deba6 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -59,19 +59,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -85,19 +97,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -111,19 +135,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -137,19 +173,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -161,19 +209,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -187,19 +247,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -213,19 +285,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -239,19 +323,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -265,19 +361,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -291,19 +399,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -317,19 +437,31 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } @@ -343,23 +475,64 @@ "200": { "description": "OK", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } } }, "422": { "description": "Unprocessable Content", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } }, "500": { "description": "Internal Server Error", "content": { - "application/json": {} + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } } } } } + }, + "components": { + "schemas": { + "ErrorResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "msg": { + "type": "string" + } + } + }, + "SuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object" + } + } + } + } } } From 137500bbd0d523e14243e8cd97d5ee120d05b98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sun, 18 Aug 2024 20:01:34 +0300 Subject: [PATCH 07/33] chore(openapi): add UserSuccessResponse ref to auth responses --- Server/openapi.json | 241 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 229 insertions(+), 12 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index b0b7deba6..cf4338be6 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -61,7 +61,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -99,7 +99,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -137,7 +137,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -175,7 +175,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -211,7 +211,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -249,7 +249,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -287,7 +287,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -325,7 +325,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -363,7 +363,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -401,7 +401,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -439,7 +439,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -477,7 +477,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UserSuccessResponse" } } } @@ -532,6 +532,223 @@ "type": "object" } } + }, + "UserSuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "$ref": "#/components/schemas/UserDataType" + } + } + }, + "MonitorSuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "$ref": "#/components/schemas/MonitorDataType" + } + } + }, + "CheckDataSuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "$ref": "#/components/schemas/CheckDataType" + } + } + }, + "AlertDataSuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "msg": { + "type": "string" + }, + "data": { + "type": "object", + "$ref": "#/components/schemas/AlertDataType" + } + } + }, + "UserDataType": { + "type": "object", + "required": [ + "firstname", + "lastname", + "email", + "profilePicUrl", + "isActive", + "isVerified", + "updated_at", + "created_at" + ], + "properties": { + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "profilePicUrl": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "isVerified": { + "type": "boolean" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + } + } + }, + "MonitorDataType": { + "type": "object", + "required": [ + "userId", + "name", + "description", + "url", + "isActive", + "interval", + "updatedAt", + "createdAt" + ], + "properties": { + "userId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "interval": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + } + }, + "CheckDataType": { + "type": "object", + "required": [ + "monitorId", + "status", + "responseTime", + "statusCode", + "message", + "updatedAt", + "createdAt" + ], + "properties": { + "monitorId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "responseTime": { + "type": "integer" + }, + "statusCode": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + } + }, + "AlertDataType": { + "type": "object", + "required": [ + "checkId", + "monitorId", + "userId", + "status", + "message", + "notifiedStatus", + "acknowledgedStatus", + "updatedAt", + "createdAt" + ], + "properties": { + "checkId": { + "type": "string" + }, + "monitorId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "message": { + "type": "string" + }, + "notifiedStatus": { + "type": "boolean" + }, + "acknowledgedStatus": { + "type": "boolean" + }, + "updatedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + } } } } From cc08d7f8d59602fc33c84ebeb7ba6cf868f99826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sun, 18 Aug 2024 20:20:28 +0300 Subject: [PATCH 08/33] fix(openapi): update property names to camelCase in UserDataType schema --- Server/openapi.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index cf4338be6..536992638 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -602,8 +602,8 @@ "profilePicUrl", "isActive", "isVerified", - "updated_at", - "created_at" + "updatedAt", + "createdAt" ], "properties": { "firstname": { @@ -625,10 +625,10 @@ "isVerified": { "type": "boolean" }, - "updated_at": { + "updatedAt": { "type": "string" }, - "created_at": { + "createdAt": { "type": "string" } } From 21ce46a29e4bdd2e0ac19e16b8691c2829e806bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Fri, 30 Aug 2024 21:09:16 +0300 Subject: [PATCH 09/33] feat(openapi): add request body to login and register routes --- Server/openapi.json | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/Server/openapi.json b/Server/openapi.json index 536992638..066bded08 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -55,6 +55,45 @@ "post": { "tags": ["auth"], "description": "Register a new user", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["firstName", "lastName", "email", "password", "role", "teamId"], + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "format": "password" + }, + "profileImage": { + "type": "file", + "format": "file" + }, + "role": { + "type": "array", + "enum": [["user"], ["admin"], ["superadmin"]], + "default": ["superadmin"] + }, + "teamId": { + "type": "string", + "format": "uuid" + } + } + } + } + } + }, "responses": { "200": { "description": "OK", @@ -93,6 +132,26 @@ "post": { "tags": ["auth"], "description": "Login with credentials", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email", "password"], + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "format": "password" + } + } + } + } + } + }, "responses": { "200": { "description": "OK", From bdb293c18159b152d35cbcdfd6ac8a167210d0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Fri, 30 Aug 2024 21:10:13 +0300 Subject: [PATCH 10/33] fix(openapi): remove logout route from auth --- Server/openapi.json | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index 066bded08..72397d139 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -186,44 +186,6 @@ } } }, - "/auth/logout": { - "post": { - "tags": ["auth"], - "description": "Logout", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" - } - } - } - }, - "422": { - "description": "Unprocessable Content", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, "/auth/user/:userId": { "put": { "tags": ["auth"], From 649cf6835654fbdb3bc3f84b535e8679b81a064f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Fri, 30 Aug 2024 21:12:08 +0300 Subject: [PATCH 11/33] chore(openapi): remove remote development server --- Server/openapi.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Server/openapi.json b/Server/openapi.json index 72397d139..5db6250c5 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -16,17 +16,6 @@ "version": "1.0" }, "servers": [ - { - "url": "https://bluewavelabs.ca/{API_PATH}", - "description": "Remote Development Server", - "variables": { - "API_PATH": { - "description": "API Base Path", - "enum": ["api/v1", "api/v1.1", "api/v2"], - "default": "api/v1" - } - } - }, { "url": "http://localhost:{PORT}/{API_PATH}", "description": "Local Development Server", From f680ed8af1b23b3ab0a0d8bbfc1dffbed0b71d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sat, 31 Aug 2024 13:44:23 +0300 Subject: [PATCH 12/33] chore(openapi): add missing route groups to tags - invite - monitors - checks - alerts - pagespeed - maintenance-window - healthy - mail --- Server/openapi.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Server/openapi.json b/Server/openapi.json index 5db6250c5..d9a55dbe6 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -37,6 +37,38 @@ { "name": "auth", "description": "Authentication" + }, + { + "name": "invite", + "description": "Invite" + }, + { + "name": "monitors", + "description": "Monitors" + }, + { + "name": "checks", + "description": "Checks" + }, + { + "name": "alerts", + "description": "Alerts" + }, + { + "name": "pagespeed", + "description": "Page Speed" + }, + { + "name": "maintenance-window", + "description": "Maintenance window" + }, + { + "name": "healthy", + "description": "Health check" + }, + { + "name": "mail", + "description": "Mail service" } ], "paths": { From b6190728a02d32c8ae23631db9d1bfd8a26742a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C5=9Ei=C5=9Fmano=C4=9Flu?= Date: Sat, 31 Aug 2024 13:51:36 +0300 Subject: [PATCH 13/33] feat(openapi): add /healthy route for health check --- Server/openapi.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Server/openapi.json b/Server/openapi.json index d9a55dbe6..23c16521d 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -546,6 +546,44 @@ } } } + }, + "/healthy": { + "get": { + "tags": ["healthy"], + "description": "Health check", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + } + } + } } }, "components": { From d75b494d58b1f8aa67d45312a2edacd22d32c6c3 Mon Sep 17 00:00:00 2001 From: M M Date: Sun, 1 Sep 2024 12:25:23 -0700 Subject: [PATCH 14/33] Change color of percentage according to uptime of monitor. --- Client/src/Components/Alert/index.jsx | 4 ++-- .../src/Pages/Monitors/Home/monitorData.jsx | 23 +++++++++++++------ Client/src/Utils/Theme/darkTheme.js | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index eb5fbd13b..cafd58451 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -31,7 +31,7 @@ const icons = { const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => { const theme = useTheme(); - const { text, light, border } = theme.palette[variant]; + const { text, bg, border } = theme.palette[variant]; const icon = icons[variant]; return ( @@ -45,7 +45,7 @@ const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => { padding: hasIcon ? theme.spacing(8) : `${theme.spacing(4)} ${theme.spacing(8)}`, - backgroundColor: light, + backgroundColor: bg, border: `solid 1px ${border}`, borderRadius: theme.shape.borderRadius, }} diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index d3fd66958..9fd97408c 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -32,21 +32,30 @@ export const buildData = (monitors, isAdmin, navigate) => { data.rows = monitors.map((monitor, idx) => { let uptimePercentage = ""; + let percentageColor = theme.palette.success.main; + if (monitor.uptimePercentage !== undefined) { uptimePercentage = monitor.uptimePercentage === 0 - ? "0" - : (monitor.uptimePercentage * 100).toFixed(2); - } + ? "0" + : (monitor.uptimePercentage * 100).toFixed(2); + + if (monitor.uptimePercentage < 0.25) { + percentageColor = theme.palette.error.text; + } else if (monitor.uptimePercentage >= 0.25 && monitor.uptimePercentage < 0.5) { + percentageColor = theme.palette.warning.main; + } else if (monitor.uptimePercentage >= 0.5 && monitor.uptimePercentage < 0.75) { + percentageColor = theme.palette.warning.light; + } else if (monitor.uptimePercentage >= 0.75 && monitor.uptimePercentage <= 1) { + percentageColor = theme.palette.success.main; + } + } const params = { url: monitor.url, title: monitor.name, percentage: uptimePercentage, - percentageColor: - monitor.status === true - ? theme.palette.success.main - : theme.palette.error.text, + percentageColor, status: monitor.status === true ? "up" : "down", }; diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js index 15df7c1b8..a39fb4c1c 100644 --- a/Client/src/Utils/Theme/darkTheme.js +++ b/Client/src/Utils/Theme/darkTheme.js @@ -53,7 +53,7 @@ const darkTheme = createTheme({ warning: { text: "#e88c30", main: "#FF9F00", - light: "#27272a", + light: "#ffd600", bg: "#1E1E1E", border: "#e88c30", }, From 3da323e44e24180095198b3285cbc9ad2851af6e Mon Sep 17 00:00:00 2001 From: M M Date: Sun, 1 Sep 2024 13:40:52 -0700 Subject: [PATCH 15/33] Adjust colors for the light theme. --- Client/src/Utils/Theme/lightTheme.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js index bb83d09b2..51639fb0e 100644 --- a/Client/src/Utils/Theme/lightTheme.js +++ b/Client/src/Utils/Theme/lightTheme.js @@ -36,7 +36,7 @@ const lightTheme = createTheme({ }, success: { text: "#079455", - main: "#17b26a", + main: "#45bb7a", light: "#d4f4e1", bg: "#ecfdf3", }, @@ -49,8 +49,8 @@ const lightTheme = createTheme({ }, warning: { text: "#DC6803", - main: "#fdb022", - light: "#fffcf5", + main: "#ff7a00", + light: "#ffb400", bg: "#ffecbc", border: "#fec84b", }, From c515358d78c161c9f7eaa132643658fb947441f2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 2 Sep 2024 10:16:22 -0700 Subject: [PATCH 16/33] fix host proptypes --- Client/src/Pages/Monitors/Home/host.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Client/src/Pages/Monitors/Home/host.jsx b/Client/src/Pages/Monitors/Home/host.jsx index d450a53f4..9e6a71a05 100644 --- a/Client/src/Pages/Monitors/Home/host.jsx +++ b/Client/src/Pages/Monitors/Home/host.jsx @@ -53,7 +53,8 @@ Host.propTypes = { params: PropTypes.shape({ title: PropTypes.string, percentageColor: PropTypes.string, - percentage: PropTypes.number, + percentage: PropTypes.string, + url: PropTypes.string, }).isRequired, }; From 071a7fc9cf0e8307c9fd6921b7ac4a8ea3e0dc1d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 2 Sep 2024 10:45:07 -0700 Subject: [PATCH 17/33] Add indeterminate state to monitors --- Client/src/Components/Label/index.jsx | 8 ++- Client/src/Pages/Monitors/Home/host.jsx | 3 +- .../src/Pages/Monitors/Home/monitorData.jsx | 14 +++-- Server/controllers/monitorController.js | 1 + Server/models/Monitor.js | 4 +- Server/service/jobQueue.js | 1 - Server/service/networkService.js | 62 ++++++++++++------- 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Client/src/Components/Label/index.jsx b/Client/src/Components/Label/index.jsx index 0799a87d7..96468ffd6 100644 --- a/Client/src/Components/Label/index.jsx +++ b/Client/src/Components/Label/index.jsx @@ -126,6 +126,7 @@ ColoredLabel.propTypes = { */ const StatusLabel = ({ status, text, customStyles }) => { + console.log(status); const theme = useTheme(); const colors = { up: { @@ -138,6 +139,11 @@ const StatusLabel = ({ status, text, customStyles }) => { bgColor: theme.palette.error.bg, borderColor: theme.palette.error.light, }, + unknown: { + dotColor: theme.palette.unresolved.main, + bgColor: theme.palette.unresolved.bg, + borderColor: theme.palette.unresolved.light, + }, "cannot resolve": { dotColor: theme.palette.unresolved.main, bgColor: theme.palette.unresolved.bg, @@ -170,7 +176,7 @@ const StatusLabel = ({ status, text, customStyles }) => { }; StatusLabel.propTypes = { - status: PropTypes.oneOf(["up", "down", "cannot resolve"]), + status: PropTypes.oneOf(["up", "down", "unknown", "cannot resolve"]), text: PropTypes.string, customStyles: PropTypes.object, }; diff --git a/Client/src/Pages/Monitors/Home/host.jsx b/Client/src/Pages/Monitors/Home/host.jsx index d450a53f4..9e6a71a05 100644 --- a/Client/src/Pages/Monitors/Home/host.jsx +++ b/Client/src/Pages/Monitors/Home/host.jsx @@ -53,7 +53,8 @@ Host.propTypes = { params: PropTypes.shape({ title: PropTypes.string, percentageColor: PropTypes.string, - percentage: PropTypes.number, + percentage: PropTypes.string, + url: PropTypes.string, }).isRequired, }; diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index d3fd66958..28ff08756 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -35,8 +35,8 @@ export const buildData = (monitors, isAdmin, navigate) => { if (monitor.uptimePercentage !== undefined) { uptimePercentage = monitor.uptimePercentage === 0 - ? "0" - : (monitor.uptimePercentage * 100).toFixed(2); + ? "0" + : (monitor.uptimePercentage * 100).toFixed(2); } const params = { @@ -47,7 +47,12 @@ export const buildData = (monitors, isAdmin, navigate) => { monitor.status === true ? theme.palette.success.main : theme.palette.error.text, - status: monitor.status === true ? "up" : "down", + status: + monitor.status === undefined + ? "unknown" + : monitor.status === true + ? "up" + : "down", }; // Reverse checks so latest check is on the right @@ -86,6 +91,3 @@ export const buildData = (monitors, isAdmin, navigate) => { }); return data; }; - - - diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index f33ddcf10..702b28be4 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -359,6 +359,7 @@ const pauseMonitor = async (req, res, next) => { await req.jobQueue.addJob(monitor._id, monitor); } monitor.isActive = !monitor.isActive; + monitor.status = undefined; monitor.save(); return res.status(200).json({ success: true, diff --git a/Server/models/Monitor.js b/Server/models/Monitor.js index fdc147d1b..d91275e23 100644 --- a/Server/models/Monitor.js +++ b/Server/models/Monitor.js @@ -24,7 +24,7 @@ const MonitorSchema = mongoose.Schema( }, status: { type: Boolean, - default: true, + default: undefined, }, type: { type: String, @@ -46,7 +46,7 @@ const MonitorSchema = mongoose.Schema( }, uptimePercentage: { type: Number, - default: undefined, + default: undefined, }, notifications: [ { diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index ed15e0f94..5248c88fa 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -238,7 +238,6 @@ class JobQueue { console.log("Adding job", payload.url); // Execute job immediately await this.queue.add(jobName, payload); - await this.queue.add(jobName, payload, { repeat: { every: payload.interval, diff --git a/Server/service/networkService.js b/Server/service/networkService.js index f5c333a60..e9d44a5b3 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -15,10 +15,31 @@ class NetworkService { this.NETWORK_ERROR = 5000; } - async handleNotification(job, isAlive) { + async handleNotification(monitor, isAlive) { + let template = + isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; + let status = isAlive === true ? "up" : "down"; + + const notifications = await this.db.getNotificationsByMonitorId( + monitor._id + ); + for (const notification of notifications) { + if (notification.type === "email") { + await this.emailService.buildAndSendEmail( + template, + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.address, + `Monitor ${monitor.name} is ${status}` + ); + } + } + } + + async handleStatusUpdate(job, isAlive) { try { const { _id } = job.data; const monitor = await this.db.getMonitorById(_id); + if (monitor === null || monitor === undefined) { logger.error(`Null Monitor: ${_id}`, { method: "handleNotification", @@ -28,25 +49,22 @@ class NetworkService { return; } - // If monitor status changes, update monitor status and send notification - if (monitor.status !== isAlive) { - monitor.status = !monitor.status; + // Two general cases: + // 1. Monitor status is undefined + // 2. Monitor status is defined + + // If the monitor status is undefined, set it's status to isAlive. if isAlive is false, send notification + if (monitor.status === undefined) { + monitor.status = isAlive; await monitor.save(); - - let template = - isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; - let status = isAlive === true ? "up" : "down"; - - const notifications = await this.db.getNotificationsByMonitorId(_id); - for (const notification of notifications) { - if (notification.type === "email") { - await this.emailService.buildAndSendEmail( - template, - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is ${status}` - ); - } + if (isAlive === false) { + this.handleNotification(monitor, isAlive); + } + } else { + if (monitor.status !== isAlive) { + monitor.status = !monitor.status; + await monitor.save(); + this.handleNotification(monitor, isAlive); } } } catch (error) { @@ -113,7 +131,7 @@ class NetworkService { }; return await this.logAndStoreCheck(checkData, this.db.createCheck); } finally { - this.handleNotification(job, isAlive); + this.handleStatusUpdate(job, isAlive); } } @@ -161,7 +179,7 @@ class NetworkService { return await this.logAndStoreCheck(checkData, this.db.createCheck); } finally { - this.handleNotification(job, isAlive); + this.handleStatusUpdate(job, isAlive); } } @@ -241,7 +259,7 @@ class NetworkService { }; this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); } finally { - this.handleNotification(job, isAlive); + this.handleStatusUpdate(job, isAlive); } } From 4d61ecfe35bfd6187e73e7ee229220fa51442d03 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 2 Sep 2024 10:52:11 -0700 Subject: [PATCH 18/33] Add error handling to notifications --- Server/service/networkService.js | 40 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index e9d44a5b3..23b115c97 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -16,22 +16,30 @@ class NetworkService { } async handleNotification(monitor, isAlive) { - let template = - isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; - let status = isAlive === true ? "up" : "down"; + try { + let template = + isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; + let status = isAlive === true ? "up" : "down"; - const notifications = await this.db.getNotificationsByMonitorId( - monitor._id - ); - for (const notification of notifications) { - if (notification.type === "email") { - await this.emailService.buildAndSendEmail( - template, - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is ${status}` - ); + const notifications = await this.db.getNotificationsByMonitorId( + monitor._id + ); + for (const notification of notifications) { + if (notification.type === "email") { + await this.emailService.buildAndSendEmail( + template, + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.address, + `Monitor ${monitor.name} is ${status}` + ); + } } + } catch (error) { + logger.error(error.message, { + method: "handleNotification", + service: this.SERVICE_NAME, + monitorId: monitor._id, + }); } } @@ -42,7 +50,7 @@ class NetworkService { if (monitor === null || monitor === undefined) { logger.error(`Null Monitor: ${_id}`, { - method: "handleNotification", + method: "handleStatusUpdate", service: this.SERVICE_NAME, jobId: job.id, }); @@ -69,7 +77,7 @@ class NetworkService { } } catch (error) { logger.error(error.message, { - method: "handleNotification", + method: "handleStatusUpdate", service: this.SERVICE_NAME, jobId: job.id, }); From d2528a694e23151a9890d778e7e045a1a626b32a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 2 Sep 2024 11:00:59 -0700 Subject: [PATCH 19/33] simplified conditionals for updating status and sending notifications --- Server/service/networkService.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 23b115c97..863349dbf 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -56,22 +56,12 @@ class NetworkService { }); return; } - - // Two general cases: - // 1. Monitor status is undefined - // 2. Monitor status is defined - - // If the monitor status is undefined, set it's status to isAlive. if isAlive is false, send notification - if (monitor.status === undefined) { + if (monitor.status === undefined || monitor.status !== isAlive) { + const oldStatus = monitor.status; monitor.status = isAlive; await monitor.save(); - if (isAlive === false) { - this.handleNotification(monitor, isAlive); - } - } else { - if (monitor.status !== isAlive) { - monitor.status = !monitor.status; - await monitor.save(); + + if (oldStatus !== undefined && oldStatus !== isAlive) { this.handleNotification(monitor, isAlive); } } From 3a27ce0a8aafbf8932e85c89ccc17f5097c8099a Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 2 Sep 2024 14:00:26 -0700 Subject: [PATCH 20/33] Revert to old colors on the light and dark theme. --- Client/src/Utils/Theme/darkTheme.js | 2 +- Client/src/Utils/Theme/lightTheme.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js index a39fb4c1c..15df7c1b8 100644 --- a/Client/src/Utils/Theme/darkTheme.js +++ b/Client/src/Utils/Theme/darkTheme.js @@ -53,7 +53,7 @@ const darkTheme = createTheme({ warning: { text: "#e88c30", main: "#FF9F00", - light: "#ffd600", + light: "#27272a", bg: "#1E1E1E", border: "#e88c30", }, diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js index 51639fb0e..bb83d09b2 100644 --- a/Client/src/Utils/Theme/lightTheme.js +++ b/Client/src/Utils/Theme/lightTheme.js @@ -36,7 +36,7 @@ const lightTheme = createTheme({ }, success: { text: "#079455", - main: "#45bb7a", + main: "#17b26a", light: "#d4f4e1", bg: "#ecfdf3", }, @@ -49,8 +49,8 @@ const lightTheme = createTheme({ }, warning: { text: "#DC6803", - main: "#ff7a00", - light: "#ffb400", + main: "#fdb022", + light: "#fffcf5", bg: "#ffecbc", border: "#fec84b", }, From 081ad080a8bb3d9cc4ea91b63b147a8d8e0346a4 Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 2 Sep 2024 15:03:40 -0700 Subject: [PATCH 21/33] Add new objects to the color palettes. --- Client/src/Pages/Monitors/Home/monitorData.jsx | 10 +++++----- Client/src/Utils/Theme/darkTheme.js | 6 ++++++ Client/src/Utils/Theme/lightTheme.js | 6 ++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index 9fd97408c..ee167edb4 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -32,7 +32,7 @@ export const buildData = (monitors, isAdmin, navigate) => { data.rows = monitors.map((monitor, idx) => { let uptimePercentage = ""; - let percentageColor = theme.palette.success.main; + let percentageColor = theme.palette.percentage.green; if (monitor.uptimePercentage !== undefined) { uptimePercentage = @@ -41,13 +41,13 @@ export const buildData = (monitors, isAdmin, navigate) => { : (monitor.uptimePercentage * 100).toFixed(2); if (monitor.uptimePercentage < 0.25) { - percentageColor = theme.palette.error.text; + percentageColor = theme.palette.percentage.red; } else if (monitor.uptimePercentage >= 0.25 && monitor.uptimePercentage < 0.5) { - percentageColor = theme.palette.warning.main; + percentageColor = theme.palette.percentage.orange; } else if (monitor.uptimePercentage >= 0.5 && monitor.uptimePercentage < 0.75) { - percentageColor = theme.palette.warning.light; + percentageColor = theme.palette.percentage.yellow; } else if (monitor.uptimePercentage >= 0.75 && monitor.uptimePercentage <= 1) { - percentageColor = theme.palette.success.main; + percentageColor = theme.palette.percentage.green; } } diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js index 15df7c1b8..3b77966cd 100644 --- a/Client/src/Utils/Theme/darkTheme.js +++ b/Client/src/Utils/Theme/darkTheme.js @@ -57,6 +57,12 @@ const darkTheme = createTheme({ bg: "#1E1E1E", border: "#e88c30", }, + percentage: { + red: "#d32f2f", + orange: "#e88c30", + yellow: "#ffd600", + green: "#079455", + }, unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, divider: border.light, other: { diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js index bb83d09b2..67c2d72e5 100644 --- a/Client/src/Utils/Theme/lightTheme.js +++ b/Client/src/Utils/Theme/lightTheme.js @@ -54,6 +54,12 @@ const lightTheme = createTheme({ bg: "#ffecbc", border: "#fec84b", }, + percentage: { + red: "#d32f2f", + orange: "#ec8013", + yellow: "#ffb800", + green: "#079455", + }, unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, divider: border.light, other: { From 4ebaadbe24549cdb50e77688c260bcc4444aa5ba Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 2 Sep 2024 15:34:01 -0700 Subject: [PATCH 22/33] Use ternary based logic rather than conditional statements for clarity. --- .../src/Pages/Monitors/Home/monitorData.jsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index ee167edb4..2f176bd04 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -40,16 +40,15 @@ export const buildData = (monitors, isAdmin, navigate) => { ? "0" : (monitor.uptimePercentage * 100).toFixed(2); - if (monitor.uptimePercentage < 0.25) { - percentageColor = theme.palette.percentage.red; - } else if (monitor.uptimePercentage >= 0.25 && monitor.uptimePercentage < 0.5) { - percentageColor = theme.palette.percentage.orange; - } else if (monitor.uptimePercentage >= 0.5 && monitor.uptimePercentage < 0.75) { - percentageColor = theme.palette.percentage.yellow; - } else if (monitor.uptimePercentage >= 0.75 && monitor.uptimePercentage <= 1) { - percentageColor = theme.palette.percentage.green; - } - } + percentageColor = + monitor.uptimePercentage < 0.25 + ? theme.palette.percentage.red + : monitor.uptimePercentage < 0.5 + ? theme.palette.percentage.orange + : monitor.uptimePercentage < 0.75 + ? theme.palette.percentage.yellow + : theme.palette.percentage.green; + } const params = { url: monitor.url, From 6e8334026c6985df02b87f5e33535a56d2b8a02e Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 2 Sep 2024 16:19:56 -0700 Subject: [PATCH 23/33] Add docs. --- Client/src/Pages/Monitors/Home/monitorData.jsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index 2f176bd04..4220fc2c5 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -27,6 +27,14 @@ const data = { rows: [], }; +/** + * Builds table data for a list of monitors. + * + * @param {Array} monitors - An array of monitor objects containing information about each monitor. + * @param {boolean} isAdmin - Flag indicating if the current user is an admin. + * @param {Function} navigate - A function to navigate to the monitor detail page. + * @returns {Object} The data structure containing columns and rows for the table. + */ export const buildData = (monitors, isAdmin, navigate) => { const theme = useTheme(); @@ -34,13 +42,14 @@ export const buildData = (monitors, isAdmin, navigate) => { let uptimePercentage = ""; let percentageColor = theme.palette.percentage.green; + // Determine uptime percentage and color based on the monitor's uptimePercentage value if (monitor.uptimePercentage !== undefined) { uptimePercentage = monitor.uptimePercentage === 0 ? "0" : (monitor.uptimePercentage * 100).toFixed(2); - percentageColor = + percentageColor = monitor.uptimePercentage < 0.25 ? theme.palette.percentage.red : monitor.uptimePercentage < 0.5 @@ -58,7 +67,7 @@ export const buildData = (monitors, isAdmin, navigate) => { status: monitor.status === true ? "up" : "down", }; - // Reverse checks so latest check is on the right + // Reverse checks so the latest check is on the right const reversedChecks = monitor.checks.slice().reverse(); return { @@ -94,6 +103,3 @@ export const buildData = (monitors, isAdmin, navigate) => { }); return data; }; - - - From c4dafae21e5f626b42e694519bf8e0f922e5c80a Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 2 Sep 2024 19:07:43 -0700 Subject: [PATCH 24/33] Give props more meaningful names. --- Client/src/Pages/Monitors/Home/monitorData.jsx | 10 +++++----- Client/src/Utils/Theme/darkTheme.js | 8 ++++---- Client/src/Utils/Theme/lightTheme.js | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index 4220fc2c5..643327e7e 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -40,7 +40,7 @@ export const buildData = (monitors, isAdmin, navigate) => { data.rows = monitors.map((monitor, idx) => { let uptimePercentage = ""; - let percentageColor = theme.palette.percentage.green; + let percentageColor = theme.palette.percentage.q4; // Determine uptime percentage and color based on the monitor's uptimePercentage value if (monitor.uptimePercentage !== undefined) { @@ -51,12 +51,12 @@ export const buildData = (monitors, isAdmin, navigate) => { percentageColor = monitor.uptimePercentage < 0.25 - ? theme.palette.percentage.red + ? theme.palette.percentage.q1 : monitor.uptimePercentage < 0.5 - ? theme.palette.percentage.orange + ? theme.palette.percentage.q2 : monitor.uptimePercentage < 0.75 - ? theme.palette.percentage.yellow - : theme.palette.percentage.green; + ? theme.palette.percentage.q3 + : theme.palette.percentage.q4; } const params = { diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js index 3b77966cd..f680533f8 100644 --- a/Client/src/Utils/Theme/darkTheme.js +++ b/Client/src/Utils/Theme/darkTheme.js @@ -58,10 +58,10 @@ const darkTheme = createTheme({ border: "#e88c30", }, percentage: { - red: "#d32f2f", - orange: "#e88c30", - yellow: "#ffd600", - green: "#079455", + q1: "#d32f2f", + q2: "#e88c30", + q3: "#ffd600", + q4: "#079455", }, unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, divider: border.light, diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js index 67c2d72e5..cc8b2f226 100644 --- a/Client/src/Utils/Theme/lightTheme.js +++ b/Client/src/Utils/Theme/lightTheme.js @@ -55,10 +55,10 @@ const lightTheme = createTheme({ border: "#fec84b", }, percentage: { - red: "#d32f2f", - orange: "#ec8013", - yellow: "#ffb800", - green: "#079455", + q1: "#d32f2f", + q2: "#ec8013", + q3: "#ffb800", + q4: "#079455", }, unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, divider: border.light, From ed99748718545b457ed8d49f63ee87bda34ec4a0 Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 2 Sep 2024 19:14:43 -0700 Subject: [PATCH 25/33] Name props according to purpose rather than value. --- Client/src/Pages/Monitors/Home/monitorData.jsx | 10 +++++----- Client/src/Utils/Theme/darkTheme.js | 8 ++++---- Client/src/Utils/Theme/lightTheme.js | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index 643327e7e..1942e5a9c 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -40,7 +40,7 @@ export const buildData = (monitors, isAdmin, navigate) => { data.rows = monitors.map((monitor, idx) => { let uptimePercentage = ""; - let percentageColor = theme.palette.percentage.q4; + let percentageColor = theme.palette.percentage.uptimeExcellent; // Determine uptime percentage and color based on the monitor's uptimePercentage value if (monitor.uptimePercentage !== undefined) { @@ -51,12 +51,12 @@ export const buildData = (monitors, isAdmin, navigate) => { percentageColor = monitor.uptimePercentage < 0.25 - ? theme.palette.percentage.q1 + ? theme.palette.percentage.uptimePoor : monitor.uptimePercentage < 0.5 - ? theme.palette.percentage.q2 + ? theme.palette.percentage.uptimeFair : monitor.uptimePercentage < 0.75 - ? theme.palette.percentage.q3 - : theme.palette.percentage.q4; + ? theme.palette.percentage.uptimeGood + : theme.palette.percentage.uptimeExcellent; } const params = { diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js index f680533f8..008de87db 100644 --- a/Client/src/Utils/Theme/darkTheme.js +++ b/Client/src/Utils/Theme/darkTheme.js @@ -58,10 +58,10 @@ const darkTheme = createTheme({ border: "#e88c30", }, percentage: { - q1: "#d32f2f", - q2: "#e88c30", - q3: "#ffd600", - q4: "#079455", + uptimePoor: "#d32f2f", + uptimeFair: "#e88c30", + uptimeGood: "#ffd600", + uptimeExcellent: "#079455", }, unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, divider: border.light, diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js index cc8b2f226..6688a0ca4 100644 --- a/Client/src/Utils/Theme/lightTheme.js +++ b/Client/src/Utils/Theme/lightTheme.js @@ -55,10 +55,10 @@ const lightTheme = createTheme({ border: "#fec84b", }, percentage: { - q1: "#d32f2f", - q2: "#ec8013", - q3: "#ffb800", - q4: "#079455", + uptimePoor: "#d32f2f", + uptimeFair: "#ec8013", + uptimeGood: "#ffb800", + uptimeExcellent: "#079455", }, unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" }, divider: border.light, From d77be4ce7413054d6cfead0b2db62a9d997e65f0 Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 3 Sep 2024 07:41:42 -0700 Subject: [PATCH 26/33] Delete unused component. --- .../Charts/ResponseTimeChart/index.css | 0 .../Charts/ResponseTimeChart/index.jsx | 43 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 Client/src/Components/Charts/ResponseTimeChart/index.css delete mode 100644 Client/src/Components/Charts/ResponseTimeChart/index.jsx diff --git a/Client/src/Components/Charts/ResponseTimeChart/index.css b/Client/src/Components/Charts/ResponseTimeChart/index.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/Client/src/Components/Charts/ResponseTimeChart/index.jsx b/Client/src/Components/Charts/ResponseTimeChart/index.jsx deleted file mode 100644 index fd6c0f41e..000000000 --- a/Client/src/Components/Charts/ResponseTimeChart/index.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from "prop-types"; -import { Stack } from "@mui/material"; -import { BarChart, Bar, ResponsiveContainer, Cell } from "recharts"; -import "./index.css"; - -const ResponseTimeChart = ({ checks = [] }) => { - return ( - - - - - {checks.map((check, index) => ( - - ))} - - - - - ); -}; - -ResponseTimeChart.propTypes = { - checks: PropTypes.array, -}; -export default ResponseTimeChart; From 1bde8fdb868924646b636e0647aa047dccb1838d Mon Sep 17 00:00:00 2001 From: Alexander Holliday Date: Tue, 3 Sep 2024 09:30:43 -0700 Subject: [PATCH 27/33] Revert "Add indeterminate state to monitors, resolves #765" --- Client/src/Components/Label/index.jsx | 8 +-- .../src/Pages/Monitors/Home/monitorData.jsx | 9 +++ Server/controllers/monitorController.js | 1 - Server/models/Monitor.js | 4 +- Server/service/jobQueue.js | 1 + Server/service/networkService.js | 64 +++++++------------ 6 files changed, 37 insertions(+), 50 deletions(-) diff --git a/Client/src/Components/Label/index.jsx b/Client/src/Components/Label/index.jsx index 96468ffd6..0799a87d7 100644 --- a/Client/src/Components/Label/index.jsx +++ b/Client/src/Components/Label/index.jsx @@ -126,7 +126,6 @@ ColoredLabel.propTypes = { */ const StatusLabel = ({ status, text, customStyles }) => { - console.log(status); const theme = useTheme(); const colors = { up: { @@ -139,11 +138,6 @@ const StatusLabel = ({ status, text, customStyles }) => { bgColor: theme.palette.error.bg, borderColor: theme.palette.error.light, }, - unknown: { - dotColor: theme.palette.unresolved.main, - bgColor: theme.palette.unresolved.bg, - borderColor: theme.palette.unresolved.light, - }, "cannot resolve": { dotColor: theme.palette.unresolved.main, bgColor: theme.palette.unresolved.bg, @@ -176,7 +170,7 @@ const StatusLabel = ({ status, text, customStyles }) => { }; StatusLabel.propTypes = { - status: PropTypes.oneOf(["up", "down", "unknown", "cannot resolve"]), + status: PropTypes.oneOf(["up", "down", "cannot resolve"]), text: PropTypes.string, customStyles: PropTypes.object, }; diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index d84496923..1942e5a9c 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -48,6 +48,15 @@ export const buildData = (monitors, isAdmin, navigate) => { monitor.uptimePercentage === 0 ? "0" : (monitor.uptimePercentage * 100).toFixed(2); + + percentageColor = + monitor.uptimePercentage < 0.25 + ? theme.palette.percentage.uptimePoor + : monitor.uptimePercentage < 0.5 + ? theme.palette.percentage.uptimeFair + : monitor.uptimePercentage < 0.75 + ? theme.palette.percentage.uptimeGood + : theme.palette.percentage.uptimeExcellent; } const params = { diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 702b28be4..f33ddcf10 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -359,7 +359,6 @@ const pauseMonitor = async (req, res, next) => { await req.jobQueue.addJob(monitor._id, monitor); } monitor.isActive = !monitor.isActive; - monitor.status = undefined; monitor.save(); return res.status(200).json({ success: true, diff --git a/Server/models/Monitor.js b/Server/models/Monitor.js index d91275e23..fdc147d1b 100644 --- a/Server/models/Monitor.js +++ b/Server/models/Monitor.js @@ -24,7 +24,7 @@ const MonitorSchema = mongoose.Schema( }, status: { type: Boolean, - default: undefined, + default: true, }, type: { type: String, @@ -46,7 +46,7 @@ const MonitorSchema = mongoose.Schema( }, uptimePercentage: { type: Number, - default: undefined, + default: undefined, }, notifications: [ { diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 5248c88fa..ed15e0f94 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -238,6 +238,7 @@ class JobQueue { console.log("Adding job", payload.url); // Execute job immediately await this.queue.add(jobName, payload); + await this.queue.add(jobName, payload, { repeat: { every: payload.interval, diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 863349dbf..f5c333a60 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -15,59 +15,43 @@ class NetworkService { this.NETWORK_ERROR = 5000; } - async handleNotification(monitor, isAlive) { - try { - let template = - isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; - let status = isAlive === true ? "up" : "down"; - - const notifications = await this.db.getNotificationsByMonitorId( - monitor._id - ); - for (const notification of notifications) { - if (notification.type === "email") { - await this.emailService.buildAndSendEmail( - template, - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is ${status}` - ); - } - } - } catch (error) { - logger.error(error.message, { - method: "handleNotification", - service: this.SERVICE_NAME, - monitorId: monitor._id, - }); - } - } - - async handleStatusUpdate(job, isAlive) { + async handleNotification(job, isAlive) { try { const { _id } = job.data; const monitor = await this.db.getMonitorById(_id); - if (monitor === null || monitor === undefined) { logger.error(`Null Monitor: ${_id}`, { - method: "handleStatusUpdate", + method: "handleNotification", service: this.SERVICE_NAME, jobId: job.id, }); return; } - if (monitor.status === undefined || monitor.status !== isAlive) { - const oldStatus = monitor.status; - monitor.status = isAlive; + + // If monitor status changes, update monitor status and send notification + if (monitor.status !== isAlive) { + monitor.status = !monitor.status; await monitor.save(); - if (oldStatus !== undefined && oldStatus !== isAlive) { - this.handleNotification(monitor, isAlive); + let template = + isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; + let status = isAlive === true ? "up" : "down"; + + const notifications = await this.db.getNotificationsByMonitorId(_id); + for (const notification of notifications) { + if (notification.type === "email") { + await this.emailService.buildAndSendEmail( + template, + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.address, + `Monitor ${monitor.name} is ${status}` + ); + } } } } catch (error) { logger.error(error.message, { - method: "handleStatusUpdate", + method: "handleNotification", service: this.SERVICE_NAME, jobId: job.id, }); @@ -129,7 +113,7 @@ class NetworkService { }; return await this.logAndStoreCheck(checkData, this.db.createCheck); } finally { - this.handleStatusUpdate(job, isAlive); + this.handleNotification(job, isAlive); } } @@ -177,7 +161,7 @@ class NetworkService { return await this.logAndStoreCheck(checkData, this.db.createCheck); } finally { - this.handleStatusUpdate(job, isAlive); + this.handleNotification(job, isAlive); } } @@ -257,7 +241,7 @@ class NetworkService { }; this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); } finally { - this.handleStatusUpdate(job, isAlive); + this.handleNotification(job, isAlive); } } From cfc2700d19d1ee52d84f89b3ca14e4ddbbb360eb Mon Sep 17 00:00:00 2001 From: Mohammad Khalilzadeh <140876993+MuhammadKhalilzadeh@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:42:31 +0330 Subject: [PATCH 28/33] Autocomplete (#774) * autocomplete implementation * autocomplete implementation * autocomplete implementation * fetched upstream * fixes after fetched upstream * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete * autocomplete --- .../Components/Inputs/Autocomplete/index.jsx | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 Client/src/Components/Inputs/Autocomplete/index.jsx diff --git a/Client/src/Components/Inputs/Autocomplete/index.jsx b/Client/src/Components/Inputs/Autocomplete/index.jsx new file mode 100644 index 000000000..5256efccc --- /dev/null +++ b/Client/src/Components/Inputs/Autocomplete/index.jsx @@ -0,0 +1,116 @@ +import { Autocomplete, TextField } from "@mui/material"; +import React from "react"; +import PropTypes from "prop-types"; +import { useTheme } from "@emotion/react"; + +/** + * @example + * + * const options = [ + * { _id: "66d6119ef959cbc681e034f0", name: "Googler" }, + * { _id: "66d6119ef959cbc681e034f0", name: "CNN" }, + * { _id: "66d61a1bf959cbc681e0353f", name: "X Corp." }, + * ]; + * + * + * + */ + +/** + * AutoCompleteField component. + * + * @component + * @param {Object} props - The component props. + * @param {string} props.id - The ID of the autocomplete field. + * @param {string} props.type - The type of the input field (text or number). + * @param {Array} props.options - The options for the autocomplete field. + * @param {string} props.placeholder - The placeholder text for the input field. + * @param {boolean} props.disabled - Indicates if the field is disabled. + * @returns {JSX.Element} The AutoCompleteField component. + */ + +const AutoCompleteField = ({ + id, + type, + options, + placeholder = "Type to search", + disabled, +}) => { + const [value, setValue] = React.useState(); + const [inputValue, setInputValue] = React.useState(""); + const theme = useTheme(); + + return ( + { + setValue(newValue); + }} + inputValue={inputValue} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + options={options} + getOptionLabel={(option) => option.name} + renderInput={(params) => ( + + )} + renderOption={(props, option) => { + const { key, ...optionProps } = props; + return ( +
  • +
    {{option.name}}
    +
  • + ); + }} + slotProps={{ + popper: { + sx: { + "& ul": { p: 0 }, + "& li": { borderRadius: theme.shape.borderRadius }, + }, + }, + paper: { + sx: { + p: 2, + fontSize: 13, + }, + }, + }} + /> + ); +}; + +AutoCompleteField.displayName = "AutoCompleteField"; + +AutoCompleteField.propTypes = { + id: PropTypes.string, + type: PropTypes.oneOf(["text", "number"]), + options: PropTypes.array, + placeholder: PropTypes.string, + disabled: PropTypes.bool, + setWidth: PropTypes.string, +}; + +export default AutoCompleteField; From 88316de306d56e92e961b76f8053700075f5a2f4 Mon Sep 17 00:00:00 2001 From: Alexander Holliday Date: Tue, 3 Sep 2024 12:08:15 -0700 Subject: [PATCH 29/33] add indeterminate state back to monitorData after bad conflict resolution (#785) --- Client/src/Pages/Monitors/Home/monitorData.jsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/monitorData.jsx b/Client/src/Pages/Monitors/Home/monitorData.jsx index 1942e5a9c..2351a59e9 100644 --- a/Client/src/Pages/Monitors/Home/monitorData.jsx +++ b/Client/src/Pages/Monitors/Home/monitorData.jsx @@ -40,7 +40,7 @@ export const buildData = (monitors, isAdmin, navigate) => { data.rows = monitors.map((monitor, idx) => { let uptimePercentage = ""; - let percentageColor = theme.palette.percentage.uptimeExcellent; + let percentageColor = theme.palette.percentage.uptimeExcellent; // Determine uptime percentage and color based on the monitor's uptimePercentage value if (monitor.uptimePercentage !== undefined) { @@ -63,8 +63,13 @@ export const buildData = (monitors, isAdmin, navigate) => { url: monitor.url, title: monitor.name, percentage: uptimePercentage, - percentageColor, - status: monitor.status === true ? "up" : "down", + percentageColor, + status: + monitor.status === undefined + ? "unknown" + : monitor.status === true + ? "up" + : "down", }; // Reverse checks so the latest check is on the right From bc0df325d1043fc4b2fa0caa414724045a5253e7 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 3 Sep 2024 14:39:16 -0700 Subject: [PATCH 30/33] Add route, controller, db operations, and validation for aggregate monitor stats --- Client/src/Utils/NetworkService.js | 2 +- Server/controllers/monitorController.js | 42 +++++++++++- Server/db/mongo/MongoDB.js | 2 + Server/db/mongo/modules/monitorModule.js | 83 ++++++++++++++++++++++++ Server/routes/monitorRoute.js | 1 + Server/validation/joi.js | 9 +++ 6 files changed, 137 insertions(+), 2 deletions(-) diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index 31a23b5b1..6dcde82bb 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -116,7 +116,7 @@ class NetworkService { * @param {string} [sortOrder] - The order in which to sort the retrieved statistics. * @param {number} [limit] - The maximum number of statistics to retrieve. * @param {string} [dateRange] - The date range for which to retrieve statistics. - * @param {number} [numToDisplay] - The number of statistics to display. + * @param {number} [numToDisplay] - The number of checks to display. * @param {boolean} [normalize] - Whether to normalize the retrieved statistics. * @returns {Promise} The response from the axios GET request. */ diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index f33ddcf10..0d831f5b6 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -6,12 +6,13 @@ const { editMonitorBodyValidation, getMonitorsByTeamIdQueryValidation, pauseMonitorParamValidation, + getMonitorAggregateStatsParamValidation, + getMonitorAggregateStatsQueryValidation, } = require("../validation/joi"); const sslChecker = require("ssl-checker"); const SERVICE_NAME = "monitorController"; const { errorMessages, successMessages } = require("../utils/messages"); -const { runInNewContext } = require("vm"); /** * Returns all monitors @@ -35,6 +36,44 @@ const getAllMonitors = async (req, res, next) => { } }; +/** + * Returns agregate stats for a monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ + +const getMonitorAggregateStats = async (req, res, next) => { + try { + await getMonitorAggregateStatsParamValidation.validateAsync(req.params); + await getMonitorAggregateStatsQueryValidation.validateAsync(req.query); + } catch (error) { + error.status = 422; + error.message = + error.details?.[0]?.message || error.message || "Validation Error"; + next(error); + return; + } + + try { + const { monitorId } = req.params; + const dateRange = req.query.dateRange; + const aggregateStats = await req.db.getMonitorAggregateStats( + monitorId, + dateRange + ); + return res.json({ + success: true, + msg: successMessages.MONTIOR_STATS_BY_ID, + data: aggregateStats, + }); + } catch (error) { + next(error); + } +}; + /** * Returns monitor stats for monitor with matching ID * @async @@ -376,6 +415,7 @@ const pauseMonitor = async (req, res, next) => { module.exports = { getAllMonitors, + getMonitorAggregateStats, getMonitorStatsById, getMonitorCertificate, getMonitorById, diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js index 912bfb438..6f959f758 100644 --- a/Server/db/mongo/MongoDB.js +++ b/Server/db/mongo/MongoDB.js @@ -64,6 +64,7 @@ const { const { getAllMonitors, + getMonitorAggregateStats, getMonitorStatsById, getMonitorById, getMonitorsByTeamId, @@ -144,6 +145,7 @@ module.exports = { resetPassword, checkSuperadmin, getAllMonitors, + getMonitorAggregateStats, getMonitorStatsById, getMonitorById, getMonitorsByTeamId, diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 79e8917ea..0bd054000 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -143,6 +143,88 @@ const getStatusBarValues = (monitor, checks) => { } return statusBarValues.reverse(); }; + +/** + * Get aggregate monitor stats for charts + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const getMonitorAggregateStats = async (monitorId, dateRange) => { + const startDates = { + day: new Date(new Date().setDate(new Date().getDate() - 1)), + + week: new Date(new Date().setDate(new Date().getDate() - 7)), + month: new Date(new Date().setMonth(new Date().getMonth() - 1)), + }; + + try { + const startDate = startDates[dateRange]; + if (!startDate) { + throw new Error("Invalid date range specified"); + } + const endDate = new Date(); + + const checks = await Check.find({ + monitorId: monitorId, + createdAt: { + $gte: startDate, + $lte: endDate, + }, + }); + + let groupedChecks; + // Group checks by hour if range is day + if (dateRange === "day") { + groupedChecks = checks.reduce((acc, check) => { + const hour = new Date(check.createdAt).getHours(); + if (!acc[hour]) { + acc[hour] = { hour, checks: [] }; + } + acc[hour].checks.push(check); + return acc; + }, {}); + } + + // Group checks by day if range is week or month + else { + groupedChecks = checks.reduce((acc, check) => { + const day = new Date(check.createdAt).toISOString().split("T")[0]; // Extract the date part + if (!acc[day]) { + acc[day] = { day, checks: [] }; + } + acc[day].checks.push(check); + return acc; + }, {}); + } + + // Map grouped checks to stats + stats = Object.values(groupedChecks).map((group) => { + const totalChecks = group.checks.length; + const totalIncidents = group.checks.filter( + (check) => check.status === false + ).length; + const avgResponseTime = + group.checks.reduce((sum, check) => sum + check.responseTime, 0) / + totalChecks; + + return { + hour: group.hour, + day: group.day, + totalChecks, + totalIncidents, + avgResponseTime: `${avgResponseTime.toFixed(2)}ms`, + }; + }); + + return stats; + } catch (error) { + throw error; + } +}; + /** * Get stats by monitor ID * @async @@ -452,6 +534,7 @@ const editMonitor = async (candidateId, candidateMonitor) => { module.exports = { getAllMonitors, + getMonitorAggregateStats, getMonitorStatsById, getMonitorById, getMonitorsByTeamId, diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index ee1c08488..7831f42f7 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -3,6 +3,7 @@ const monitorController = require("../controllers/monitorController"); const { isAllowed } = require("../middleware/isAllowed"); router.get("/", monitorController.getAllMonitors); +router.get("/aggregate/:monitorId", monitorController.getMonitorAggregateStats); router.get("/stats/:monitorId", monitorController.getMonitorStatsById); router.get("/certificate/:monitorId", monitorController.getMonitorCertificate); router.get("/:monitorId", monitorController.getMonitorById); diff --git a/Server/validation/joi.js b/Server/validation/joi.js index ccc8da9f6..629deddf5 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -201,6 +201,13 @@ const pauseMonitorParamValidation = joi.object({ monitorId: joi.string().required(), }); +const getMonitorAggregateStatsParamValidation = joi.object({ + monitorId: joi.string().required(), +}); +const getMonitorAggregateStatsQueryValidation = joi.object({ + dateRange: joi.string().valid("day", "week", "month"), +}); + //**************************************** // Alerts //**************************************** @@ -357,6 +364,8 @@ module.exports = { getMonitorByIdQueryValidation, getMonitorsByTeamIdValidation, getMonitorsByTeamIdQueryValidation, + getMonitorAggregateStatsParamValidation, + getMonitorAggregateStatsQueryValidation, editMonitorBodyValidation, pauseMonitorParamValidation, editUserParamValidation, From 99b4da347f0bb51ed5781699ca3f6c64336fd38d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 3 Sep 2024 14:47:37 -0700 Subject: [PATCH 31/33] remove space --- Server/db/mongo/modules/monitorModule.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 0bd054000..11b4b49a3 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -155,7 +155,6 @@ const getStatusBarValues = (monitor, checks) => { const getMonitorAggregateStats = async (monitorId, dateRange) => { const startDates = { day: new Date(new Date().setDate(new Date().getDate() - 1)), - week: new Date(new Date().setDate(new Date().getDate() - 7)), month: new Date(new Date().setMonth(new Date().getMonth() - 1)), }; From 64fe1a3af58579913f08126a1015d0a326a17248 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 3 Sep 2024 14:52:00 -0700 Subject: [PATCH 32/33] Return raw value instead of formatted string --- Server/db/mongo/modules/monitorModule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 11b4b49a3..e0718a0bb 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -214,7 +214,7 @@ const getMonitorAggregateStats = async (monitorId, dateRange) => { day: group.day, totalChecks, totalIncidents, - avgResponseTime: `${avgResponseTime.toFixed(2)}ms`, + avgResponseTime, }; }); From 34ce3af412d483b04ddfedd367984fa5ec4142fe Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 3 Sep 2024 15:04:29 -0700 Subject: [PATCH 33/33] Add 7 days uptime percentage stat --- Server/db/mongo/modules/monitorModule.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index e0718a0bb..b913519b6 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -281,6 +281,10 @@ const getMonitorStatsById = async (req) => { ...checksQuery, createdAt: { $gte: filterLookup.day }, }; + const checksQuery7Days = { + ...checksQuery, + createdAt: { $gte: filterLookup.week }, + }; const checksQuery30Days = { ...checksQuery, createdAt: { $gte: filterLookup.month }, @@ -290,15 +294,18 @@ const getMonitorStatsById = async (req) => { createdAt: { $gte: filterLookup.hour }, }; - const [checks24Hours, checks30Days, checks60Mins] = await Promise.all([ - model.find(checksQuery24Hours).sort({ createdAt: sortOrder }), - model.find(checksQuery30Days).sort({ createdAt: sortOrder }), - model.find(checksQuery60Mins).sort({ createdAt: sortOrder }), - ]); + const [checks24Hours, checks7Days, checks30Days, checks60Mins] = + await Promise.all([ + model.find(checksQuery24Hours).sort({ createdAt: sortOrder }), + model.find(checksQuery7Days).sort({ createdAt: sortOrder }), + model.find(checksQuery30Days).sort({ createdAt: sortOrder }), + model.find(checksQuery60Mins).sort({ createdAt: sortOrder }), + ]); // HTTP/PING Specific stats monitorStats.avgResponseTime24hours = getAverageResponseTime24Hours(checks24Hours); + monitorStats.uptime7Days = getUptimePercentage(checks7Days); monitorStats.uptime24Hours = getUptimePercentage(checks24Hours); monitorStats.uptime30Days = getUptimePercentage(checks30Days); monitorStats.statusBar = getStatusBarValues(monitor, checks60Mins);