From 1383d8b59acb7db9852c410b3838d15cdeb7d693 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 23 Sep 2025 14:12:17 -0700 Subject: [PATCH 1/2] add workflow --- .github/workflows/check-build.yml | 23 +++++++++++++++++++ ...s => 0001_migrateStatusWindowThreshold.ts} | 0 .../db/mongo/migration/{index.js => index.ts} | 0 server/tsconfig.json | 2 +- 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/check-build.yml rename server/src/db/mongo/migration/{0001_migrateStatusWindowThreshold.js => 0001_migrateStatusWindowThreshold.ts} (100%) rename server/src/db/mongo/migration/{index.js => index.ts} (100%) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml new file mode 100644 index 000000000..8417b823f --- /dev/null +++ b/.github/workflows/check-build.yml @@ -0,0 +1,23 @@ +name: Build Check ( Server) +on: + pull_request: + workflow_dispatch: + +jobs: + build-server: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install server dependencies + working-directory: server + run: npm install + + - name: Check server build + working-directory: server + run: npm run build diff --git a/server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.js b/server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.ts similarity index 100% rename from server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.js rename to server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.ts diff --git a/server/src/db/mongo/migration/index.js b/server/src/db/mongo/migration/index.ts similarity index 100% rename from server/src/db/mongo/migration/index.js rename to server/src/db/mongo/migration/index.ts diff --git a/server/tsconfig.json b/server/tsconfig.json index 3aa773ab1..260ccb964 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -22,5 +22,5 @@ "allowJs": true, "checkJs": false }, - "exclude": ["node_modules", "dist", "**/*.config.js"] + "exclude": ["node_modules", "dist/", "**/*.config.js"] } From b3b6f1d834ce64623d3c9a4c9953de79b633db62 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 23 Sep 2025 14:23:45 -0700 Subject: [PATCH 2/2] move all v1 files to v1 dirs --- server/src/app.js | 10 +- server/src/config/routes.js | 4 +- server/src/config/services.js | 78 +-- server/src/controllers/v1/baseController.js | 2 +- .../src/controllers/v2/MonitorController.ts | 157 ++++++ server/src/db/{mongo => v1}/MongoDB.js | 2 +- .../0001_migrateStatusWindowThreshold.ts | 2 +- .../src/db/{mongo => v1}/migration/index.ts | 0 server/src/db/{ => v1}/models/AppSettings.js | 0 server/src/db/{ => v1}/models/Check.js | 0 server/src/db/{ => v1}/models/InviteToken.js | 0 .../db/{ => v1}/models/MaintenanceWindow.js | 0 server/src/db/{ => v1}/models/Monitor.js | 0 server/src/db/{ => v1}/models/MonitorStats.js | 0 server/src/db/{ => v1}/models/Notification.js | 0 .../src/db/{ => v1}/models/RecoveryToken.js | 0 server/src/db/{ => v1}/models/StatusPage.js | 0 server/src/db/{ => v1}/models/Team.js | 0 server/src/db/{ => v1}/models/User.js | 1 - .../db/{mongo => v1}/modules/checkModule.js | 0 .../modules/checkModuleQueries.js | 0 .../db/{mongo => v1}/modules/inviteModule.js | 0 .../modules/maintenanceWindowModule.js | 0 .../db/{mongo => v1}/modules/monitorModule.js | 0 .../modules/monitorModuleQueries.js | 0 .../modules/notificationModule.js | 0 .../{mongo => v1}/modules/recoveryModule.js | 0 .../{mongo => v1}/modules/settingsModule.js | 0 .../{mongo => v1}/modules/statusPageModule.js | 0 .../db/{mongo => v1}/modules/userModule.js | 0 server/src/db/{mongo => v1}/utils/seedDb.js | 4 +- server/src/index.js | 4 +- .../src/middleware/{ => v1}/handleErrors.js | 6 +- server/src/middleware/{ => v1}/isAllowed.js | 6 +- .../middleware/{ => v1}/languageMiddleware.js | 2 +- server/src/middleware/{ => v1}/rateLimiter.js | 0 .../middleware/{ => v1}/responseHandler.js | 0 .../src/middleware/{ => v1}/sanitization.js | 0 server/src/middleware/{ => v1}/verifyJWT.js | 6 +- .../middleware/{ => v1}/verifyOwnership.js | 6 +- .../middleware/{ => v1}/verifySuperAdmin.js | 2 +- .../middleware/{ => v1}/verifyTeamAccess.js | 0 server/src/routes/v1/authRoute.js | 4 +- server/src/routes/v1/checkRoute.js | 2 +- server/src/routes/v1/diagnosticRoute.js | 4 +- server/src/routes/v1/inviteRoute.js | 4 +- server/src/routes/v1/logRoutes.js | 2 +- .../src/routes/v1/maintenanceWindowRoute.js | 2 +- server/src/routes/v1/monitorRoute.js | 2 +- server/src/routes/v1/queueRoute.js | 2 +- server/src/routes/v1/settingsRoute.js | 2 +- server/src/routes/v1/statusPageRoute.js | 2 +- .../service/{ => v1}/business/checkService.js | 0 .../{ => v1}/business/diagnosticService.js | 0 .../{ => v1}/business/inviteService.js | 0 .../business/maintenanceWindowService.js | 0 .../{ => v1}/business/monitorService.js | 2 +- .../service/{ => v1}/business/userService.js | 0 .../SuperSimpleQueue/SuperSimpleQueue.js | 0 .../SuperSimpleQueueHelper.js | 10 +- .../{ => v1}/infrastructure/bufferService.js | 0 .../{ => v1}/infrastructure/emailService.js | 0 .../{ => v1}/infrastructure/errorService.js | 0 .../{ => v1}/infrastructure/networkService.js | 0 .../infrastructure/notificationService.js | 0 .../infrastructure/notificationUtils.js | 0 .../{ => v1}/infrastructure/statusService.js | 2 +- .../{ => v1}/system/serviceRegistry.js | 0 .../{ => v1}/system/settingsService.js | 0 .../service/{ => v1}/system/stringService.js | 0 .../{ => v1}/system/translationService.js | 0 .../src/service/v2/business/CheckService.ts | 132 +++++ .../src/service/v2/business/MonitorService.ts | 464 ++++++++++++++++++ 73 files changed, 840 insertions(+), 88 deletions(-) create mode 100644 server/src/controllers/v2/MonitorController.ts rename server/src/db/{mongo => v1}/MongoDB.js (97%) rename server/src/db/{mongo => v1}/migration/0001_migrateStatusWindowThreshold.ts (92%) rename server/src/db/{mongo => v1}/migration/index.ts (100%) rename server/src/db/{ => v1}/models/AppSettings.js (100%) rename server/src/db/{ => v1}/models/Check.js (100%) rename server/src/db/{ => v1}/models/InviteToken.js (100%) rename server/src/db/{ => v1}/models/MaintenanceWindow.js (100%) rename server/src/db/{ => v1}/models/Monitor.js (100%) rename server/src/db/{ => v1}/models/MonitorStats.js (100%) rename server/src/db/{ => v1}/models/Notification.js (100%) rename server/src/db/{ => v1}/models/RecoveryToken.js (100%) rename server/src/db/{ => v1}/models/StatusPage.js (100%) rename server/src/db/{ => v1}/models/Team.js (100%) rename server/src/db/{ => v1}/models/User.js (98%) rename server/src/db/{mongo => v1}/modules/checkModule.js (100%) rename server/src/db/{mongo => v1}/modules/checkModuleQueries.js (100%) rename server/src/db/{mongo => v1}/modules/inviteModule.js (100%) rename server/src/db/{mongo => v1}/modules/maintenanceWindowModule.js (100%) rename server/src/db/{mongo => v1}/modules/monitorModule.js (100%) rename server/src/db/{mongo => v1}/modules/monitorModuleQueries.js (100%) rename server/src/db/{mongo => v1}/modules/notificationModule.js (100%) rename server/src/db/{mongo => v1}/modules/recoveryModule.js (100%) rename server/src/db/{mongo => v1}/modules/settingsModule.js (100%) rename server/src/db/{mongo => v1}/modules/statusPageModule.js (100%) rename server/src/db/{mongo => v1}/modules/userModule.js (100%) rename server/src/db/{mongo => v1}/utils/seedDb.js (95%) rename server/src/middleware/{ => v1}/handleErrors.js (71%) rename server/src/middleware/{ => v1}/isAllowed.js (87%) rename server/src/middleware/{ => v1}/languageMiddleware.js (93%) rename server/src/middleware/{ => v1}/rateLimiter.js (100%) rename server/src/middleware/{ => v1}/responseHandler.js (100%) rename server/src/middleware/{ => v1}/sanitization.js (100%) rename server/src/middleware/{ => v1}/verifyJWT.js (87%) rename server/src/middleware/{ => v1}/verifyOwnership.js (88%) rename server/src/middleware/{ => v1}/verifySuperAdmin.js (97%) rename server/src/middleware/{ => v1}/verifyTeamAccess.js (100%) rename server/src/service/{ => v1}/business/checkService.js (100%) rename server/src/service/{ => v1}/business/diagnosticService.js (100%) rename server/src/service/{ => v1}/business/inviteService.js (100%) rename server/src/service/{ => v1}/business/maintenanceWindowService.js (100%) rename server/src/service/{ => v1}/business/monitorService.js (99%) rename server/src/service/{ => v1}/business/userService.js (100%) rename server/src/service/{ => v1}/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js (100%) rename server/src/service/{ => v1}/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js (90%) rename server/src/service/{ => v1}/infrastructure/bufferService.js (100%) rename server/src/service/{ => v1}/infrastructure/emailService.js (100%) rename server/src/service/{ => v1}/infrastructure/errorService.js (100%) rename server/src/service/{ => v1}/infrastructure/networkService.js (100%) rename server/src/service/{ => v1}/infrastructure/notificationService.js (100%) rename server/src/service/{ => v1}/infrastructure/notificationUtils.js (100%) rename server/src/service/{ => v1}/infrastructure/statusService.js (99%) rename server/src/service/{ => v1}/system/serviceRegistry.js (100%) rename server/src/service/{ => v1}/system/settingsService.js (100%) rename server/src/service/{ => v1}/system/stringService.js (100%) rename server/src/service/{ => v1}/system/translationService.js (100%) create mode 100644 server/src/service/v2/business/CheckService.ts create mode 100644 server/src/service/v2/business/MonitorService.ts diff --git a/server/src/app.js b/server/src/app.js index 9cabda6b6..253baee88 100644 --- a/server/src/app.js +++ b/server/src/app.js @@ -1,16 +1,16 @@ import express from "express"; import path from "path"; -import { responseHandler } from "./middleware/responseHandler.js"; +import { responseHandler } from "./middleware/v1/responseHandler.js"; import cors from "cors"; import helmet from "helmet"; import compression from "compression"; import cookieParser from "cookie-parser"; -import languageMiddleware from "./middleware/languageMiddleware.js"; +import languageMiddleware from "./middleware/v1/languageMiddleware.js"; import swaggerUi from "swagger-ui-express"; -import { handleErrors } from "./middleware/handleErrors.js"; +import { handleErrors } from "./middleware/v1/handleErrors.js"; import { setupRoutes } from "./config/routes.js"; -import { generalApiLimiter } from "./middleware/rateLimiter.js"; -import { sanitizeBody, sanitizeQuery } from "./middleware/sanitization.js"; +import { generalApiLimiter } from "./middleware/v1/rateLimiter.js"; +import { sanitizeBody, sanitizeQuery } from "./middleware/v1/sanitization.js"; export const createApp = ({ services, controllers, envSettings, frontendPath, openApiSpec }) => { const allowedOrigin = envSettings.clientHost; diff --git a/server/src/config/routes.js b/server/src/config/routes.js index 2ec93a2dc..40bf8e994 100644 --- a/server/src/config/routes.js +++ b/server/src/config/routes.js @@ -1,5 +1,5 @@ -import { verifyJWT } from "../middleware/verifyJWT.js"; -import { authApiLimiter } from "../middleware/rateLimiter.js"; +import { verifyJWT } from "../middleware/v1/verifyJWT.js"; +import { authApiLimiter } from "../middleware/v1/rateLimiter.js"; import AuthRoutes from "../routes/v1/authRoute.js"; import InviteRoutes from "../routes/v1//inviteRoute.js"; diff --git a/server/src/config/services.js b/server/src/config/services.js index 08c2525f3..22f978071 100644 --- a/server/src/config/services.js +++ b/server/src/config/services.js @@ -1,22 +1,22 @@ -import ServiceRegistry from "../service/system/serviceRegistry.js"; -import TranslationService from "../service/system/translationService.js"; -import StringService from "../service/system/stringService.js"; -import MongoDB from "../db/mongo/MongoDB.js"; -import NetworkService from "../service/infrastructure/networkService.js"; -import EmailService from "../service/infrastructure/emailService.js"; -import BufferService from "../service/infrastructure/bufferService.js"; -import StatusService from "../service/infrastructure/statusService.js"; -import NotificationUtils from "../service/infrastructure/notificationUtils.js"; -import NotificationService from "../service/infrastructure/notificationService.js"; -import ErrorService from "../service/infrastructure/errorService.js"; -import SuperSimpleQueueHelper from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; -import SuperSimpleQueue from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js"; -import UserService from "../service/business/userService.js"; -import CheckService from "../service/business/checkService.js"; -import DiagnosticService from "../service/business/diagnosticService.js"; -import InviteService from "../service/business/inviteService.js"; -import MaintenanceWindowService from "../service/business/maintenanceWindowService.js"; -import MonitorService from "../service/business/monitorService.js"; +import ServiceRegistry from "../service/v1/system/serviceRegistry.js"; +import TranslationService from "../service/v1/system/translationService.js"; +import StringService from "../service/v1/system/stringService.js"; +import MongoDB from "../db/v1/MongoDB.js"; +import NetworkService from "../service/v1/infrastructure/networkService.js"; +import EmailService from "../service/v1/infrastructure/emailService.js"; +import BufferService from "../service/v1/infrastructure/bufferService.js"; +import StatusService from "../service/v1/infrastructure/statusService.js"; +import NotificationUtils from "../service/v1/infrastructure/notificationUtils.js"; +import NotificationService from "../service/v1/infrastructure/notificationService.js"; +import ErrorService from "../service/v1/infrastructure/errorService.js"; +import SuperSimpleQueueHelper from "../service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; +import SuperSimpleQueue from "../service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js"; +import UserService from "../service/v1/business/userService.js"; +import CheckService from "../service/v1/business/checkService.js"; +import DiagnosticService from "../service/v1/business/diagnosticService.js"; +import InviteService from "../service/v1/business/inviteService.js"; +import MaintenanceWindowService from "../service/v1/business/maintenanceWindowService.js"; +import MonitorService from "../service/v1/business/monitorService.js"; import papaparse from "papaparse"; import axios from "axios"; import got from "got"; @@ -46,27 +46,27 @@ import { GenerateAvatarImage } from "../utils/imageProcessing.js"; import { ParseBoolean } from "../utils/utils.js"; // Models -import Check from "../db/models/Check.js"; -import Monitor from "../db/models/Monitor.js"; -import User from "../db/models/User.js"; -import InviteToken from "../db/models/InviteToken.js"; -import StatusPage from "../db/models/StatusPage.js"; -import Team from "../db/models/Team.js"; -import MaintenanceWindow from "../db/models/MaintenanceWindow.js"; -import MonitorStats from "../db/models/MonitorStats.js"; -import Notification from "../db/models/Notification.js"; -import RecoveryToken from "../db/models/RecoveryToken.js"; -import AppSettings from "../db/models/AppSettings.js"; +import Check from "../db/v1/models/Check.js"; +import Monitor from "../db/v1/models/Monitor.js"; +import User from "../db/v1/models/User.js"; +import InviteToken from "../db/v1/models/InviteToken.js"; +import StatusPage from "../db/v1/models/StatusPage.js"; +import Team from "../db/v1/models/Team.js"; +import MaintenanceWindow from "../db/v1/models/MaintenanceWindow.js"; +import MonitorStats from "../db/v1/models/MonitorStats.js"; +import Notification from "../db/v1/models/Notification.js"; +import RecoveryToken from "../db/v1/models/RecoveryToken.js"; +import AppSettings from "../db/v1/models/AppSettings.js"; -import InviteModule from "../db/mongo/modules/inviteModule.js"; -import CheckModule from "../db/mongo/modules/checkModule.js"; -import StatusPageModule from "../db/mongo/modules/statusPageModule.js"; -import UserModule from "../db/mongo/modules/userModule.js"; -import MaintenanceWindowModule from "../db/mongo/modules/maintenanceWindowModule.js"; -import MonitorModule from "../db/mongo/modules/monitorModule.js"; -import NotificationModule from "../db/mongo/modules/notificationModule.js"; -import RecoveryModule from "../db/mongo/modules/recoveryModule.js"; -import SettingsModule from "../db/mongo/modules/settingsModule.js"; +import InviteModule from "../db/v1/modules/inviteModule.js"; +import CheckModule from "../db/v1/modules/checkModule.js"; +import StatusPageModule from "../db/v1/modules/statusPageModule.js"; +import UserModule from "../db/v1/modules//userModule.js"; +import MaintenanceWindowModule from "../db/v1/modules/maintenanceWindowModule.js"; +import MonitorModule from "../db/v1/modules/monitorModule.js"; +import NotificationModule from "../db/v1/modules/notificationModule.js"; +import RecoveryModule from "../db/v1/modules/recoveryModule.js"; +import SettingsModule from "../db/v1/modules/settingsModule.js"; export const initializeServices = async ({ logger, envSettings, settingsService }) => { const serviceRegistry = new ServiceRegistry({ logger }); diff --git a/server/src/controllers/v1/baseController.js b/server/src/controllers/v1/baseController.js index 314905c82..e8612cec5 100644 --- a/server/src/controllers/v1/baseController.js +++ b/server/src/controllers/v1/baseController.js @@ -1,4 +1,4 @@ -import { AppError } from "../../service/infrastructure/errorService.js"; +import { AppError } from "../../service/v1/infrastructure/errorService.js"; export const createCommonDependencies = (db, errorService, logger, stringService) => { return { diff --git a/server/src/controllers/v2/MonitorController.ts b/server/src/controllers/v2/MonitorController.ts new file mode 100644 index 000000000..6493b7f44 --- /dev/null +++ b/server/src/controllers/v2/MonitorController.ts @@ -0,0 +1,157 @@ +import { Request, Response, NextFunction } from "express"; +import ApiError from "../../utils/ApiError.js"; +import MonitorService from "../../service/v2/business/MonitorService.js"; +import { MonitorType } from "../../db/v1/models/Monitor.js"; +class MonitorController { + private monitorService: MonitorService; + constructor(monitorService: MonitorService) { + this.monitorService = monitorService; + } + + create = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const monitor = await this.monitorService.create(tokenizedUser, req.body); + res.status(201).json({ + message: "Monitor created successfully", + data: monitor, + }); + } catch (error) { + next(error); + } + }; + + get = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const id = req.params.id; + if (!id) { + throw new ApiError("Monitor ID is required", 400); + } + + const range = req.query.range; + if (!range || typeof range !== "string") throw new ApiError("Range query parameter is required", 400); + + let monitor; + + const status = req.query.status; + if (status && typeof status !== "string") { + throw new ApiError("Status query parameter must be a string", 400); + } + + if (req.query.embedChecks === "true") { + monitor = await this.monitorService.getEmbedChecks(id, range, status); + } else { + monitor = await this.monitorService.get(id); + } + + res.status(200).json({ + message: "Monitor retrieved successfully", + data: monitor, + }); + } catch (error) { + next(error); + } + }; + + getAll = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + let monitors; + if (req.query.embedChecks === "true") { + const page = Math.max(1, Number(req.query.page) || 1); + const limit = Math.max(1, Number(req.query.limit) || 10); + const type: MonitorType[] = req.query.type as MonitorType[]; + + monitors = await this.monitorService.getAllEmbedChecks(page, limit, type); + } else { + monitors = await this.monitorService.getAll(); + } + + res.status(200).json({ + message: "Monitors retrieved successfully", + data: monitors, + }); + } catch (error) { + next(error); + } + }; + + toggleActive = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const id = req.params.id; + if (!id) { + throw new ApiError("Monitor ID is required", 400); + } + + const monitor = await this.monitorService.toggleActive(id, tokenizedUser); + res.status(200).json({ + message: "Monitor paused/unpaused successfully", + data: monitor, + }); + } catch (error) { + next(error); + } + }; + + update = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const id = req.params.id; + if (!id) { + throw new ApiError("Monitor ID is required", 400); + } + + const monitor = await this.monitorService.update(tokenizedUser, id, req.body); + res.status(200).json({ + message: "Monitor updated successfully", + data: monitor, + }); + } catch (error) { + next(error); + } + }; + + delete = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + const id = req.params.id; + if (!id) { + throw new ApiError("Monitor ID is required", 400); + } + await this.monitorService.delete(id); + + res.status(200).json({ + message: "Monitor deleted successfully", + }); + } catch (error) { + next(error); + } + }; +} + +export default MonitorController; diff --git a/server/src/db/mongo/MongoDB.js b/server/src/db/v1/MongoDB.js similarity index 97% rename from server/src/db/mongo/MongoDB.js rename to server/src/db/v1/MongoDB.js index 74fcbd89f..3bdae3525 100755 --- a/server/src/db/mongo/MongoDB.js +++ b/server/src/db/v1/MongoDB.js @@ -1,5 +1,5 @@ import mongoose from "mongoose"; -import AppSettings from "../models/AppSettings.js"; +import AppSettings from "./models/AppSettings.js"; import { runMigrations } from "./migration/index.js"; class MongoDB { static SERVICE_NAME = "MongoDB"; diff --git a/server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.ts b/server/src/db/v1/migration/0001_migrateStatusWindowThreshold.ts similarity index 92% rename from server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.ts rename to server/src/db/v1/migration/0001_migrateStatusWindowThreshold.ts index 8f6988de4..857d36381 100644 --- a/server/src/db/mongo/migration/0001_migrateStatusWindowThreshold.ts +++ b/server/src/db/v1/migration/0001_migrateStatusWindowThreshold.ts @@ -1,4 +1,4 @@ -import Monitor from "../../models/Monitor.js"; +import Monitor from "../models/Monitor.js"; async function migrateStatusWindowThreshold() { try { const monitors = await Monitor.find({ statusWindowThreshold: { $lt: 1 } }); diff --git a/server/src/db/mongo/migration/index.ts b/server/src/db/v1/migration/index.ts similarity index 100% rename from server/src/db/mongo/migration/index.ts rename to server/src/db/v1/migration/index.ts diff --git a/server/src/db/models/AppSettings.js b/server/src/db/v1/models/AppSettings.js similarity index 100% rename from server/src/db/models/AppSettings.js rename to server/src/db/v1/models/AppSettings.js diff --git a/server/src/db/models/Check.js b/server/src/db/v1/models/Check.js similarity index 100% rename from server/src/db/models/Check.js rename to server/src/db/v1/models/Check.js diff --git a/server/src/db/models/InviteToken.js b/server/src/db/v1/models/InviteToken.js similarity index 100% rename from server/src/db/models/InviteToken.js rename to server/src/db/v1/models/InviteToken.js diff --git a/server/src/db/models/MaintenanceWindow.js b/server/src/db/v1/models/MaintenanceWindow.js similarity index 100% rename from server/src/db/models/MaintenanceWindow.js rename to server/src/db/v1/models/MaintenanceWindow.js diff --git a/server/src/db/models/Monitor.js b/server/src/db/v1/models/Monitor.js similarity index 100% rename from server/src/db/models/Monitor.js rename to server/src/db/v1/models/Monitor.js diff --git a/server/src/db/models/MonitorStats.js b/server/src/db/v1/models/MonitorStats.js similarity index 100% rename from server/src/db/models/MonitorStats.js rename to server/src/db/v1/models/MonitorStats.js diff --git a/server/src/db/models/Notification.js b/server/src/db/v1/models/Notification.js similarity index 100% rename from server/src/db/models/Notification.js rename to server/src/db/v1/models/Notification.js diff --git a/server/src/db/models/RecoveryToken.js b/server/src/db/v1/models/RecoveryToken.js similarity index 100% rename from server/src/db/models/RecoveryToken.js rename to server/src/db/v1/models/RecoveryToken.js diff --git a/server/src/db/models/StatusPage.js b/server/src/db/v1/models/StatusPage.js similarity index 100% rename from server/src/db/models/StatusPage.js rename to server/src/db/v1/models/StatusPage.js diff --git a/server/src/db/models/Team.js b/server/src/db/v1/models/Team.js similarity index 100% rename from server/src/db/models/Team.js rename to server/src/db/v1/models/Team.js diff --git a/server/src/db/models/User.js b/server/src/db/v1/models/User.js similarity index 98% rename from server/src/db/models/User.js rename to server/src/db/v1/models/User.js index 9368d98b1..bf3012b26 100755 --- a/server/src/db/models/User.js +++ b/server/src/db/v1/models/User.js @@ -1,6 +1,5 @@ import mongoose from "mongoose"; import bcrypt from "bcryptjs"; -import logger from "../../utils/logger.js"; import Monitor from "./Monitor.js"; import Team from "./Team.js"; import Notification from "./Notification.js"; diff --git a/server/src/db/mongo/modules/checkModule.js b/server/src/db/v1/modules/checkModule.js similarity index 100% rename from server/src/db/mongo/modules/checkModule.js rename to server/src/db/v1/modules/checkModule.js diff --git a/server/src/db/mongo/modules/checkModuleQueries.js b/server/src/db/v1/modules/checkModuleQueries.js similarity index 100% rename from server/src/db/mongo/modules/checkModuleQueries.js rename to server/src/db/v1/modules/checkModuleQueries.js diff --git a/server/src/db/mongo/modules/inviteModule.js b/server/src/db/v1/modules/inviteModule.js similarity index 100% rename from server/src/db/mongo/modules/inviteModule.js rename to server/src/db/v1/modules/inviteModule.js diff --git a/server/src/db/mongo/modules/maintenanceWindowModule.js b/server/src/db/v1/modules/maintenanceWindowModule.js similarity index 100% rename from server/src/db/mongo/modules/maintenanceWindowModule.js rename to server/src/db/v1/modules/maintenanceWindowModule.js diff --git a/server/src/db/mongo/modules/monitorModule.js b/server/src/db/v1/modules/monitorModule.js similarity index 100% rename from server/src/db/mongo/modules/monitorModule.js rename to server/src/db/v1/modules/monitorModule.js diff --git a/server/src/db/mongo/modules/monitorModuleQueries.js b/server/src/db/v1/modules/monitorModuleQueries.js similarity index 100% rename from server/src/db/mongo/modules/monitorModuleQueries.js rename to server/src/db/v1/modules/monitorModuleQueries.js diff --git a/server/src/db/mongo/modules/notificationModule.js b/server/src/db/v1/modules/notificationModule.js similarity index 100% rename from server/src/db/mongo/modules/notificationModule.js rename to server/src/db/v1/modules/notificationModule.js diff --git a/server/src/db/mongo/modules/recoveryModule.js b/server/src/db/v1/modules/recoveryModule.js similarity index 100% rename from server/src/db/mongo/modules/recoveryModule.js rename to server/src/db/v1/modules/recoveryModule.js diff --git a/server/src/db/mongo/modules/settingsModule.js b/server/src/db/v1/modules/settingsModule.js similarity index 100% rename from server/src/db/mongo/modules/settingsModule.js rename to server/src/db/v1/modules/settingsModule.js diff --git a/server/src/db/mongo/modules/statusPageModule.js b/server/src/db/v1/modules/statusPageModule.js similarity index 100% rename from server/src/db/mongo/modules/statusPageModule.js rename to server/src/db/v1/modules/statusPageModule.js diff --git a/server/src/db/mongo/modules/userModule.js b/server/src/db/v1/modules/userModule.js similarity index 100% rename from server/src/db/mongo/modules/userModule.js rename to server/src/db/v1/modules/userModule.js diff --git a/server/src/db/mongo/utils/seedDb.js b/server/src/db/v1/utils/seedDb.js similarity index 95% rename from server/src/db/mongo/utils/seedDb.js rename to server/src/db/v1/utils/seedDb.js index 224744a51..cdfe3675c 100755 --- a/server/src/db/mongo/utils/seedDb.js +++ b/server/src/db/v1/utils/seedDb.js @@ -1,5 +1,5 @@ -import Monitor from "../../models/Monitor.js"; -import Check from "../../models/Check.js"; +import Monitor from "../models/Monitor.js"; +import Check from "../models/Check.js"; import { logger } from "../../../utils/logger.js"; const generateRandomUrl = () => { diff --git a/server/src/index.js b/server/src/index.js index d1ff649eb..e8b756d0e 100755 --- a/server/src/index.js +++ b/server/src/index.js @@ -7,8 +7,8 @@ import path from "path"; import fs from "fs"; import Logger from "./utils/logger.js"; -import SettingsService from "./service/system/settingsService.js"; -import AppSettings from "./db/models/AppSettings.js"; +import SettingsService from "./service/v1/system/settingsService.js"; +import AppSettings from "./db/v1/models/AppSettings.js"; const SERVICE_NAME = "Server"; let logger; diff --git a/server/src/middleware/handleErrors.js b/server/src/middleware/v1/handleErrors.js similarity index 71% rename from server/src/middleware/handleErrors.js rename to server/src/middleware/v1/handleErrors.js index b0ded5ab7..43671fbe2 100755 --- a/server/src/middleware/handleErrors.js +++ b/server/src/middleware/v1/handleErrors.js @@ -1,6 +1,6 @@ -import { logger } from "../utils/logger.js"; -import ServiceRegistry from "../service/system/serviceRegistry.js"; -import StringService from "../service/system/stringService.js"; +import { logger } from "../../utils/logger.js"; +import ServiceRegistry from "../../service/v1/system/serviceRegistry.js"; +import StringService from "../../service/v1/system/stringService.js"; const handleErrors = (error, req, res, next) => { const status = error.status || 500; diff --git a/server/src/middleware/isAllowed.js b/server/src/middleware/v1/isAllowed.js similarity index 87% rename from server/src/middleware/isAllowed.js rename to server/src/middleware/v1/isAllowed.js index 5d61974be..cc240f531 100755 --- a/server/src/middleware/isAllowed.js +++ b/server/src/middleware/v1/isAllowed.js @@ -1,9 +1,9 @@ import jwt from "jsonwebtoken"; const TOKEN_PREFIX = "Bearer "; const SERVICE_NAME = "allowedRoles"; -import ServiceRegistry from "../service/system/serviceRegistry.js"; -import StringService from "../service/system/stringService.js"; -import SettingsService from "../service/system/settingsService.js"; +import ServiceRegistry from "../../service/v1/system/serviceRegistry.js"; +import StringService from "../../service/v1/system/stringService.js"; +import SettingsService from "../../service/v1/system/settingsService.js"; const isAllowed = (allowedRoles) => { return (req, res, next) => { diff --git a/server/src/middleware/languageMiddleware.js b/server/src/middleware/v1/languageMiddleware.js similarity index 93% rename from server/src/middleware/languageMiddleware.js rename to server/src/middleware/v1/languageMiddleware.js index 6293dc837..819858a21 100755 --- a/server/src/middleware/languageMiddleware.js +++ b/server/src/middleware/v1/languageMiddleware.js @@ -1,4 +1,4 @@ -import { logger } from "../utils/logger.js"; +import { logger } from "../../utils/logger.js"; const languageMiddleware = (stringService, translationService) => async (req, res, next) => { try { diff --git a/server/src/middleware/rateLimiter.js b/server/src/middleware/v1/rateLimiter.js similarity index 100% rename from server/src/middleware/rateLimiter.js rename to server/src/middleware/v1/rateLimiter.js diff --git a/server/src/middleware/responseHandler.js b/server/src/middleware/v1/responseHandler.js similarity index 100% rename from server/src/middleware/responseHandler.js rename to server/src/middleware/v1/responseHandler.js diff --git a/server/src/middleware/sanitization.js b/server/src/middleware/v1/sanitization.js similarity index 100% rename from server/src/middleware/sanitization.js rename to server/src/middleware/v1/sanitization.js diff --git a/server/src/middleware/verifyJWT.js b/server/src/middleware/v1/verifyJWT.js similarity index 87% rename from server/src/middleware/verifyJWT.js rename to server/src/middleware/v1/verifyJWT.js index d01db0a97..b57391dfd 100755 --- a/server/src/middleware/verifyJWT.js +++ b/server/src/middleware/v1/verifyJWT.js @@ -1,7 +1,7 @@ import jwt from "jsonwebtoken"; -import ServiceRegistry from "../service/system/serviceRegistry.js"; -import SettingsService from "../service/system/settingsService.js"; -import StringService from "../service/system/stringService.js"; +import ServiceRegistry from "../../service/v1/system/serviceRegistry.js"; +import SettingsService from "../../service/v1/system/settingsService.js"; +import StringService from "../../service/v1/system/stringService.js"; const SERVICE_NAME = "verifyJWT"; const TOKEN_PREFIX = "Bearer "; diff --git a/server/src/middleware/verifyOwnership.js b/server/src/middleware/v1/verifyOwnership.js similarity index 88% rename from server/src/middleware/verifyOwnership.js rename to server/src/middleware/v1/verifyOwnership.js index 4431b0c26..741fb6436 100755 --- a/server/src/middleware/verifyOwnership.js +++ b/server/src/middleware/v1/verifyOwnership.js @@ -1,6 +1,6 @@ -import { logger } from "../utils/logger.js"; -import ServiceRegistry from "../service/system/serviceRegistry.js"; -import StringService from "../service/system/stringService.js"; +import { logger } from "../../utils/logger.js"; +import ServiceRegistry from "../../service/v1/system/serviceRegistry.js"; +import StringService from "../../service/v1/system/stringService.js"; import { ObjectId } from "mongodb"; const SERVICE_NAME = "verifyOwnership"; diff --git a/server/src/middleware/verifySuperAdmin.js b/server/src/middleware/v1/verifySuperAdmin.js similarity index 97% rename from server/src/middleware/verifySuperAdmin.js rename to server/src/middleware/v1/verifySuperAdmin.js index a5a93eede..41ac0a3e0 100755 --- a/server/src/middleware/verifySuperAdmin.js +++ b/server/src/middleware/v1/verifySuperAdmin.js @@ -1,5 +1,5 @@ const jwt = require("jsonwebtoken"); -const logger = require("../utils/logger"); +const logger = require("../../utils/logger.js"); const SERVICE_NAME = "verifyAdmin"; const TOKEN_PREFIX = "Bearer "; import ServiceRegistry from "../service/serviceRegistry.js"; diff --git a/server/src/middleware/verifyTeamAccess.js b/server/src/middleware/v1/verifyTeamAccess.js similarity index 100% rename from server/src/middleware/verifyTeamAccess.js rename to server/src/middleware/v1/verifyTeamAccess.js diff --git a/server/src/routes/v1/authRoute.js b/server/src/routes/v1/authRoute.js index f69f94f43..d9b036b05 100755 --- a/server/src/routes/v1/authRoute.js +++ b/server/src/routes/v1/authRoute.js @@ -1,6 +1,6 @@ import { Router } from "express"; -import { verifyJWT } from "../../middleware/verifyJWT.js"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { verifyJWT } from "../../middleware/v1/verifyJWT.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; import multer from "multer"; const upload = multer(); diff --git a/server/src/routes/v1/checkRoute.js b/server/src/routes/v1/checkRoute.js index 673fe51e0..78f6b8a42 100755 --- a/server/src/routes/v1/checkRoute.js +++ b/server/src/routes/v1/checkRoute.js @@ -1,6 +1,6 @@ import { Router } from "express"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; class CheckRoutes { constructor(checkController) { diff --git a/server/src/routes/v1/diagnosticRoute.js b/server/src/routes/v1/diagnosticRoute.js index 1b7cc06be..2cf481e08 100755 --- a/server/src/routes/v1/diagnosticRoute.js +++ b/server/src/routes/v1/diagnosticRoute.js @@ -1,6 +1,6 @@ import { Router } from "express"; -import { verifyJWT } from "../../middleware/verifyJWT.js"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { verifyJWT } from "../../middleware/v1/verifyJWT.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; class DiagnosticRoutes { constructor(diagnosticController) { diff --git a/server/src/routes/v1/inviteRoute.js b/server/src/routes/v1/inviteRoute.js index 74fcfeb91..a47550536 100755 --- a/server/src/routes/v1/inviteRoute.js +++ b/server/src/routes/v1/inviteRoute.js @@ -1,6 +1,6 @@ import { Router } from "express"; -import { verifyJWT } from "../../middleware/verifyJWT.js"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { verifyJWT } from "../../middleware/v1/verifyJWT.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; class InviteRoutes { constructor(inviteController) { diff --git a/server/src/routes/v1/logRoutes.js b/server/src/routes/v1/logRoutes.js index feabe669a..43da8b75a 100755 --- a/server/src/routes/v1/logRoutes.js +++ b/server/src/routes/v1/logRoutes.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; class LogRoutes { constructor(logController) { this.router = Router(); diff --git a/server/src/routes/v1/maintenanceWindowRoute.js b/server/src/routes/v1/maintenanceWindowRoute.js index 622e98f88..3df040994 100755 --- a/server/src/routes/v1/maintenanceWindowRoute.js +++ b/server/src/routes/v1/maintenanceWindowRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import MaintenanceWindow from "../../db/models/MaintenanceWindow.js"; +import MaintenanceWindow from "../../db/v1/models/MaintenanceWindow.js"; class MaintenanceWindowRoutes { constructor(maintenanceWindowController) { this.router = Router(); diff --git a/server/src/routes/v1/monitorRoute.js b/server/src/routes/v1/monitorRoute.js index dde72e256..107964f6b 100755 --- a/server/src/routes/v1/monitorRoute.js +++ b/server/src/routes/v1/monitorRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; import multer from "multer"; import { fetchMonitorCertificate } from "../../controllers/v1/controllerUtils.js"; const upload = multer({ diff --git a/server/src/routes/v1/queueRoute.js b/server/src/routes/v1/queueRoute.js index 59e1cb744..c6cfa05da 100755 --- a/server/src/routes/v1/queueRoute.js +++ b/server/src/routes/v1/queueRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; class QueueRoutes { constructor(queueController) { this.router = Router(); diff --git a/server/src/routes/v1/settingsRoute.js b/server/src/routes/v1/settingsRoute.js index 76556e923..e88186af2 100755 --- a/server/src/routes/v1/settingsRoute.js +++ b/server/src/routes/v1/settingsRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/v1/isAllowed.js"; class SettingsRoutes { constructor(settingsController) { diff --git a/server/src/routes/v1/statusPageRoute.js b/server/src/routes/v1/statusPageRoute.js index 8300066a8..2c3b0b265 100755 --- a/server/src/routes/v1/statusPageRoute.js +++ b/server/src/routes/v1/statusPageRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { verifyJWT } from "../../middleware/verifyJWT.js"; +import { verifyJWT } from "../../middleware/v1/verifyJWT.js"; import multer from "multer"; const upload = multer(); diff --git a/server/src/service/business/checkService.js b/server/src/service/v1/business/checkService.js similarity index 100% rename from server/src/service/business/checkService.js rename to server/src/service/v1/business/checkService.js diff --git a/server/src/service/business/diagnosticService.js b/server/src/service/v1/business/diagnosticService.js similarity index 100% rename from server/src/service/business/diagnosticService.js rename to server/src/service/v1/business/diagnosticService.js diff --git a/server/src/service/business/inviteService.js b/server/src/service/v1/business/inviteService.js similarity index 100% rename from server/src/service/business/inviteService.js rename to server/src/service/v1/business/inviteService.js diff --git a/server/src/service/business/maintenanceWindowService.js b/server/src/service/v1/business/maintenanceWindowService.js similarity index 100% rename from server/src/service/business/maintenanceWindowService.js rename to server/src/service/v1/business/maintenanceWindowService.js diff --git a/server/src/service/business/monitorService.js b/server/src/service/v1/business/monitorService.js similarity index 99% rename from server/src/service/business/monitorService.js rename to server/src/service/v1/business/monitorService.js index 637870b71..061234102 100644 --- a/server/src/service/business/monitorService.js +++ b/server/src/service/v1/business/monitorService.js @@ -1,4 +1,4 @@ -import { createMonitorsBodyValidation } from "../../validation/joi.js"; +import { createMonitorsBodyValidation } from "../../../validation/joi.js"; const SERVICE_NAME = "MonitorService"; class MonitorService { diff --git a/server/src/service/business/userService.js b/server/src/service/v1/business/userService.js similarity index 100% rename from server/src/service/business/userService.js rename to server/src/service/v1/business/userService.js diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js b/server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js similarity index 100% rename from server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js rename to server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js b/server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js similarity index 90% rename from server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js rename to server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js index b348e2534..2833a59f5 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js +++ b/server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js @@ -5,11 +5,11 @@ class SuperSimpleQueueHelper { /** * @param {{ - * db: import("../database").Database, - * logger: import("../logger").Logger, - * networkService: import("../networkService").NetworkService, - * statusService: import("../statusService").StatusService, - * notificationService: import("../notificationService").NotificationService + * db: import("../database.js").Database, + * logger: import("../logger.js").Logger, + * networkService: import("../networkService.js").NetworkService, + * statusService: import("../statusService.js").StatusService, + * notificationService: import("../notificationService.js").NotificationService * }} */ constructor({ db, logger, networkService, statusService, notificationService }) { diff --git a/server/src/service/infrastructure/bufferService.js b/server/src/service/v1/infrastructure/bufferService.js similarity index 100% rename from server/src/service/infrastructure/bufferService.js rename to server/src/service/v1/infrastructure/bufferService.js diff --git a/server/src/service/infrastructure/emailService.js b/server/src/service/v1/infrastructure/emailService.js similarity index 100% rename from server/src/service/infrastructure/emailService.js rename to server/src/service/v1/infrastructure/emailService.js diff --git a/server/src/service/infrastructure/errorService.js b/server/src/service/v1/infrastructure/errorService.js similarity index 100% rename from server/src/service/infrastructure/errorService.js rename to server/src/service/v1/infrastructure/errorService.js diff --git a/server/src/service/infrastructure/networkService.js b/server/src/service/v1/infrastructure/networkService.js similarity index 100% rename from server/src/service/infrastructure/networkService.js rename to server/src/service/v1/infrastructure/networkService.js diff --git a/server/src/service/infrastructure/notificationService.js b/server/src/service/v1/infrastructure/notificationService.js similarity index 100% rename from server/src/service/infrastructure/notificationService.js rename to server/src/service/v1/infrastructure/notificationService.js diff --git a/server/src/service/infrastructure/notificationUtils.js b/server/src/service/v1/infrastructure/notificationUtils.js similarity index 100% rename from server/src/service/infrastructure/notificationUtils.js rename to server/src/service/v1/infrastructure/notificationUtils.js diff --git a/server/src/service/infrastructure/statusService.js b/server/src/service/v1/infrastructure/statusService.js similarity index 99% rename from server/src/service/infrastructure/statusService.js rename to server/src/service/v1/infrastructure/statusService.js index 72fa359d7..248d1c910 100755 --- a/server/src/service/infrastructure/statusService.js +++ b/server/src/service/v1/infrastructure/statusService.js @@ -1,4 +1,4 @@ -import MonitorStats from "../../db/models/MonitorStats.js"; +import MonitorStats from "../../../db/v1/models/MonitorStats.js"; const SERVICE_NAME = "StatusService"; class StatusService { diff --git a/server/src/service/system/serviceRegistry.js b/server/src/service/v1/system/serviceRegistry.js similarity index 100% rename from server/src/service/system/serviceRegistry.js rename to server/src/service/v1/system/serviceRegistry.js diff --git a/server/src/service/system/settingsService.js b/server/src/service/v1/system/settingsService.js similarity index 100% rename from server/src/service/system/settingsService.js rename to server/src/service/v1/system/settingsService.js diff --git a/server/src/service/system/stringService.js b/server/src/service/v1/system/stringService.js similarity index 100% rename from server/src/service/system/stringService.js rename to server/src/service/v1/system/stringService.js diff --git a/server/src/service/system/translationService.js b/server/src/service/v1/system/translationService.js similarity index 100% rename from server/src/service/system/translationService.js rename to server/src/service/v1/system/translationService.js diff --git a/server/src/service/v2/business/CheckService.ts b/server/src/service/v2/business/CheckService.ts new file mode 100644 index 000000000..181fe048d --- /dev/null +++ b/server/src/service/v2/business/CheckService.ts @@ -0,0 +1,132 @@ +import { json } from "stream/consumers"; +import { ICheck, Check, Monitor, ISystemInfo, ICaptureInfo } from "../../../db/v1/models/index.js"; +import { MonitorType } from "../../../db/v1/models/Monitor.js"; +import { StatusResponse } from "../infrastructure/NetworkService.js"; +import type { ICapturePayload, ILighthousePayload } from "../infrastructure/NetworkService.js"; +import mongoose from "mongoose"; + +export interface ICheckService { + buildCheck: (statusResponse: StatusResponse, type: MonitorType) => Promise; + cleanupOrphanedChecks: () => Promise; +} + +class CheckService implements ICheckService { + private isCapturePayload = (payload: any): payload is ICapturePayload => { + if (!payload || typeof payload !== "object") return false; + + if (!("data" in payload) || typeof payload.data !== "object") { + return false; + } + + const data = payload.data as Partial; + if (!data.cpu || typeof data.cpu !== "object" || typeof data.cpu.usage_percent !== "number") { + return false; + } + s; + + if (!data.memory || typeof data.memory !== "object" || typeof data.memory.usage_percent !== "number") { + return false; + } + + if (data.disk && !Array.isArray(data.disk)) { + return false; + } + if (data.net && !Array.isArray(data.net)) { + return false; + } + + if (!("capture" in payload) || typeof payload.capture !== "object") return false; + const capture = payload.capture as Record; + if (typeof capture.version !== "string" || typeof capture.mode !== "string") return false; + + return true; + }; + + private isPagespeedPayload = (payload: any): payload is ILighthousePayload => { + if (!payload || typeof payload !== "object") return false; + + if (!("lighthouseResult" in payload) || typeof payload.lighthouseResult !== "object") { + return false; + } + return true; + }; + + private buildBaseCheck = (statusResponse: StatusResponse) => { + const monitorId = new mongoose.Types.ObjectId(statusResponse.monitorId); + const check = new Check({ + monitorId: monitorId, + type: statusResponse?.type, + status: statusResponse?.status, + message: statusResponse?.message, + responseTime: statusResponse?.responseTime, + timings: statusResponse?.timings, + }); + return check; + }; + + private buildInfrastructureCheck = (statusResponse: StatusResponse) => { + if (!this.isCapturePayload(statusResponse.payload)) { + throw new Error("Invalid payload for infrastructure monitor"); + } + const check = this.buildBaseCheck(statusResponse); + check.system = statusResponse.payload.data; + check.capture = statusResponse.payload.capture; + return check; + }; + + private buildPagespeedCheck = (statusResponse: StatusResponse) => { + if (!this.isPagespeedPayload(statusResponse.payload)) { + throw new Error("Invalid payload for pagespeed monitor"); + } + const check = this.buildBaseCheck(statusResponse); + const lighthouseResult = statusResponse?.payload?.lighthouseResult; + check.lighthouse = { + accessibility: lighthouseResult?.categories?.accessibility?.score || 0, + bestPractices: lighthouseResult?.categories?.["best-practices"]?.score || 0, + seo: lighthouseResult?.categories?.seo?.score || 0, + performance: lighthouseResult?.categories?.performance?.score || 0, + audits: { + cls: lighthouseResult?.audits?.["cumulative-layout-shift"] || {}, + si: lighthouseResult?.audits?.["speed-index"] || {}, + fcp: lighthouseResult?.audits?.["first-contentful-paint"] || {}, + lcp: lighthouseResult?.audits?.["largest-contentful-paint"] || {}, + tbt: lighthouseResult?.audits?.["total-blocking-time"] || {}, + }, + }; + return check; + }; + + buildCheck = async (statusResponse: StatusResponse, type: MonitorType): Promise => { + switch (type) { + case "infrastructure": + return this.buildInfrastructureCheck(statusResponse as StatusResponse); + + case "pagespeed": + return this.buildPagespeedCheck(statusResponse as StatusResponse); + case "http": + case "https": + return this.buildBaseCheck(statusResponse); + + case "ping": + return this.buildBaseCheck(statusResponse); + default: + throw new Error(`Unsupported monitor type: ${type}`); + } + }; + + cleanupOrphanedChecks = async () => { + try { + const monitorIds = await Monitor.find().distinct("_id"); + const result = await Check.deleteMany({ + monitorId: { $nin: monitorIds }, + }); + console.log(`Deleted ${result.deletedCount} orphaned Checks.`); + return true; + } catch (error) { + console.error("Error cleaning up orphaned Checks:", error); + return false; + } + }; +} + +export default CheckService; diff --git a/server/src/service/v2/business/MonitorService.ts b/server/src/service/v2/business/MonitorService.ts new file mode 100644 index 000000000..6e48e79e5 --- /dev/null +++ b/server/src/service/v2/business/MonitorService.ts @@ -0,0 +1,464 @@ +import mongoose from "mongoose"; + +import { IMonitor, Monitor, ITokenizedUser, MonitorStats, Check } from "../../../db/v1/models/index.js"; +import ApiError from "../../../utils/ApiError.js"; +import { IJobQueue } from "../infrastructure/JobQueue.js"; +import { MonitorWithChecksResponse } from "../../../types/monitor-response-with-checks.js"; +import { MonitorStatus, MonitorType } from "../../../db/v1/models/monitors/Monitor.js"; +export interface IMonitorService { + create: (tokenizedUser: ITokenizedUser, monitorData: IMonitor) => Promise; + getAll: () => Promise; + getAllEmbedChecks: (page: number, limit: number, type: MonitorType[]) => Promise; + get: (monitorId: string) => Promise; + getEmbedChecks: (monitorId: string, range: string, status?: string) => Promise; + toggleActive: (monitorId: string, tokenizedUser: ITokenizedUser) => Promise; + update: (tokenizedUser: ITokenizedUser, monitorId: string, updateData: Partial) => Promise; + delete: (monitorId: string) => Promise; +} + +class MonitorService implements IMonitorService { + private jobQueue: IJobQueue; + constructor(jobQueue: IJobQueue) { + this.jobQueue = jobQueue; + } + + create = async (tokenizedUser: ITokenizedUser, monitorData: IMonitor) => { + const monitor = await Monitor.create({ + ...monitorData, + createdBy: tokenizedUser.sub, + updatedBy: tokenizedUser.sub, + }); + await MonitorStats.create({ + monitorId: monitor._id, + currentStreakStartedAt: Date.now(), + }); + await this.jobQueue.addJob(monitor); + return monitor; + }; + + getAll = async () => { + return Monitor.find(); + }; + + getAllEmbedChecks = async (page: number, limit: number, type: MonitorType[] = []) => { + const skip = (page - 1) * limit; + let find = {}; + if (type.length > 0) find = { type: { $in: type } }; + const monitors = await Monitor.find(find).skip(skip).limit(limit); + return monitors; + }; + + get = async (monitorId: string) => { + const monitor = await Monitor.findById(monitorId); + if (!monitor) { + throw new ApiError("Monitor not found", 404); + } + return monitor; + }; + + private getStartDate(range: string): Date { + const now = new Date(); + switch (range) { + case "30m": + return new Date(now.getTime() - 30 * 60 * 1000); + case "24h": + return new Date(now.getTime() - 24 * 60 * 60 * 1000); + case "7d": + return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); + case "30d": + return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); + default: + throw new ApiError("Invalid range parameter", 400); + } + } + + private getDateFormat(range: string): string { + switch (range) { + case "30m": + return "%Y-%m-%dT%H:%M:00Z"; + case "24h": + case "7d": + return "%Y-%m-%dT%H:00:00Z"; + case "30d": + return "%Y-%m-%d"; + default: + throw new ApiError("Invalid range parameter", 400); + } + } + + private getBaseGroup = (dateFormat: string): Record => { + return { + _id: { $dateToString: { format: dateFormat, date: "$createdAt" } }, + count: { $sum: 1 }, + avgResponseTime: { $avg: "$responseTime" }, + }; + }; + + private getBaseProjection = (): object => { + return { status: 1, responseTime: 1, createdAt: 1 }; + }; + + private getPageSpeedGroup = (dateFormat: string): Record => { + return { + _id: { $dateToString: { format: dateFormat, date: "$createdAt" } }, + count: { $sum: 1 }, + avgResponseTime: { $avg: "$responseTime" }, + accessibility: { $avg: "$lighthouse.accessibility" }, + bestPractices: { $avg: "$lighthouse.bestPractices" }, + seo: { $avg: "$lighthouse.seo" }, + performance: { $avg: "$lighthouse.performance" }, + cls: { $avg: "$lighthouse.audits.cls.score" }, + si: { $avg: "$lighthouse.audits.si.score" }, + fcp: { $avg: "$lighthouse.audits.fcp.score" }, + lcp: { $avg: "$lighthouse.audits.lcp.score" }, + tbt: { $avg: "$lighthouse.audits.tbt.score" }, + }; + }; + + private getPageSpeedProjection = (): object => { + const projectStage: any = { status: 1, responseTime: 1, createdAt: 1 }; + projectStage["lighthouse.accessibility"] = 1; + projectStage["lighthouse.seo"] = 1; + projectStage["lighthouse.bestPractices"] = 1; + projectStage["lighthouse.performance"] = 1; + projectStage["lighthouse.audits.cls.score"] = 1; + projectStage["lighthouse.audits.si.score"] = 1; + projectStage["lighthouse.audits.fcp.score"] = 1; + projectStage["lighthouse.audits.lcp.score"] = 1; + projectStage["lighthouse.audits.tbt.score"] = 1; + return projectStage; + }; + + private getInfraGroup = (dateFormat: string): Record => { + return { + _id: { $dateToString: { format: dateFormat, date: "$createdAt" } }, + count: { $sum: 1 }, + avgResponseTime: { $avg: "$responseTime" }, + physicalCores: { $last: "$system.cpu.physical_core" }, + logicalCores: { $last: "$system.cpu.logical_core" }, + frequency: { $avg: "$system.cpu.frequency" }, + currentFrequency: { $last: "$system.cpu.current_frequency" }, + tempsArrays: { $push: "$system.cpu.temperature" }, + freePercent: { $avg: "$system.cpu.free_percent" }, + usedPercent: { $avg: "$system.cpu.usage_percent" }, + total_bytes: { $last: "$system.memory.total_bytes" }, + available_bytes: { $last: "$system.memory.available_bytes" }, + used_bytes: { $last: "$system.memory.used_bytes" }, + memory_usage_percent: { $avg: "$system.memory.usage_percent" }, + disksArray: { $push: "$system.disk" }, + os: { $last: "$system.host.os" }, + platform: { $last: "$system.host.platform" }, + kernel_version: { $last: "$system.host.kernel_version" }, + pretty_name: { $last: "$system.host.pretty_name" }, + netsArray: { $push: "$system.net" }, + }; + }; + + private getInfraProjection = (): object => { + const projectStage: any = { status: 1, responseTime: 1, createdAt: 1 }; + projectStage["system.cpu.physical_core"] = 1; + projectStage["system.cpu.logical_core"] = 1; + projectStage["system.cpu.frequency"] = 1; + projectStage["system.cpu.current_frequency"] = 1; + projectStage["system.cpu.temperature"] = 1; + projectStage["system.cpu.free_percent"] = 1; + projectStage["system.cpu.usage_percent"] = 1; + projectStage["system.memory.total_bytes"] = 1; + projectStage["system.memory.available_bytes"] = 1; + projectStage["system.memory.used_bytes"] = 1; + projectStage["system.memory.usage_percent"] = 1; + projectStage["system.disk"] = 1; + projectStage["system.host.os"] = 1; + projectStage["system.host.platform"] = 1; + projectStage["system.host.kernel_version"] = 1; + projectStage["system.host.pretty_name"] = 1; + projectStage["system.net"] = 1; + return projectStage; + }; + + private getFinalProjection = (type: string): object => { + if (type === "pagespeed") { + return { + _id: 1, + count: 1, + avgResponseTime: 1, + accessibility: "$accessibility", + seo: "$seo", + bestPractices: "$bestPractices", + performance: "$performance", + cls: "$cls", + si: "$si", + fcp: "$fcp", + lcp: "$lcp", + tbt: "$tbt", + }; + } + + if (type === "infrastructure") { + return { + _id: 1, + count: 1, + avgResponseTime: 1, + cpu: { + physicalCores: "$physicalCores", + logicalCores: "$logicalCores", + frequency: "$frequency", + currentFrequency: "$currentFrequency", + temperatures: { + $map: { + input: { + $range: [0, { $size: { $arrayElemAt: ["$tempsArrays", 0] } }], + }, + as: "idx", + in: { + $avg: { + $map: { + input: "$tempsArrays", + as: "arr", + in: { $arrayElemAt: ["$$arr", "$$idx"] }, + }, + }, + }, + }, + }, + freePercent: "$freePercent", + usedPercent: "$usedPercent", + }, + memory: { + total_bytes: "$total_bytes", + available_bytes: "$available_bytes", + used_bytes: "$used_bytes", + usage_percent: "$memory_usage_percent", + }, + disks: { + $map: { + input: { + $range: [0, { $size: { $arrayElemAt: ["$disksArray", 0] } }], + }, + as: "idx", + in: { + $let: { + vars: { + diskGroup: { + $map: { + input: "$disksArray", + as: "diskArr", + in: { $arrayElemAt: ["$$diskArr", "$$idx"] }, + }, + }, + }, + in: { + device: { $arrayElemAt: ["$$diskGroup.device", 0] }, + total_bytes: { $avg: "$$diskGroup.total_bytes" }, + free_bytes: { $avg: "$$diskGroup.free_bytes" }, + used_bytes: { $avg: "$$diskGroup.used_bytes" }, + usage_percent: { $avg: "$$diskGroup.usage_percent" }, + total_inodes: { $avg: "$$diskGroup.total_inodes" }, + free_inodes: { $avg: "$$diskGroup.free_inodes" }, + used_inodes: { $avg: "$$diskGroup.used_inodes" }, + inodes_usage_percent: { + $avg: "$$diskGroup.inodes_usage_percent", + }, + read_bytes: { $avg: "$$diskGroup.read_bytes" }, + write_bytes: { $avg: "$$diskGroup.write_bytes" }, + read_time: { $avg: "$$diskGroup.read_time" }, + write_time: { $avg: "$$diskGroup.write_time" }, + }, + }, + }, + }, + }, + host: { + os: "$os", + platform: "$platform", + kernel_version: "$kernel_version", + pretty_name: "$pretty_name", + }, + net: { + $map: { + input: { + $range: [0, { $size: { $arrayElemAt: ["$netsArray", 0] } }], + }, + as: "idx", + in: { + $let: { + vars: { + netGroup: { + $map: { + input: "$netsArray", + as: "netArr", + in: { $arrayElemAt: ["$$netArr", "$$idx"] }, + }, + }, + }, + in: { + name: { $arrayElemAt: ["$$netGroup.name", 0] }, + bytes_sent: { $avg: "$$netGroup.bytes_sent" }, + bytes_recv: { $avg: "$$netGroup.bytes_recv" }, + packets_sent: { $avg: "$$netGroup.packets_sent" }, + packets_recv: { $avg: "$$netGroup.packets_recv" }, + err_in: { $avg: "$$netGroup.err_in" }, + err_out: { $avg: "$$netGroup.err_out" }, + drop_in: { $avg: "$$netGroup.drop_in" }, + drop_out: { $avg: "$$netGroup.drop_out" }, + fifo_in: { $avg: "$$netGroup.fifo_in" }, + fifo_out: { $avg: "$$netGroup.fifo_out" }, + }, + }, + }, + }, + }, + }; + } + return {}; + }; + + getEmbedChecks = async (monitorId: string, range: string, status: string | undefined): Promise => { + const monitor = await Monitor.findById(monitorId); + if (!monitor) { + throw new ApiError("Monitor not found", 404); + } + const startDate = this.getStartDate(range); + const dateFormat = this.getDateFormat(range); + + // Build match stage + const matchStage: { + monitorId: mongoose.Types.ObjectId; + createdAt: { $gte: Date }; + status?: string; + } = { + monitorId: monitor._id, + createdAt: { $gte: startDate }, + }; + + if (status) { + matchStage.status = status; + } + + let groupClause; + + if (monitor.type === "pagespeed") { + groupClause = this.getPageSpeedGroup(dateFormat); + } else if (monitor.type === "infrastructure") { + groupClause = this.getInfraGroup(dateFormat); + } else { + groupClause = this.getBaseGroup(dateFormat); + } + + let projectStage; + if (monitor.type === "pagespeed") { + projectStage = this.getPageSpeedProjection(); + } else if (monitor.type === "infrastructure") { + projectStage = this.getInfraProjection(); + } else { + projectStage = this.getBaseProjection(); + } + + let finalProjection = {}; + if (monitor.type === "pagespeed" || monitor.type === "infrastructure") { + finalProjection = this.getFinalProjection(monitor.type); + } else { + finalProjection = { _id: 1, count: 1, avgResponseTime: 1 }; + } + + const checks = await Check.aggregate([ + { + $match: matchStage, + }, + { $sort: { createdAt: 1 } }, + { $project: projectStage }, + { $group: groupClause }, + { $sort: { _id: -1 } }, + { + $project: finalProjection, + }, + ]); + + // Get monitor stats + const monitorStats = await MonitorStats.findOne({ + monitorId: monitor._id, + }).lean(); + + if (!monitorStats) { + throw new ApiError("Monitor stats not found", 404); + } + + return { + monitor: monitor.toObject(), + checks, + stats: monitorStats, + }; + }; + + async toggleActive(id: string, tokenizedUser: ITokenizedUser) { + const pendingStatus: MonitorStatus = "initializing"; + const updatedMonitor = await Monitor.findOneAndUpdate( + { _id: id }, + [ + { + $set: { + isActive: { $not: "$isActive" }, + status: pendingStatus, + updatedBy: tokenizedUser.sub, + updatedAt: new Date(), + }, + }, + ], + { new: true } + ); + + if (!updatedMonitor) { + throw new ApiError("Monitor not found", 404); + } + + await this.jobQueue.updateJob(updatedMonitor); + + if (updatedMonitor?.isActive) { + await this.jobQueue.resumeJob(updatedMonitor); + } else { + await this.jobQueue.pauseJob(updatedMonitor); + } + return updatedMonitor; + } + + async update(tokenizedUser: ITokenizedUser, monitorId: string, updateData: Partial) { + const allowedFields: (keyof IMonitor)[] = ["name", "interval", "isActive", "n", "m", "notificationChannels"]; + const safeUpdate: Partial = {}; + + for (const field of allowedFields) { + if (updateData[field] !== undefined) { + (safeUpdate as any)[field] = updateData[field]; + } + } + + const updatedMonitor = await Monitor.findByIdAndUpdate( + monitorId, + { + $set: { + ...safeUpdate, + updatedAt: new Date(), + updatedBy: tokenizedUser.sub, + }, + }, + { new: true, runValidators: true } + ); + + if (!updatedMonitor) { + throw new ApiError("Monitor not found", 404); + } + await this.jobQueue.updateJob(updatedMonitor); + return updatedMonitor; + } + + async delete(monitorId: string) { + const monitor = await Monitor.findById(monitorId); + if (!monitor) { + throw new ApiError("Monitor not found", 404); + } + await monitor.deleteOne(); + await this.jobQueue.deleteJob(monitor); + return true; + } +} + +export default MonitorService;