diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index c087fb73a..905385e70 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -609,7 +609,7 @@ class NetworkService { */ async updateChecksTTL(config) { return this.axiosInstance.put( - `/checks/ttl`, + `/checks/team/ttl`, { ttl: config.ttl }, { headers: { diff --git a/Docker/nginx/conf.d/default.conf b/Docker/nginx/conf.d/default.conf index a04d32c87..a27e8a744 100644 --- a/Docker/nginx/conf.d/default.conf +++ b/Docker/nginx/conf.d/default.conf @@ -23,6 +23,15 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } + + location /api-doc/ { + proxy_pass http://server:5000/api-doc/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } } server { @@ -48,4 +57,13 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } + + location /api-doc/ { + proxy_pass http://server:5000/api-doc/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } } diff --git a/Server/controllers/alertController.js b/Server/controllers/alertController.js deleted file mode 100644 index cd31db565..000000000 --- a/Server/controllers/alertController.js +++ /dev/null @@ -1,192 +0,0 @@ -const logger = require("../utils/logger"); -require("dotenv").config(); -const { - createAlertParamValidation, - createAlertBodyValidation, - getAlertsByUserIdParamValidation, - getAlertsByMonitorIdParamValidation, - getAlertByIdParamValidation, - editAlertParamValidation, - editAlertBodyValidation, - deleteAlertParamValidation, -} = require("../validation/joi"); -const { successMessages } = require("../utils/messages"); -const SERVICE_NAME = "alertController"; - -/** - * Creates an alert for a monitor - * @async - * @param {Express.Request} req - * @param {Express.Response} res - * @returns {Promise} - * @throws {Error} - */ -const createAlert = async (req, res, next) => { - try { - await createAlertParamValidation.validateAsync(req.params); - await createAlertBodyValidation.validateAsync(req.body); - } catch (error) { - error.status = 422; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - error.service = SERVICE_NAME; - next(error); - return; - } - - try { - const alertData = { ...req.body }; - const alert = await req.db.createAlert(alertData); - return res - .status(200) - .json({ success: true, msg: successMessages.ALERT_CREATE, data: alert }); - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "createAlert") : null; - next(error); - } -}; - -/** - * Gets all alerts for a user - * @async - * @param {Express.Request} req - * @param {Express.Response} res - * @returns {Promise} - * @throws {Error} - */ -const getAlertsByUserId = async (req, res, next) => { - try { - await getAlertsByUserIdParamValidation.validateAsync(req.params); - } catch (error) { - error.status = 422; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - error.service = SERVICE_NAME; - next(error); - return; - } - - try { - const alerts = await req.db.getAlertsByUserId(req.params.userId); - return res.status(200).json({ - success: true, - msg: successMessages.ALERT_GET_BY_USER, - data: alerts, - }); - } catch (error) { - error.service = SERVICE_NAME; - next(error); - } -}; - -const getAlertsByMonitorId = async (req, res, next) => { - try { - await getAlertsByMonitorIdParamValidation.validateAsync(req.params); - } catch (error) { - error.status = 422; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - error.service = SERVICE_NAME; - next(error); - return; - } - - try { - const alerts = await req.db.getAlertsByMonitorId(req.params.monitorId); - return res.status(200).json({ - success: true, - msg: successMessages.ALERT_GET_BY_MONITOR, - data: alerts, - }); - } catch (error) { - error.service = SERVICE_NAME; - next(error); - } -}; - -const getAlertById = async (req, res, next) => { - try { - await getAlertByIdParamValidation.validateAsync(req.params); - } catch (error) { - error.status = 422; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - error.service = SERVICE_NAME; - next(error); - return; - } - - try { - const alert = await req.db.getAlertById(req.params.alertId); - return res.status(200).json({ - success: true, - msg: successMessages.ALERT_GET_BY_ID, - data: alert, - }); - } catch (error) { - error.service = SERVICE_NAME; - next(error); - } -}; - -const editAlert = async (req, res, next) => { - try { - await editAlertParamValidation.validateAsync(req.params); - await editAlertBodyValidation.validateAsync(req.body); - } catch (error) { - console.log(error); - error.status = 422; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - error.service = SERVICE_NAME; - next(error); - return; - } - - try { - const alertData = { ...req.body }; - const alert = await req.db.editAlert(req.params.alertId, alertData); - return res.status(200).json({ - success: true, - msg: successMessages.ALERT_EDIT, - data: alert, - }); - } catch (error) { - error.service = SERVICE_NAME; - next(error); - } -}; -const deleteAlert = async (req, res, next) => { - try { - await deleteAlertParamValidation.validateAsync(req.params); - } catch (error) { - error.status = 422; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - error.service = SERVICE_NAME; - next(error); - return; - } - - try { - const deleted = await req.db.deleteAlert(req.params.alertId); - return res.status(200).json({ - success: true, - msg: successMessages.ALERT_DELETE, - data: deleted, - }); - } catch (error) { - error.service = SERVICE_NAME; - next(error); - } -}; - -module.exports = { - createAlert, - getAlertsByUserId, - getAlertsByMonitorId, - getAlertById, - editAlert, - deleteAlert, -}; diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index a3e245ef3..f39ffb019 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -49,23 +49,6 @@ const registerController = async (req, res, next) => { next(error); return; } - - // TODO Can there be more than one admin? - // // Check if an admin user exists, if so, error - // try { - // const admin = await req.db.checkAdmin(req, res); - // console.log(admin); - // if (admin === true) { - // throw new Error(errorMessages.AUTH_ADMIN_EXISTS); - // } - // } catch (error) { - // console.log("WEEEEEEE", error.message); - // error.service = SERVICE_NAME; - // error.status = 403; - // next(error); - // return; - // } - // Create a new user try { const newUser = await req.db.insertUser({ ...req.body }, req.file); diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index f5bd98b74..e534e0f60 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -44,48 +44,6 @@ 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) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined - ? (error.method = "getMonitorAggregateStats") - : null; - next(error); - } -}; - /** * Returns monitor stats for monitor with matching ID * @async @@ -516,7 +474,6 @@ const addDemoMonitors = async (req, res, next) => { module.exports = { getAllMonitors, - getMonitorAggregateStats, getMonitorStatsById, getMonitorCertificate, getMonitorById, diff --git a/Server/controllers/pageSpeedCheckController.js b/Server/controllers/pageSpeedCheckController.js deleted file mode 100644 index b07fac4d7..000000000 --- a/Server/controllers/pageSpeedCheckController.js +++ /dev/null @@ -1,129 +0,0 @@ -const PageSpeedService = require("../service/pageSpeedService"); -const { successMessages } = require("../utils/messages"); -const pageSpeedService = new PageSpeedService(); -const SERVICE_NAME = "pagespeedCheckController"; -const { - getPageSpeedCheckParamValidation, - createPageSpeedCheckParamValidation, - createPageSpeedCheckBodyValidation, - deletePageSpeedCheckParamValidation, -} = require("../validation/joi"); - -/** - * Gets all PageSpeedChecks for a monitor - * @async - * @param {Express.Request} req - The request object - * @param {Express.Response} res - The response object - * @param {Function} next - The next middleware function - * @returns {Promise} - */ -const getPageSpeedChecks = async (req, res, next) => { - try { - // Validate monitorId parameter - await getPageSpeedCheckParamValidation.validateAsync(req.params); - } catch (error) { - error.status = 422; - error.service = SERVICE_NAME; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - next(error); - return; - } - - try { - return res.status(200).json({ msg: "Hit getPageSpeedChecks" }); - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "getPageSpeedChecks") : null; - next(error); - } -}; - -/** - * Creates a new PageSpeedCheck for a monitor - * @async - * @param {Express.Request} req - The request object - * @param {Express.Response} res - The response object - * @param {Function} next - The next middleware function - * @returns {Promise} - */ -const createPageSpeedCheck = async (req, res, next) => { - try { - await createPageSpeedCheckParamValidation.validateAsync(req.params); - await createPageSpeedCheckBodyValidation.validateAsync(req.body); - } catch (error) { - error.status = 422; - error.service = SERVICE_NAME; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - next(error); - return; - } - - try { - const { monitorId } = req.params; - const { url } = req.body; - - // Run the PageSpeed check - const pageSpeedResults = await pageSpeedService.runPageSpeedCheck(url); - - // Extract categories scores - const categories = pageSpeedResults.lighthouseResult?.categories; - - if (!categories) { - throw new Error("Categories not found in PageSpeed results"); - } - - const newPageSpeedCheck = await pageSpeedService.createPageSpeedCheck({ - monitorId, - accessibility: (categories.accessibility?.score || 0) * 100, - bestPractices: (categories["best-practices"]?.score || 0) * 100, - seo: (categories.seo?.score || 0) * 100, - performance: (categories.performance?.score || 0) * 100, - }); - - return res.status(201).json({ - msg: successMessages.CREATED, - data: newPageSpeedCheck, - }); - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "createPageSpeedCheck") : null; - next(error); - } -}; - -/** - * Deletes all PageSpeedChecks for a monitor - * @async - * @param {Express.Request} req - The request object - * @param {Express.Response} res - The response object - * @param {Function} next - The next middleware function - * @returns {Promise} - */ -const deletePageSpeedCheck = async (req, res, next) => { - try { - // Validate monitorId parameter - await deletePageSpeedCheckParamValidation.validateAsync(req.params); - } catch (error) { - error.status = 422; - error.service = SERVICE_NAME; - error.message = - error.details?.[0]?.message || error.message || "Validation Error"; - next(error); - return; - } - try { - return res.status(200).json({ msg: "Hit deletePageSpeedCheck" }); - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "deletePageSpeedCheck") : null; - next(error); - } -}; - -module.exports = { - getPageSpeedChecks, - createPageSpeedCheck, - deletePageSpeedCheck, -}; diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js index 5960552c4..bbb3304ee 100644 --- a/Server/controllers/queueController.js +++ b/Server/controllers/queueController.js @@ -41,7 +41,7 @@ const addJob = async (req, res, next) => { const obliterateQueue = async (req, res, next) => { try { const obliterated = await req.jobQueue.obliterate(); - return res.status(200).send("Obliterated jobs"); + return res.status(200).send("Obliterated queue"); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "obliterateQueue") : null; diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js index ebc50f7da..c5fcff08c 100644 --- a/Server/db/mongo/MongoDB.js +++ b/Server/db/mongo/MongoDB.js @@ -100,20 +100,6 @@ const { updateChecksTTL, } = require("./modules/checkModule"); -//**************************************** -// Alerts -//**************************************** - -const { - createAlert, - getAlertsByUserId, - getAlertsByMonitorId, - getAlertById, - editAlert, - deleteAlert, - deleteAlertByMonitorId, -} = require("./modules/alertModule"); - //**************************************** // Maintenance Window //**************************************** @@ -163,13 +149,6 @@ module.exports = { deleteChecks, deleteChecksByTeamId, updateChecksTTL, - createAlert, - getAlertsByUserId, - getAlertsByMonitorId, - getAlertById, - editAlert, - deleteAlert, - deleteAlertByMonitorId, deleteMonitorsByUserId, createPageSpeedCheck, getPageSpeedChecks, diff --git a/Server/db/mongo/modules/alertModule.js b/Server/db/mongo/modules/alertModule.js deleted file mode 100644 index b9e65647b..000000000 --- a/Server/db/mongo/modules/alertModule.js +++ /dev/null @@ -1,140 +0,0 @@ -const Alert = require("../../../models/Alert"); - -/** - * Creates an Alert associated with a Monitor and User - * @async - * @param {Object} alertData - * @param {string} alertData.checkId - * @param {string} alert.monitorId - * @param {string} alert.userId - * @param {boolean} alert.status - * @param {string} alert.message - * @param {boolean} alert.notifiedStatus - * @param {boolean} alert.acknowledgeStatus - * @returns {>} - * @throws {Error} - */ -const createAlert = async (alertData) => { - try { - const alert = await new Alert({ ...alertData }).save(); - return alert; - } catch (error) { - throw error; - } -}; - -/** - * Gets all alerts a User has set - * @async - * @param {string} userId - * @returns {>} - * @throws {Error} - */ -const getAlertsByUserId = async (userId) => { - try { - const alerts = await Alert.find({ userId }); - return alerts; - } catch (error) { - throw error; - } -}; - -/** - * Gets all alerts a for an associated Monitor - * @async - * @param {string} monitorId - * @returns {>} - * @throws {Error} - */ -const getAlertsByMonitorId = async (monitorId) => { - try { - const alerts = await Alert.find({ monitorId }); - return alerts; - } catch (error) { - throw error; - } -}; - -/** - * Returns an alert with specified ID - * @async - * @param {string} alertId - * @returns {Promise} - * @throws {Error} - */ -const getAlertById = async (alertId) => { - try { - const alert = await Alert.findById(alertId); - return alert; - } catch (error) { - throw error; - } -}; - -/** - * Returns an edited alert with the specified ID - * @async - * @param {string} alertId - * @param {Object} alertData - * @param {string} alertData.checkId - * @param {string} alertData.monitorId - * @param {string} alertData.userId - * @param {boolean} alertData.status - * @param {string} alertData.message - * @param {boolean} alertData.notifiedStatus - * @param {boolean} alertData.acknowledgeStatus - * @param {Express.Response} res - * @returns {Promise>} - * @throws {Error} - */ -const editAlert = async (alertId, alertData) => { - try { - const editedAlert = await Alert.findByIdAndUpdate(alertId, alertData, { - new: true, - }); - return editedAlert; - } catch (error) { - throw error; - } -}; - -/** - * Deletes an alert with the specified ID - * @async - * @param {string} alertId - * @throws {Error} - */ -const deleteAlert = async (alertId) => { - try { - const result = await Alert.findByIdAndDelete(alertId); - return result; - } catch (error) { - throw error; - } -}; - -/** - * Deletes alerts by monitor ID. - * - * @param {string} monitorId - The ID of the monitor. - * @returns {Promise} A promise that resolves to the result of the delete operation. - * @throws {Error} If an error occurs while deleting the alerts. - */ -const deleteAlertByMonitorId = async (monitorId) => { - try { - const result = await Alert.deleteMany({ monitorId }); - return result; - } catch (error) { - throw error; - } -}; - -module.exports = { - createAlert, - getAlertsByUserId, - getAlertsByMonitorId, - getAlertById, - editAlert, - deleteAlert, - deleteAlertByMonitorId, -}; diff --git a/Server/index.js b/Server/index.js index 846b11e3d..05c9d3059 100644 --- a/Server/index.js +++ b/Server/index.js @@ -1,3 +1,7 @@ +const path = require("path"); +const fs = require("fs"); +const swaggerUi = require("swagger-ui-express"); + const express = require("express"); const helmet = require("helmet"); const cors = require("cors"); @@ -10,8 +14,6 @@ const authRouter = require("./routes/authRoute"); const inviteRouter = require("./routes/inviteRoute"); const monitorRouter = require("./routes/monitorRoute"); const checkRouter = require("./routes/checkRoute"); -const alertRouter = require("./routes/alertRoute"); -const pageSpeedCheckRouter = require("./routes/pageSpeedCheckRoute"); const maintenanceWindowRouter = require("./routes/maintenanceWindowRoute"); const { connectDbAndRunServer } = require("./configs/db"); @@ -21,8 +23,11 @@ const NetworkService = require("./service/networkService"); const EmailService = require("./service/emailService"); const PageSpeedService = require("./service/pageSpeedService"); const SERVICE_NAME = "Server"; -let cleaningUp = false; +let cleaningUp = false; +const openApiSpec = JSON.parse( + fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8") +); // Need to wrap server setup in a function to handle async nature of JobQueue const startApp = async () => { // ************************** @@ -70,16 +75,16 @@ const startApp = async () => { next(); }); + // Swagger UI + app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec)); + //routes app.use("/api/v1/auth", authRouter); app.use("/api/v1/invite", inviteRouter); app.use("/api/v1/monitors", verifyJWT, monitorRouter); app.use("/api/v1/checks", verifyJWT, checkRouter); - app.use("/api/v1/alerts", verifyJWT, alertRouter); - app.use("/api/v1/pagespeed", verifyJWT, pageSpeedCheckRouter); app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRouter); - //Temporary route for testing, remove later - app.use("/api/v1/job", queueRouter); + app.use("/api/v1/queue", verifyJWT, queueRouter); //health check app.use("/api/v1/healthy", (req, res) => { diff --git a/Server/models/Alert.js b/Server/models/Alert.js deleted file mode 100644 index 758a7eba2..000000000 --- a/Server/models/Alert.js +++ /dev/null @@ -1,38 +0,0 @@ -const mongoose = require('mongoose') - -const AlertSchema = mongoose.Schema( - { - checkId: { - type: mongoose.Schema.Types.ObjectId, - ref: 'Check', - immutable: true, - }, - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: 'Monitor', - immutable: true, - }, - userId: { - type: mongoose.Schema.Types.ObjectId, - ref: 'User', - immutable: true, - }, - status: { - type: Boolean, - }, - message: { - type: String, - }, - notifiedStatus: { - type: Boolean, - }, - acknowledgeStatus: { - type: Boolean, - } - }, - { - timestamps: true, - } -); - -module.exports = mongoose.model('Alert', AlertSchema); diff --git a/Server/openapi.json b/Server/openapi.json index 23c16521d..6a19bde13 100644 --- a/Server/openapi.json +++ b/Server/openapi.json @@ -50,25 +50,13 @@ "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" + "name": "queue", + "description": "Queue" } ], "paths": { @@ -81,7 +69,14 @@ "application/json": { "schema": { "type": "object", - "required": ["firstName", "lastName", "email", "password", "role", "teamId"], + "required": [ + "firstName", + "lastName", + "email", + "password", + "role", + "teamId" + ], "properties": { "firstName": { "type": "string" @@ -103,7 +98,7 @@ }, "role": { "type": "array", - "enum": [["user"], ["admin"], ["superadmin"]], + "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]], "default": ["superadmin"] }, "teamId": { @@ -121,7 +116,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -179,7 +174,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -207,17 +202,37 @@ } } }, - "/auth/user/:userId": { + "/auth/user/{userId}": { "put": { "tags": ["auth"], - "description": "Change user informations", + "description": "Change user information", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserUpdateRequest" + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -242,18 +257,33 @@ } } } - } + }, + "security": [ + { + "bearerAuth": [] + } + ] }, "delete": { "tags": ["auth"], "description": "Delete user", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -278,10 +308,15 @@ } } } - } + }, + "security": [ + { + "bearerAuth": [] + } + ] } }, - "/auth/users/admin": { + "/auth/users/superadmin": { "get": { "tags": ["auth"], "description": "Checks to see if an admin account exists", @@ -291,7 +326,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -316,7 +351,12 @@ } } } - } + }, + "security": [ + { + "bearerAuth": [] + } + ] } }, "/auth/users": { @@ -329,7 +369,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -354,96 +394,41 @@ } } } - } - } - }, - "/auth/invite": { - "post": { - "tags": ["auth"], - "description": "Invite people to application", - "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" - } - } - } + }, + "security": [ + { + "bearerAuth": [] } - } - } - }, - "/auth/invite/verify": { - "post": { - "tags": ["auth"], - "description": "Verify the invite", - "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/recovery/request": { "post": { "tags": ["auth"], "description": "Request a recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email"], + "properties": { + "email": { + "type": "string", + "format": "email" + } + } + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -475,13 +460,28 @@ "post": { "tags": ["auth"], "description": "Validate recovery token", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["recoveryToken"], + "properties": { + "recoveryToken": { + "type": "string" + } + } + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -513,13 +513,31 @@ "post": { "tags": ["auth"], "description": "Password reset", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["recoveryToken", "password"], + "properties": { + "recoveryToken": { + "type": "string" + }, + "password": { + "type": "string" + } + } + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSuccessResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -547,22 +565,45 @@ } } }, - "/healthy": { - "get": { - "tags": ["healthy"], - "description": "Health check", + "/invite": { + "post": { + "tags": ["invite"], + "description": "Request an invitation", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email", "role"], + "properties": { + "email": { + "type": "string" + }, + "role": { + "type": "array" + } + } + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -572,27 +613,1456 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/invite/verify": { + "post": { + "tags": ["invite"], + "description": "Request an invitation", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["token"], + "properties": { + "token": { + "type": "string" } } } } } - } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors": { + "get": { + "tags": ["monitors"], + "description": "Get all monitors", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["monitors"], + "description": "Create a new monitor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMonitorBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["monitors"], + "description": "Delete all monitors", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/{monitorId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitor by id", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "put": { + "tags": ["monitors"], + "description": "Update monitor by id", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMonitorBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["monitors"], + "description": "Delete monitor by id", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/stats/{monitorId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitor stats", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/certificate/{monitorId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitor certificate", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/team/summary/{teamId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitors and summary by teamId", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "required": false, + "schema": { + "type": "array", + "enum": ["http", "ping", "pagespeed"] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/team/{teamId}": { + "get": { + "tags": ["monitors"], + "description": "Get monitors by teamId", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "status", + "description": "Status of monitor, true for up, false for down", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "checkOrder", + "description": "Order of checks", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["asc", "desc"] + } + }, + { + "name": "limit", + "description": "Number of checks to return with monitor", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "type", + "description": "Type of monitor", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed"] + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "rowsPerPage", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "filter", + "description": "Value to filter by", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "field", + "description": "Field to filter on", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "order", + "description": "Sort order of results", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["http", "ping", "pagespeed"] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/pause/{monitorId}": { + "post": { + "tags": ["monitors"], + "description": "Pause monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/monitors/demo": { + "post": { + "tags": ["monitors"], + "description": "Create a demo monitor", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMonitorBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/checks/{monitorId}": { + "get": { + "tags": ["checks"], + "description": "Get all checks for a monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["checks"], + "description": "Create a new check", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCheckBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["checks"], + "description": "Delete all checks for a monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/checks/team/{teamId}": { + "get": { + "tags": ["checks"], + "description": "Get all checks for a team", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": ["checks"], + "description": "Delete all checks for a team", + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/checks/team/ttl": { + "put": { + "tags": ["checks"], + "description": "Update check TTL", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCheckTTLBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/maintenance-window/monitor/{monitorId}": { + "get": { + "tags": ["maintenance-window"], + "description": "Get maintenance window for monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["maintenance-window"], + "description": "Create maintenance window for monitor", + "parameters": [ + { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMaintenanceWindowBody" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/maintenance-window/user/{userId}": { + "get": { + "tags": ["maintenance-window"], + "description": "Get maintenance window for user", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/queue/jobs": { + "get": { + "tags": ["queue"], + "description": "Get all jobs in queue", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "post": { + "tags": ["queue"], + "description": "Create a new job. Useful for testing scaling workers", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/queue/metrics": { + "get": { + "tags": ["queue"], + "description": "Get queue metrics", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/queue/obliterate": { + "post": { + "tags": ["queue"], + "description": "Obliterate job queue", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "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" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] } } }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, "schemas": { "ErrorResponse": { "type": "object", "properties": { "success": { - "type": "boolean" + "type": "boolean", + "default": false }, "msg": { "type": "string" @@ -603,7 +2073,8 @@ "type": "object", "properties": { "success": { - "type": "boolean" + "type": "boolean", + "default": true }, "msg": { "type": "string" @@ -613,128 +2084,68 @@ } } }, - "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": { + "UserUpdateRequest": { "type": "object", "required": [ - "firstname", - "lastname", + "firstName", + "lastName", "email", - "profilePicUrl", - "isActive", - "isVerified", - "updatedAt", - "createdAt" + "password", + "role", + "teamId" ], "properties": { - "firstname": { + "firstName": { "type": "string" }, - "lastname": { + "lastName": { "type": "string" }, - "email": { + "password": { "type": "string", - "format": "email" + "format": "password" }, - "profilePicUrl": { - "type": "string" + "newPassword": { + "type": "string", + "format": "password" }, - "isActive": { + "profileImage": { + "type": "file", + "format": "file" + }, + "role": { + "type": "array", + "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]], + "default": ["superadmin"] + }, + "deleteProfileImage": { "type": "boolean" - }, - "isVerified": { - "type": "boolean" - }, - "updatedAt": { - "type": "string" - }, - "createdAt": { - "type": "string" } } }, - "MonitorDataType": { + "CreateMonitorBody": { "type": "object", - "required": [ - "userId", - "name", - "description", - "url", - "isActive", - "interval", - "updatedAt", - "createdAt" - ], + "required": ["userId", "teamId", "name", "description", "type", "url"], "properties": { + "_id": { + "type": "string" + }, "userId": { "type": "string" }, + "teamId": { + "type": "string" + }, "name": { "type": "string" }, "description": { "type": "string" }, + "type": { + "type": "string", + "enum": ["http", "ping", "pagespeed"] + }, "url": { "type": "string" }, @@ -744,31 +2155,49 @@ "interval": { "type": "integer" }, - "updatedAt": { - "type": "string" - }, - "createdAt": { - "type": "string" + "notifications": { + "type": "array", + "items": { + "type": "string" + } } } }, - "CheckDataType": { + "UpdateMonitorBody": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "interval": { + "type": "integer" + }, + "notifications": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateCheckBody": { "type": "object", "required": [ "monitorId", "status", "responseTime", "statusCode", - "message", - "updatedAt", - "createdAt" + "message" ], "properties": { "monitorId": { "type": "string" }, "status": { - "type": "string" + "type": "boolean" }, "responseTime": { "type": "integer" @@ -778,55 +2207,42 @@ }, "message": { "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "createdAt": { - "type": "string" } } }, - "AlertDataType": { + "UpdateCheckTTLBody": { "type": "object", - "required": [ - "checkId", - "monitorId", - "userId", - "status", - "message", - "notifiedStatus", - "acknowledgedStatus", - "updatedAt", - "createdAt" - ], + "required": ["ttl"], + "properties": { + "ttl": { + "type": "integer" + } + } + }, + "CreateMaintenanceWindowBody": { + "type": "object", + "required": ["userId", "active", "oneTime", "start", "end"], "properties": { - "checkId": { - "type": "string" - }, - "monitorId": { - "type": "string" - }, "userId": { "type": "string" }, - "status": { - "type": "string" - }, - "message": { - "type": "string" - }, - "notifiedStatus": { + "active": { "type": "boolean" }, - "acknowledgedStatus": { + "oneTime": { "type": "boolean" }, - "updatedAt": { - "type": "string" + "start": { + "type": "string", + "format": "date-time" }, - "createdAt": { - "type": "string" + "end": { + "type": "string", + "format": "date-time" + }, + "expiry": { + "type": "string", + "format": "date-time" } } } diff --git a/Server/routes/alertRoute.js b/Server/routes/alertRoute.js deleted file mode 100644 index 434c0495a..000000000 --- a/Server/routes/alertRoute.js +++ /dev/null @@ -1,44 +0,0 @@ -const router = require("express").Router(); -const alertController = require("../controllers/alertController"); -const { verifyOwnership } = require("../middleware/verifyOwnership"); -const Alert = require("../models/Alert"); -const Monitor = require("../models/Monitor"); - -// Create alert -router.post( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - alertController.createAlert -); -// Get all alerts for a user -router.get( - "/user/:userId", - verifyOwnership(Monitor, "monitorId"), - alertController.getAlertsByUserId -); -// Get all alerts for a monitor -router.get( - "/monitor/:monitorId", - verifyOwnership(Monitor, "monitorId"), - alertController.getAlertsByMonitorId -); -// Get a single alert -router.get( - "/:alertId", - verifyOwnership(Monitor, "monitorId"), - alertController.getAlertById -); -// Edit -router.put( - "/:alertId", - verifyOwnership(Monitor, "monitorId"), - alertController.editAlert -); -//Delete -router.delete( - "/:alertId", - verifyOwnership(Monitor, "monitorId"), - alertController.deleteAlert -); - -module.exports = router; diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index a26e9eb94..fe99e7271 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -4,22 +4,20 @@ const { verifyOwnership } = require("../middleware/verifyOwnership"); const { isAllowed } = require("../middleware/isAllowed"); const Monitor = require("../models/Monitor"); +router.get("/:monitorId", checkController.getChecks); router.post( "/:monitorId", verifyOwnership(Monitor, "monitorId"), checkController.createCheck ); - -router.get("/:monitorId", checkController.getChecks); - -router.get("/team/:teamId", checkController.getTeamChecks); - router.delete( "/:monitorId", verifyOwnership(Monitor, "monitorId"), checkController.deleteChecks ); +router.get("/team/:teamId", checkController.getTeamChecks); + router.delete( "/team/:teamId", isAllowed(["admin", "superadmin"]), @@ -27,7 +25,7 @@ router.delete( ); router.put( - "/ttl", + "/team/ttl", isAllowed(["admin", "superadmin"]), checkController.updateChecksTTL ); diff --git a/Server/routes/maintenanceWindowRoute.js b/Server/routes/maintenanceWindowRoute.js index 876e9e668..c357df76b 100644 --- a/Server/routes/maintenanceWindowRoute.js +++ b/Server/routes/maintenanceWindowRoute.js @@ -3,8 +3,14 @@ const maintenanceWindowController = require("../controllers/maintenanceWindowCon const { verifyOwnership } = require("../middleware/verifyOwnership"); const Monitor = require("../models/Monitor"); +router.get( + "/monitor/:monitorId", + verifyOwnership(Monitor, "monitorId"), + maintenanceWindowController.getMaintenanceWindowsByMonitorId +); + router.post( - "/:monitorId", + "/monitor/:monitorId", verifyOwnership(Monitor, "monitorId"), maintenanceWindowController.createMaintenanceWindow ); @@ -14,10 +20,4 @@ router.get( maintenanceWindowController.getMaintenanceWindowsByUserId ); -router.get( - "/monitor/:monitorId", - verifyOwnership(Monitor, "monitorId"), - maintenanceWindowController.getMaintenanceWindowsByMonitorId -); - module.exports = router; diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index c6bec5f0f..aaa9c10e9 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -3,7 +3,6 @@ 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/routes/pageSpeedCheckRoute.js b/Server/routes/pageSpeedCheckRoute.js deleted file mode 100644 index 4b1de49bc..000000000 --- a/Server/routes/pageSpeedCheckRoute.js +++ /dev/null @@ -1,45 +0,0 @@ -const router = require("express").Router(); -const { verifyOwnership } = require("../middleware/verifyOwnership"); -const Monitor = require("../models/Monitor"); -const pageSpeedCheckController = require("../controllers/pageSpeedCheckController"); - -/** - * @route POST /:monitorId - * @description Create a new PageSpeedCheck for a monitor - * @access Private - * @param {string} monitorId - The ID of the monitor - * @middleware verifyOwnership - Ensures the user owns the monitor - */ -router.post( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - pageSpeedCheckController.createPageSpeedCheck -); - -/** - * @route GET /:monitorId - * @description Get all PageSpeedChecks for a monitor - * @access Private - * @param {string} monitorId - The ID of the monitor - * @middleware verifyOwnership - Ensures the user owns the monitor - */ -router.get( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - pageSpeedCheckController.getPageSpeedChecks -); - -/** - * @route DELETE /:monitorId - * @description Delete all PageSpeedChecks for a monitor - * @access Private - * @param {string} monitorId - The ID of the monitor - * @middleware verifyOwnership - Ensures the user owns the monitor - */ -router.delete( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - pageSpeedCheckController.deletePageSpeedCheck -); - -module.exports = router; diff --git a/Server/routes/queueRoute.js b/Server/routes/queueRoute.js index cbc68df6d..b14eaa2b0 100644 --- a/Server/routes/queueRoute.js +++ b/Server/routes/queueRoute.js @@ -4,10 +4,10 @@ const queueController = require("../controllers/queueController"); router.get("/metrics", queueController.getMetrics); // Get Jobs -router.get("/", queueController.getJobs); +router.get("/jobs", queueController.getJobs); // Add Job -router.post("/", queueController.addJob); +router.post("/jobs", queueController.addJob); // Obliterate Queue router.post("/obliterate", queueController.obliterateQueue); diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 5d58dd4a4..5a6d3e8e0 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -240,12 +240,12 @@ class JobQueue { */ async addJob(jobName, payload) { try { - console.log("Adding job", payload.url); + console.log("Adding job", payload?.url ?? "No URL"); // Execute job immediately await this.queue.add(jobName, payload); await this.queue.add(jobName, payload, { repeat: { - every: payload.interval, + every: payload?.interval ?? 60000, }, }); const workerStats = await this.getWorkerStats(); diff --git a/Server/validation/joi.js b/Server/validation/joi.js index dc82dc73e..f61432595 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -137,7 +137,6 @@ const inviteBodyValidation = joi.object({ "string.email": "Must be a valid email address", }), role: joi.array().required(), - teamId: joi.string().required(), }); const inviteVerifciationBodyValidation = joi.object({ @@ -236,13 +235,6 @@ 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 //**************************************** @@ -408,8 +400,6 @@ module.exports = { getMonitorStatsByIdParamValidation, getMonitorStatsByIdQueryValidation, getCertificateParamValidation, - getMonitorAggregateStatsParamValidation, - getMonitorAggregateStatsQueryValidation, editMonitorBodyValidation, pauseMonitorParamValidation, editUserParamValidation,