Resolved merge conflict in Server/index.js
This commit is contained in:
commit
e9bcf66b29
99 files changed
+5354
-358
No files matched your search
@@ -1,16 +1,219 @@
|
||||
import { handleError } from "./controllerUtils.js";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
import Monitor from "../db/models/Monitor.js";
|
||||
import DistributedUptimeCheck from "../db/models/DistributedUptimeCheck.js";
|
||||
|
||||
const SERVICE_NAME = "DistributedUptimeQueueController";
|
||||
|
||||
class DistributedUptimeController {
|
||||
constructor() {}
|
||||
constructor(db, http, statusService) {
|
||||
this.db = db;
|
||||
this.http = http;
|
||||
this.statusService = statusService;
|
||||
this.resultsCallback = this.resultsCallback.bind(this);
|
||||
this.getDistributedUptimeMonitors = this.getDistributedUptimeMonitors.bind(this);
|
||||
this.getDistributedUptimeMonitorDetails =
|
||||
this.getDistributedUptimeMonitorDetails.bind(this);
|
||||
}
|
||||
|
||||
async resultsCallback(req, res, next) {
|
||||
try {
|
||||
const { id, result } = req.body;
|
||||
// Calculate response time
|
||||
const {
|
||||
first_byte_took,
|
||||
body_read_took,
|
||||
dns_took,
|
||||
conn_took,
|
||||
connect_took,
|
||||
tls_took,
|
||||
status_code,
|
||||
error,
|
||||
} = result;
|
||||
|
||||
// Calculate response time
|
||||
const responseTime =
|
||||
(first_byte_took +
|
||||
body_read_took +
|
||||
dns_took +
|
||||
conn_took +
|
||||
connect_took +
|
||||
tls_took) /
|
||||
1_000_000;
|
||||
|
||||
// Calculate if server is up or down
|
||||
const isErrorStatus = status_code >= 400;
|
||||
const hasError = error !== "";
|
||||
|
||||
const status = isErrorStatus || hasError ? false : true;
|
||||
|
||||
// Build response
|
||||
const distributedUptimeResponse = {
|
||||
monitorId: id,
|
||||
type: "distributed_http",
|
||||
payload: result,
|
||||
status,
|
||||
code: status_code,
|
||||
responseTime,
|
||||
first_byte_took,
|
||||
body_read_took,
|
||||
dns_took,
|
||||
conn_took,
|
||||
connect_took,
|
||||
tls_took,
|
||||
};
|
||||
if (error) {
|
||||
const code = status_code || this.NETWORK_ERROR;
|
||||
distributedUptimeResponse.code = code;
|
||||
distributedUptimeResponse.message =
|
||||
this.http.STATUS_CODES[code] || "Network Error";
|
||||
} else {
|
||||
distributedUptimeResponse.message = this.http.STATUS_CODES[status_code];
|
||||
}
|
||||
|
||||
await this.statusService.updateStatus(distributedUptimeResponse);
|
||||
|
||||
res.status(200).json({ message: "OK" });
|
||||
} catch (error) {
|
||||
throw handleError(error, SERVICE_NAME, "resultsCallback");
|
||||
next(handleError(error, SERVICE_NAME, "resultsCallback"));
|
||||
}
|
||||
}
|
||||
|
||||
async getDistributedUptimeMonitors(req, res, next) {
|
||||
try {
|
||||
res.setHeader("Content-Type", "text/event-stream");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
const BATCH_DELAY = 1000;
|
||||
let batchTimeout = null;
|
||||
let opInProgress = false;
|
||||
|
||||
// Do things here
|
||||
const notifyChange = async () => {
|
||||
if (opInProgress) {
|
||||
// Get data
|
||||
const monitors = await this.db.getMonitorsByTeamId(req);
|
||||
res.write(`data: ${JSON.stringify({ monitors })}\n\n`);
|
||||
opInProgress = false;
|
||||
}
|
||||
batchTimeout = null;
|
||||
};
|
||||
|
||||
const handleChange = () => {
|
||||
opInProgress = true;
|
||||
if (batchTimeout) clearTimeout(batchTimeout);
|
||||
batchTimeout = setTimeout(notifyChange, BATCH_DELAY);
|
||||
};
|
||||
|
||||
const monitorStream = Monitor.watch(
|
||||
[{ $match: { operationType: { $in: ["insert", "update", "delete"] } } }],
|
||||
{ fullDocument: "updateLookup" }
|
||||
);
|
||||
|
||||
const checksStream = DistributedUptimeCheck.watch(
|
||||
[{ $match: { operationType: { $in: ["insert", "update", "delete"] } } }],
|
||||
{ fullDocument: "updateLookup" }
|
||||
);
|
||||
|
||||
monitorStream.on("change", handleChange);
|
||||
checksStream.on("change", handleChange);
|
||||
|
||||
// Send initial data
|
||||
const monitors = await this.db.getMonitorsByTeamId(req);
|
||||
res.write(`data: ${JSON.stringify({ monitors })}\n\n`);
|
||||
|
||||
// Handle client disconnect
|
||||
req.on("close", () => {
|
||||
if (batchTimeout) {
|
||||
clearTimeout(batchTimeout);
|
||||
}
|
||||
monitorStream.close();
|
||||
checksStream.close();
|
||||
clearInterval(keepAlive);
|
||||
});
|
||||
|
||||
// Keep connection alive
|
||||
const keepAlive = setInterval(() => {
|
||||
res.write(": keepalive\n\n");
|
||||
}, 30000);
|
||||
|
||||
// Clean up on close
|
||||
req.on("close", () => {
|
||||
clearInterval(keepAlive);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
next(handleError(error, SERVICE_NAME, "getDistributedUptimeMonitors"));
|
||||
}
|
||||
}
|
||||
|
||||
async getDistributedUptimeMonitorDetails(req, res, next) {
|
||||
try {
|
||||
res.setHeader("Content-Type", "text/event-stream");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
const BATCH_DELAY = 1000;
|
||||
let batchTimeout = null;
|
||||
let opInProgress = false;
|
||||
|
||||
// Do things here
|
||||
const notifyChange = async () => {
|
||||
try {
|
||||
if (opInProgress) {
|
||||
// Get data
|
||||
const monitor = await this.db.getDistributedUptimeDetailsById(req);
|
||||
res.write(`data: ${JSON.stringify({ monitor })}\n\n`);
|
||||
opInProgress = false;
|
||||
}
|
||||
batchTimeout = null;
|
||||
} catch (error) {
|
||||
console.error("Error in notifyChange:", error);
|
||||
opInProgress = false;
|
||||
batchTimeout = null;
|
||||
next(handleError(error, SERVICE_NAME, "getDistributedUptimeMonitorDetails"));
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = () => {
|
||||
opInProgress = true;
|
||||
if (batchTimeout) clearTimeout(batchTimeout);
|
||||
batchTimeout = setTimeout(notifyChange, BATCH_DELAY);
|
||||
};
|
||||
|
||||
const checksStream = DistributedUptimeCheck.watch(
|
||||
[{ $match: { operationType: { $in: ["insert", "update", "delete"] } } }],
|
||||
{ fullDocument: "updateLookup" }
|
||||
);
|
||||
|
||||
checksStream.on("change", handleChange);
|
||||
|
||||
// Send initial data
|
||||
const monitor = await this.db.getDistributedUptimeDetailsById(req);
|
||||
res.write(`data: ${JSON.stringify({ monitor })}\n\n`);
|
||||
|
||||
// Handle client disconnect
|
||||
req.on("close", () => {
|
||||
if (batchTimeout) {
|
||||
clearTimeout(batchTimeout);
|
||||
}
|
||||
checksStream.close();
|
||||
clearInterval(keepAlive);
|
||||
});
|
||||
|
||||
// Keep connection alive
|
||||
const keepAlive = setInterval(() => {
|
||||
res.write(": keepalive\n\n");
|
||||
}, 30000);
|
||||
|
||||
// Clean up on close
|
||||
req.on("close", () => {
|
||||
clearInterval(keepAlive);
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getDistributedUptimeMonitorDetails"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
import {
|
||||
createStatusPageBodyValidation,
|
||||
getStatusPageParamValidation,
|
||||
imageValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
import { successMessages, errorMessages } from "../utils/messages.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageController";
|
||||
|
||||
@@ -15,13 +16,14 @@ class StatusPageController {
|
||||
createStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const statusPage = await this.db.createStatusPage(req.body);
|
||||
const statusPage = await this.db.createStatusPage(req.body, req.file);
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_CREATE,
|
||||
data: statusPage,
|
||||
@@ -30,17 +32,35 @@ class StatusPageController {
|
||||
next(handleError(error, SERVICE_NAME, "createStatusPage"));
|
||||
}
|
||||
};
|
||||
getStatusPageByUrl = async (req, res, next) => {
|
||||
|
||||
updateStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
await getStatusPageParamValidation.validateAsync(req.params);
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { url } = req.params;
|
||||
const statusPage = await this.db.getStatusPageByUrl(url);
|
||||
const statusPage = await this.db.updateStatusPage(req.body, req.file);
|
||||
if (statusPage === null) {
|
||||
const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_UPDATE,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "updateStatusPage"));
|
||||
}
|
||||
};
|
||||
|
||||
getStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
const statusPage = await this.db.getStatusPage();
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_BY_URL,
|
||||
data: statusPage,
|
||||
@@ -49,6 +69,17 @@ class StatusPageController {
|
||||
next(handleError(error, SERVICE_NAME, "getStatusPage"));
|
||||
}
|
||||
};
|
||||
|
||||
deleteStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
await this.db.deleteStatusPage();
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_DELETE,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteStatusPage"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default StatusPageController;
|
||||
@@ -1,13 +1,121 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
import { BaseCheckSchema } from "./Check.js";
|
||||
|
||||
// {
|
||||
// "id": "12123",
|
||||
// "result": {
|
||||
// "task_arrived": "2025-01-13T19:21:37.463466602Z",
|
||||
// "dns_start": "2025-01-14T00:21:33.1801319+05:00",
|
||||
// "dns_end": "2025-01-14T00:21:33.4582552+05:00",
|
||||
// "conn_start": "2025-01-14T00:21:33.1801319+05:00",
|
||||
// "conn_end": "2025-01-14T00:21:33.7076318+05:00",
|
||||
// "connect_start": "2025-01-14T00:21:33.4582552+05:00",
|
||||
// "connect_end": "2025-01-14T00:21:33.541899+05:00",
|
||||
// "tls_hand_shake_start": "2025-01-14T00:21:33.541899+05:00",
|
||||
// "tls_hand_shake_end": "2025-01-14T00:21:33.7076318+05:00",
|
||||
// "body_read_start": "2025-01-14T00:21:34.1894707+05:00",
|
||||
// "body_read_end": "2025-01-14T00:21:34.1894707+05:00",
|
||||
// "wrote_request": "2025-01-14T00:21:33.7076318+05:00",
|
||||
// "got_first_response_byte": "2025-01-14T00:21:34.1327652+05:00",
|
||||
// "first_byte_took": 425133400,
|
||||
// "body_read_took": 56030000,
|
||||
// "dns_took": 278123300,
|
||||
// "conn_took": 527499900,
|
||||
// "connect_took": 83643800,
|
||||
// "tls_took": 165732800,
|
||||
// "sni_name": "uprock.com",
|
||||
// "status_code": 200,
|
||||
// "body_size": 19320,
|
||||
// "request_header_size": 95,
|
||||
// "response_header_size": 246,
|
||||
// "response_headers": "X-Vercel-Id: bom1::iad1::sm87v-1736796096856-aec270c01f23\nDate: Mon, 13 Jan 2025 19:21:37 GMT\nServer: Vercel\nStrict-Transport-Security: max-age=63072000\nVary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url\nX-Matched-Path: /\nX-Powered-By: Next.js\nX-Vercel-Cache: MISS\nAge: 0\nCache-Control: private, no-cache, no-store, max-age=0, must-revalidate\nContent-Type: text/html; charset=utf-8",
|
||||
// "error": "",
|
||||
// "device_id": "d5f578e143a2cd603dd6bf5f846a86a538bde4a8fbe2ad1fca284ad9f033daf8",
|
||||
// "ip_address": "223.123.19.0",
|
||||
// "proof": "",
|
||||
// "created_at": "2025-01-13T19:21:37.463466912Z",
|
||||
// "continent": "AS",
|
||||
// "country_code": "PK",
|
||||
// "city": "Muzaffargarh",
|
||||
// "upt_burnt" : "0.01",
|
||||
// "location": {
|
||||
// "lat": 71.0968,
|
||||
// "lng": 30.0208
|
||||
// },
|
||||
// "payload": {
|
||||
// "callback": "https://webhook.site/2a15b0af-545a-4ac2-b913-153b97592d7a",
|
||||
// "x": "y"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const LocationSchema = new mongoose.Schema(
|
||||
{
|
||||
lat: { type: Number, required: true },
|
||||
lng: { type: Number, required: true },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const DistributedUptimeCheckSchema = mongoose.Schema(
|
||||
{
|
||||
...BaseCheckSchema.obj,
|
||||
first_byte_took: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
body_read_took: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
dns_took: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
conn_took: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
connect_took: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
tls_took: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
location: {
|
||||
type: LocationSchema,
|
||||
required: false,
|
||||
},
|
||||
continent: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
countryCode: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
city: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
uptBurnt: {
|
||||
type: mongoose.Schema.Types.Decimal128,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
DistributedUptimeCheckSchema.pre("save", function (next) {
|
||||
if (this.isModified("uptBurnt") && typeof this.uptBurnt === "string") {
|
||||
this.uptBurnt = mongoose.Types.Decimal128.fromString(this.uptBurnt);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
DistributedUptimeCheckSchema.index({ createdAt: 1 });
|
||||
DistributedUptimeCheckSchema.index({ monitorId: 1, createdAt: 1 });
|
||||
DistributedUptimeCheckSchema.index({ monitorId: 1, createdAt: -1 });
|
||||
|
||||
@@ -23,11 +23,6 @@ const StatusPageSchema = mongoose.Schema(
|
||||
required: true,
|
||||
default: "#4169E1",
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "light",
|
||||
},
|
||||
monitors: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
@@ -35,6 +30,22 @@ const StatusPageSchema = mongoose.Schema(
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
logo: {
|
||||
data: Buffer,
|
||||
contentType: String,
|
||||
},
|
||||
isPublished: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showCharts: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showUptimePercentage: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@@ -43,6 +43,11 @@ import * as hardwareCheckModule from "./modules/hardwareCheckModule.js";
|
||||
|
||||
import * as checkModule from "./modules/checkModule.js";
|
||||
|
||||
//****************************************
|
||||
// Distributed Checks
|
||||
//****************************************
|
||||
import * as distributedCheckModule from "./modules/distributedCheckModule.js";
|
||||
|
||||
//****************************************
|
||||
// Maintenance Window
|
||||
//****************************************
|
||||
@@ -74,6 +79,7 @@ class MongoDB {
|
||||
Object.assign(this, pageSpeedCheckModule);
|
||||
Object.assign(this, hardwareCheckModule);
|
||||
Object.assign(this, checkModule);
|
||||
Object.assign(this, distributedCheckModule);
|
||||
Object.assign(this, maintenanceWindowModule);
|
||||
Object.assign(this, notificationModule);
|
||||
Object.assign(this, settingsModule);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js";
|
||||
const SERVICE_NAME = "distributedCheckModule";
|
||||
|
||||
const createDistributedCheck = async (checkData) => {
|
||||
try {
|
||||
const check = await new DistributedUptimeCheck({ ...checkData }).save();
|
||||
return check;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createDistributedCheck };
|
||||
@@ -2,6 +2,7 @@ import Monitor from "../../models/Monitor.js";
|
||||
import Check from "../../models/Check.js";
|
||||
import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
import HardwareCheck from "../../models/HardwareCheck.js";
|
||||
import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import Notification from "../../models/Notification.js";
|
||||
import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js";
|
||||
@@ -11,6 +12,7 @@ import { fileURLToPath } from "url";
|
||||
import {
|
||||
buildUptimeDetailsPipeline,
|
||||
buildHardwareDetailsPipeline,
|
||||
buildDistributedUptimeDetailsPipeline,
|
||||
} from "./monitorModuleQueries.js";
|
||||
import { ObjectId } from "mongodb";
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -366,6 +368,48 @@ const getUptimeDetailsById = async (req) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getDistributedUptimeDetailsById = async (req) => {
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
if (monitor === null || monitor === undefined) {
|
||||
throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
|
||||
}
|
||||
|
||||
const { dateRange, normalize } = req.query;
|
||||
const dates = getDateRange(dateRange);
|
||||
const formatLookup = {
|
||||
day: "%Y-%m-%dT%H:%M:00Z",
|
||||
week: "%Y-%m-%dT%H:00:00Z",
|
||||
month: "%Y-%m-%dT00:00:00Z",
|
||||
};
|
||||
|
||||
const dateString = formatLookup[dateRange];
|
||||
const results = await DistributedUptimeCheck.aggregate(
|
||||
buildDistributedUptimeDetailsPipeline(monitor, dates, dateString)
|
||||
);
|
||||
|
||||
const monitorData = results[0];
|
||||
const normalizedGroupChecks = NormalizeDataUptimeDetails(
|
||||
monitorData.groupedChecks,
|
||||
10,
|
||||
100
|
||||
);
|
||||
|
||||
const monitorStats = {
|
||||
...monitor.toObject(),
|
||||
...monitorData,
|
||||
groupedChecks: normalizedGroupChecks,
|
||||
};
|
||||
|
||||
return monitorStats;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getDistributedUptimeDetailsById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get stats by monitor ID
|
||||
* @async
|
||||
@@ -630,6 +674,26 @@ const getMonitorsByTeamId = async (req) => {
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(limit
|
||||
? [
|
||||
{
|
||||
$lookup: {
|
||||
from: "distributeduptimechecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
...(limit ? [{ $limit: limit }] : []),
|
||||
],
|
||||
as: "distributeduptimechecks",
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
$addFields: {
|
||||
@@ -648,6 +712,10 @@ const getMonitorsByTeamId = async (req) => {
|
||||
case: { $eq: ["$type", "hardware"] },
|
||||
then: "$hardwarechecks",
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "distributed_http"] },
|
||||
then: "$distributeduptimechecks",
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
},
|
||||
@@ -815,6 +883,7 @@ export {
|
||||
getMonitorById,
|
||||
getMonitorsByTeamId,
|
||||
getUptimeDetailsById,
|
||||
getDistributedUptimeDetailsById,
|
||||
createMonitor,
|
||||
deleteMonitor,
|
||||
deleteAllMonitors,
|
||||
|
||||
@@ -534,4 +534,188 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
];
|
||||
};
|
||||
|
||||
export { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline };
|
||||
const buildDistributedUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
return [
|
||||
{
|
||||
$match: {
|
||||
monitorId: monitor._id,
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
createdAt: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
$facet: {
|
||||
aggregateData: [
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
avgResponseTime: {
|
||||
$avg: "$responseTime",
|
||||
},
|
||||
lastCheck: {
|
||||
$last: "$$ROOT",
|
||||
},
|
||||
totalChecks: {
|
||||
$sum: 1,
|
||||
},
|
||||
uptBurnt: {
|
||||
$sum: "$uptBurnt",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
// For the response time chart, should return checks for date window
|
||||
// Grouped by: {day: hour}, {week: day}, {month: day}
|
||||
groupedMapChecks: [
|
||||
{
|
||||
$match: {
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
date: {
|
||||
$dateToString: {
|
||||
format: dateString,
|
||||
date: "$createdAt",
|
||||
},
|
||||
},
|
||||
city: "$city",
|
||||
lat: "$location.lat",
|
||||
lng: "$location.lng",
|
||||
},
|
||||
city: { $first: "$city" }, // Add this line to include city in output
|
||||
lat: { $first: "$location.lat" },
|
||||
lng: { $first: "$location.lng" },
|
||||
avgResponseTime: {
|
||||
$avg: "$responseTime",
|
||||
},
|
||||
totalChecks: {
|
||||
$sum: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
"_id.date": 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
groupedChecks: [
|
||||
{
|
||||
$match: {
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
$dateToString: {
|
||||
format: dateString,
|
||||
date: "$createdAt",
|
||||
},
|
||||
},
|
||||
avgResponseTime: {
|
||||
$avg: "$responseTime",
|
||||
},
|
||||
totalChecks: {
|
||||
$sum: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
_id: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
// Average response time for the date window
|
||||
groupAvgResponseTime: [
|
||||
{
|
||||
$match: {
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
avgResponseTime: {
|
||||
$avg: "$responseTime",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
latestChecks: [
|
||||
{
|
||||
$sort: { createdAt: -1 }, // Sort by newest first
|
||||
},
|
||||
{
|
||||
$limit: 5, // Get only the first 5 documents
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
responseTime: 1,
|
||||
city: 1,
|
||||
countryCode: 1,
|
||||
uptBurnt: { $toString: "$uptBurnt" },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
totalUptBurnt: {
|
||||
$toString: {
|
||||
$arrayElemAt: ["$aggregateData.uptBurnt", 0],
|
||||
},
|
||||
},
|
||||
avgResponseTime: {
|
||||
$arrayElemAt: ["$aggregateData.avgResponseTime", 0],
|
||||
},
|
||||
totalChecks: {
|
||||
$arrayElemAt: ["$aggregateData.totalChecks", 0],
|
||||
},
|
||||
latestResponseTime: {
|
||||
$arrayElemAt: ["$aggregateData.lastCheck.responseTime", 0],
|
||||
},
|
||||
timeSinceLastCheck: {
|
||||
$let: {
|
||||
vars: {
|
||||
lastCheck: {
|
||||
$arrayElemAt: ["$aggregateData.lastCheck", 0],
|
||||
},
|
||||
},
|
||||
in: {
|
||||
$cond: [
|
||||
{
|
||||
$ifNull: ["$$lastCheck", false],
|
||||
},
|
||||
{
|
||||
$subtract: [new Date(), "$$lastCheck.createdAt"],
|
||||
},
|
||||
0,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
groupedMapChecks: "$groupedMapChecks",
|
||||
groupedChecks: "$groupedChecks",
|
||||
groupedAvgResponseTime: {
|
||||
$arrayElemAt: ["$groupAvgResponseTime", 0],
|
||||
},
|
||||
latestChecks: "$latestChecks",
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export {
|
||||
buildUptimeDetailsPipeline,
|
||||
buildHardwareDetailsPipeline,
|
||||
buildDistributedUptimeDetailsPipeline,
|
||||
};
|
||||
@@ -1,11 +1,18 @@
|
||||
import StatusPage from "../../models/StatusPage.js";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import { NormalizeData } from "../../../utils/dataUtils.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageModule";
|
||||
|
||||
const createStatusPage = async (statusPageData) => {
|
||||
const createStatusPage = async (statusPageData, image) => {
|
||||
try {
|
||||
const statusPage = new StatusPage({ ...statusPageData });
|
||||
if (image) {
|
||||
statusPage.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}
|
||||
await statusPage.save();
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
@@ -20,16 +27,115 @@ const createStatusPage = async (statusPageData) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPageByUrl = async (url) => {
|
||||
const updateStatusPage = async (statusPageData, image) => {
|
||||
try {
|
||||
const statusPage = await StatusPage.findOne({ url });
|
||||
if (statusPage === null || statusPage === undefined) {
|
||||
if (image) {
|
||||
statusPageData.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}
|
||||
const statusPage = await StatusPage.findOneAndUpdate({}, statusPageData, {
|
||||
new: true,
|
||||
});
|
||||
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPage = async () => {
|
||||
try {
|
||||
const statusPageQuery = await StatusPage.aggregate([
|
||||
{ $limit: 1 },
|
||||
{
|
||||
$set: {
|
||||
originalMonitors: "$monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "monitors",
|
||||
localField: "monitors",
|
||||
foreignField: "_id",
|
||||
as: "monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: "$monitors",
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
{ $limit: 25 },
|
||||
],
|
||||
as: "monitors.checks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"monitors.orderIndex": {
|
||||
$indexOfArray: ["$originalMonitors", "$monitors._id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
statusPage: { $first: "$$ROOT" },
|
||||
monitors: { $push: "$monitors" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
statusPage: {
|
||||
_id: 1,
|
||||
color: 1,
|
||||
companyName: 1,
|
||||
isPublished: 1,
|
||||
logo: 1,
|
||||
originalMonitors: 1,
|
||||
showCharts: 1,
|
||||
showUptimePercentage: 1,
|
||||
timezone: 1,
|
||||
url: 1,
|
||||
},
|
||||
monitors: {
|
||||
$sortArray: {
|
||||
input: "$monitors",
|
||||
sortBy: { orderIndex: 1 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
if (!statusPageQuery.length) {
|
||||
const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND);
|
||||
error.status = 404;
|
||||
|
||||
throw error;
|
||||
}
|
||||
return statusPage;
|
||||
|
||||
const { statusPage, monitors } = statusPageQuery[0];
|
||||
|
||||
const normalizedMonitors = monitors.map((monitor) => {
|
||||
return {
|
||||
...monitor,
|
||||
checks: NormalizeData(monitor.checks, 10, 100),
|
||||
};
|
||||
});
|
||||
|
||||
return { statusPage, monitors: normalizedMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
@@ -37,4 +143,14 @@ const getStatusPageByUrl = async (url) => {
|
||||
}
|
||||
};
|
||||
|
||||
export { createStatusPage, getStatusPageByUrl };
|
||||
const deleteStatusPage = async () => {
|
||||
try {
|
||||
await StatusPage.deleteOne({});
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createStatusPage, updateStatusPage, getStatusPage, deleteStatusPage };
|
||||
+5
-1
@@ -252,7 +252,11 @@ const startApp = async () => {
|
||||
ServiceRegistry.get(NotificationService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const distributedUptimeController = new DistributedUptimeController();
|
||||
const distributedUptimeController = new DistributedUptimeController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
http,
|
||||
ServiceRegistry.get(StatusService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
//Create routes
|
||||
const authRoutes = new AuthRoutes(authController);
|
||||
|
||||
Generated
+15
-14
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"bcrypt": "5.1.1",
|
||||
"bullmq": "5.40.0",
|
||||
"bullmq": "5.40.2",
|
||||
"cors": "^2.8.5",
|
||||
"dockerode": "4.0.4",
|
||||
"dotenv": "^16.4.5",
|
||||
@@ -1748,9 +1748,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bullmq": {
|
||||
"version": "5.40.0",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.0.tgz",
|
||||
"integrity": "sha512-tmrk32EmcbtUOGPSdwlDUcc0w+nAMqCisk8vEFFmG8aOzIehz0BxTNSj6Grh0qoMugRF3VglWk8HGUBnWqU2Fw==",
|
||||
"version": "5.40.2",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.2.tgz",
|
||||
"integrity": "sha512-Cn4NUpwGAF4WnuXR2kTZCTAUEUHajSCn/IqiDG9ry1kVvAwwwg1Ati3J5HN2uZjqD5PBfNDXYnsc2+0PzakDwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "^4.9.0",
|
||||
@@ -4206,9 +4206,10 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.2.tgz",
|
||||
"integrity": "sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.5.0.tgz",
|
||||
"integrity": "sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
@@ -5515,9 +5516,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb": {
|
||||
"version": "6.12.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz",
|
||||
"integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz",
|
||||
"integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.1.9",
|
||||
@@ -5571,14 +5572,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.9.7",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.7.tgz",
|
||||
"integrity": "sha512-mvNXmU0V8qZzMR/qoK2mjT4Ti2ALdtfS0teK+twxhlGkwzOD76V02/zWajTu2MJ7QyEmZe9OWvnJsIY0iAuX3Q==",
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.10.0.tgz",
|
||||
"integrity": "sha512-nLhk3Qrv6q/HpD2k1O7kbBqsq+/kmKpdv5KJ+LLhQlII3e1p/SSLoLP6jMuSiU6+iLK7zFw4T1niAk3mA3QVug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bson": "^6.10.1",
|
||||
"kareem": "2.6.3",
|
||||
"mongodb": "~6.12.0",
|
||||
"mongodb": "~6.13.0",
|
||||
"mpath": "0.9.0",
|
||||
"mquery": "5.0.0",
|
||||
"ms": "2.1.3",
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"bcrypt": "5.1.1",
|
||||
"bullmq": "5.40.0",
|
||||
"bullmq": "5.40.2",
|
||||
"cors": "^2.8.5",
|
||||
"dockerode": "4.0.4",
|
||||
"dotenv": "^16.4.5",
|
||||
|
||||
@@ -8,6 +8,14 @@ class DistributedUptimeRoutes {
|
||||
}
|
||||
initRoutes() {
|
||||
this.router.post("/callback", this.distributedUptimeController.resultsCallback);
|
||||
this.router.get(
|
||||
"/monitors/:teamId",
|
||||
this.distributedUptimeController.getDistributedUptimeMonitors
|
||||
);
|
||||
this.router.get(
|
||||
"/monitors/details/:monitorId",
|
||||
this.distributedUptimeController.getDistributedUptimeMonitorDetails
|
||||
);
|
||||
}
|
||||
|
||||
getRouter() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Router } from "express";
|
||||
|
||||
import { verifyJWT } from "../middleware/verifyJWT.js";
|
||||
import multer from "multer";
|
||||
const upload = multer();
|
||||
|
||||
class StatusPageRoutes {
|
||||
constructor(statusPageController) {
|
||||
@@ -10,8 +11,20 @@ class StatusPageRoutes {
|
||||
}
|
||||
|
||||
initRoutes() {
|
||||
this.router.get("/:url", this.statusPageController.getStatusPageByUrl);
|
||||
this.router.post("/:url", verifyJWT, this.statusPageController.createStatusPage);
|
||||
this.router.get("/", this.statusPageController.getStatusPage);
|
||||
this.router.post(
|
||||
"/",
|
||||
upload.single("logo"),
|
||||
verifyJWT,
|
||||
this.statusPageController.createStatusPage
|
||||
);
|
||||
this.router.put(
|
||||
"/",
|
||||
upload.single("logo"),
|
||||
verifyJWT,
|
||||
this.statusPageController.updateStatusPage
|
||||
);
|
||||
this.router.delete("/", verifyJWT, this.statusPageController.deleteStatusPage);
|
||||
}
|
||||
|
||||
getRouter() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const QUEUE_NAMES = ["uptime", "pagespeed", "hardware"];
|
||||
const QUEUE_NAMES = ["uptime", "pagespeed", "hardware", "distributed"];
|
||||
const SERVICE_NAME = "JobQueue";
|
||||
const JOBS_PER_WORKER = 5;
|
||||
const QUEUE_LOOKUP = {
|
||||
@@ -8,6 +8,7 @@ const QUEUE_LOOKUP = {
|
||||
port: "uptime",
|
||||
docker: "uptime",
|
||||
pagespeed: "pagespeed",
|
||||
distributed_http: "distributed",
|
||||
};
|
||||
const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`;
|
||||
|
||||
@@ -163,6 +164,9 @@ class NewJobQueue {
|
||||
// Get the current status
|
||||
await job.updateProgress(30);
|
||||
const networkResponse = await this.networkService.getStatus(job);
|
||||
if (job.data.type === "distributed_http") {
|
||||
return;
|
||||
}
|
||||
// Handle status change
|
||||
await job.updateProgress(60);
|
||||
const { monitor, statusChanged, prevStatus } =
|
||||
@@ -219,51 +223,6 @@ class NewJobQueue {
|
||||
connection: this.connection,
|
||||
concurrency: 5,
|
||||
});
|
||||
|
||||
// worker.on("active", (job) => {
|
||||
// this.logger.info({
|
||||
// message: `Worker started processing job: ${job.id}`,
|
||||
// service: SERVICE_NAME,
|
||||
// method: "createWorker",
|
||||
// });
|
||||
// });
|
||||
|
||||
// worker.on("completed", (job) => {
|
||||
// this.logger.info({
|
||||
// message: `Worker completed job: ${job.id}`,
|
||||
// service: SERVICE_NAME,
|
||||
// method: "createWorker",
|
||||
// });
|
||||
// });
|
||||
|
||||
// // Log job progress updates
|
||||
// worker.on("progress", (job, progress) => {
|
||||
// this.logger.info({
|
||||
// message: `Job progress: ${job.id}`,
|
||||
// service: SERVICE_NAME,
|
||||
// method: "createWorker",
|
||||
// details: `Progress: ${progress}%`,
|
||||
// });
|
||||
// });
|
||||
|
||||
// // Log when a job fails
|
||||
// worker.on("failed", (job, err) => {
|
||||
// this.logger.error({
|
||||
// message: `Worker failed job: ${job.id}`,
|
||||
// service: SERVICE_NAME,
|
||||
// method: "createWorker",
|
||||
// details: `Error: ${err.message}`,
|
||||
// stack: err.stack,
|
||||
// });
|
||||
// });
|
||||
|
||||
// worker.on("stalled", (jobId) => {
|
||||
// this.logger.warn({
|
||||
// message: `Worker stalled job: ${jobId}`,
|
||||
// service: SERVICE_NAME,
|
||||
// method: "createWorker",
|
||||
// });
|
||||
// });
|
||||
return worker;
|
||||
}
|
||||
|
||||
@@ -392,7 +351,6 @@ class NewJobQueue {
|
||||
const jobs = await queue.getJobs();
|
||||
const ret = await Promise.all(
|
||||
jobs.map(async (job) => {
|
||||
console.log(job);
|
||||
const state = await job.getState();
|
||||
return { url: job.data.url, state, progress: job.progress };
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { errorMessages, successMessages } from "../utils/messages.js";
|
||||
const SERVICE_NAME = "NetworkService";
|
||||
const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
|
||||
|
||||
/**
|
||||
* Constructs a new NetworkService instance.
|
||||
@@ -19,6 +20,7 @@ class NetworkService {
|
||||
this.TYPE_HARDWARE = "hardware";
|
||||
this.TYPE_DOCKER = "docker";
|
||||
this.TYPE_PORT = "port";
|
||||
this.TYPE_DISTRIBUTED_HTTP = "distributed_http";
|
||||
this.SERVICE_NAME = SERVICE_NAME;
|
||||
this.NETWORK_ERROR = 5000;
|
||||
this.PING_ERROR = 5001;
|
||||
@@ -323,6 +325,35 @@ class NetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
async requestDistributedHttp(job) {
|
||||
try {
|
||||
const monitor = job.data;
|
||||
const CALLBACK_URL = process.env.NGROK_URL
|
||||
? process.env.NGROK_URL
|
||||
: process.env.CALLBACK_URL;
|
||||
|
||||
const response = await this.axios.post(
|
||||
UPROCK_ENDPOINT,
|
||||
{
|
||||
id: monitor._id,
|
||||
url: monitor.url,
|
||||
callback: `${CALLBACK_URL}/api/v1/distributed-uptime/callback`,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-checkmate-key": process.env.UPROCK_API_KEY,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
error.service = this.SERVICE_NAME;
|
||||
error.method = "requestDistributedHttp";
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles unsupported job types by throwing an error with details.
|
||||
*
|
||||
@@ -406,6 +437,8 @@ class NetworkService {
|
||||
return await this.requestDocker(job);
|
||||
case this.TYPE_PORT:
|
||||
return await this.requestPort(job);
|
||||
case this.TYPE_DISTRIBUTED_HTTP:
|
||||
return await this.requestDistributedHttp(job);
|
||||
|
||||
default:
|
||||
return this.handleUnsupportedType(type);
|
||||
|
||||
@@ -25,7 +25,7 @@ class StatusService {
|
||||
* @param {Object} networkResponse - The network response containing monitorId and status.
|
||||
* @param {string} networkResponse.monitorId - The ID of the monitor.
|
||||
* @param {string} networkResponse.status - The new status of the monitor.
|
||||
* @returns {Promise<Object>} - A promise that resolves to an object containing the monitor, statusChanged flag, and previous status if the status changed, or false if an error occurred.
|
||||
* @returns {Promise<Object>} - A promise that resolves to an object containinfg the monitor, statusChanged flag, and previous status if the status changed, or false if an error occurred.
|
||||
* @returns {Promise<Object>} returnObject - The object returned by the function.
|
||||
* @returns {Object} returnObject.monitor - The monitor object.
|
||||
* @returns {boolean} returnObject.statusChanged - Flag indicating if the status has changed.
|
||||
@@ -83,8 +83,23 @@ class StatusService {
|
||||
* @returns {Object} The check object.
|
||||
*/
|
||||
buildCheck = (networkResponse) => {
|
||||
const { monitorId, teamId, type, status, responseTime, code, message, payload } =
|
||||
networkResponse;
|
||||
const {
|
||||
monitorId,
|
||||
teamId,
|
||||
type,
|
||||
status,
|
||||
responseTime,
|
||||
code,
|
||||
message,
|
||||
payload,
|
||||
first_byte_took,
|
||||
body_read_took,
|
||||
dns_took,
|
||||
conn_took,
|
||||
connect_took,
|
||||
tls_took,
|
||||
} = networkResponse;
|
||||
|
||||
const check = {
|
||||
monitorId,
|
||||
teamId,
|
||||
@@ -92,8 +107,28 @@ class StatusService {
|
||||
statusCode: code,
|
||||
responseTime,
|
||||
message,
|
||||
first_byte_took,
|
||||
body_read_took,
|
||||
dns_took,
|
||||
conn_took,
|
||||
connect_took,
|
||||
tls_took,
|
||||
};
|
||||
|
||||
if (type === "distributed_http") {
|
||||
check.continent = payload.continent;
|
||||
check.countryCode = payload.country_code;
|
||||
check.city = payload.city;
|
||||
check.location = payload.location;
|
||||
check.uptBurnt = payload.upt_burnt;
|
||||
check.first_byte_took = payload.first_byte_took;
|
||||
check.body_read_took = payload.body_read_took;
|
||||
check.dns_took = payload.dns_took;
|
||||
check.conn_took = payload.conn_took;
|
||||
check.connect_took = payload.connect_took;
|
||||
check.tls_took = payload.tls_took;
|
||||
}
|
||||
|
||||
if (type === "pagespeed") {
|
||||
const categories = payload.lighthouseResult?.categories;
|
||||
const audits = payload.lighthouseResult?.audits;
|
||||
@@ -145,6 +180,7 @@ class StatusService {
|
||||
hardware: this.db.createHardwareCheck,
|
||||
docker: this.db.createCheck,
|
||||
port: this.db.createCheck,
|
||||
distributed_http: this.db.createDistributedCheck,
|
||||
};
|
||||
const operation = operationMap[networkResponse.type];
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ const successMessages = {
|
||||
// Status Page
|
||||
STATUS_PAGE_BY_URL: "Got status page by url successfully",
|
||||
STATUS_PAGE_CREATE: "Status page created successfully",
|
||||
|
||||
STATUS_PAGE_DELETE: "Status page deleted successfully",
|
||||
STATUS_PAGE_UPDATE: "Status page updated successfully",
|
||||
// Docker
|
||||
DOCKER_SUCCESS: "Docker container status fetched successfully",
|
||||
|
||||
|
||||
@@ -423,7 +423,6 @@ const createStatusPageBodyValidation = joi.object({
|
||||
url: joi.string().required(),
|
||||
timezone: joi.string().required(),
|
||||
color: joi.string().required(),
|
||||
theme: joi.string().required(),
|
||||
monitors: joi
|
||||
.array()
|
||||
.items(joi.string().pattern(/^[0-9a-fA-F]{24}$/))
|
||||
@@ -434,8 +433,35 @@ const createStatusPageBodyValidation = joi.object({
|
||||
"array.empty": "At least one monitor is required",
|
||||
"any.required": "Monitors are required",
|
||||
}),
|
||||
isPublished: joi.boolean(),
|
||||
showCharts: joi.boolean(),
|
||||
showUptimePercentage: joi.boolean(),
|
||||
});
|
||||
|
||||
const imageValidation = joi
|
||||
.object({
|
||||
fieldname: joi.string().required(),
|
||||
originalname: joi.string().required(),
|
||||
encoding: joi.string().required(),
|
||||
mimetype: joi
|
||||
.string()
|
||||
.valid("image/jpeg", "image/png", "image/jpg")
|
||||
.required()
|
||||
.messages({
|
||||
"string.valid": "File must be a valid image (jpeg, jpg, or png)",
|
||||
}),
|
||||
size: joi.number().max(3145728).required().messages({
|
||||
"number.max": "File size must be less than 3MB",
|
||||
}),
|
||||
buffer: joi.binary().required(),
|
||||
destination: joi.string(),
|
||||
filename: joi.string(),
|
||||
path: joi.string(),
|
||||
})
|
||||
.messages({
|
||||
"any.required": "Image file is required",
|
||||
});
|
||||
|
||||
export {
|
||||
roleValidatior,
|
||||
loginValidation,
|
||||
@@ -493,4 +519,5 @@ export {
|
||||
updateAppSettingsBodyValidation,
|
||||
createStatusPageBodyValidation,
|
||||
getStatusPageParamValidation,
|
||||
imageValidation,
|
||||
};
|
||||
Reference in new issue
Block a user