Merge pull request #2975 from bluewave-labs/feat/v1/structure
feat/v1/structure
This commit is contained in:
75 files changed
+864
-89
No files matched your search
@@ -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
|
||||
+5
-5
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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";
|
||||
+1
-1
@@ -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 } });
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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";
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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 = () => {
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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) => {
|
||||
+1
-1
@@ -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 {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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 ";
|
||||
|
||||
+3
-3
@@ -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";
|
||||
+1
-1
@@ -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";
|
||||
File renamed without changes.
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { createMonitorsBodyValidation } from "../../validation/joi.js";
|
||||
import { createMonitorsBodyValidation } from "../../../validation/joi.js";
|
||||
|
||||
const SERVICE_NAME = "MonitorService";
|
||||
class MonitorService {
|
||||
File renamed without changes.
File renamed without changes.
+5
-5
@@ -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 }) {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -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 {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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<ICheck>;
|
||||
cleanupOrphanedChecks: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
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<ISystemInfo>;
|
||||
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<string, any>;
|
||||
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<ICapturePayload>) => {
|
||||
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<ILighthousePayload>) => {
|
||||
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<ICheck> => {
|
||||
switch (type) {
|
||||
case "infrastructure":
|
||||
return this.buildInfrastructureCheck(statusResponse as StatusResponse<ICapturePayload>);
|
||||
|
||||
case "pagespeed":
|
||||
return this.buildPagespeedCheck(statusResponse as StatusResponse<ILighthousePayload>);
|
||||
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;
|
||||
@@ -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<IMonitor>;
|
||||
getAll: () => Promise<IMonitor[]>;
|
||||
getAllEmbedChecks: (page: number, limit: number, type: MonitorType[]) => Promise<any[]>;
|
||||
get: (monitorId: string) => Promise<IMonitor>;
|
||||
getEmbedChecks: (monitorId: string, range: string, status?: string) => Promise<MonitorWithChecksResponse>;
|
||||
toggleActive: (monitorId: string, tokenizedUser: ITokenizedUser) => Promise<IMonitor>;
|
||||
update: (tokenizedUser: ITokenizedUser, monitorId: string, updateData: Partial<IMonitor>) => Promise<IMonitor>;
|
||||
delete: (monitorId: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
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<string, any> => {
|
||||
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<string, any> => {
|
||||
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<string, any> => {
|
||||
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<MonitorWithChecksResponse> => {
|
||||
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<IMonitor>) {
|
||||
const allowedFields: (keyof IMonitor)[] = ["name", "interval", "isActive", "n", "m", "notificationChannels"];
|
||||
const safeUpdate: Partial<IMonitor> = {};
|
||||
|
||||
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;
|
||||
@@ -22,5 +22,5 @@
|
||||
"allowJs": true,
|
||||
"checkJs": false
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.config.js"]
|
||||
"exclude": ["node_modules", "dist/", "**/*.config.js"]
|
||||
}
|
||||
Reference in new issue
Block a user