add backend changes

This commit is contained in:
Alex Holliday committed 2025-08-18 10:55:12 -07:00
1 parent 5463542532
commit 155adf549e
17 files changed
+228 -643

No files matched your search

+1 -15
View File
@@ -47,8 +47,6 @@ import { ParseBoolean } from "../utils/utils.js";
// Models
import Check from "../db/models/Check.js";
import HardwareCheck from "../db/models/HardwareCheck.js";
import PageSpeedCheck from "../db/models/PageSpeedCheck.js";
import Monitor from "../db/models/Monitor.js";
import User from "../db/models/User.js";
import InviteToken from "../db/models/InviteToken.js";
@@ -56,7 +54,6 @@ 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 NetworkCheck from "../db/models/NetworkCheck.js";
import Notification from "../db/models/Notification.js";
import RecoveryToken from "../db/models/RecoveryToken.js";
import AppSettings from "../db/models/AppSettings.js";
@@ -65,12 +62,9 @@ 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 HardwareCheckModule from "../db/mongo/modules/hardwareCheckModule.js";
import MaintenanceWindowModule from "../db/mongo/modules/maintenanceWindowModule.js";
import MonitorModule from "../db/mongo/modules/monitorModule.js";
import NetworkCheckModule from "../db/mongo/modules/networkCheckModule.js";
import NotificationModule from "../db/mongo/modules/notificationModule.js";
import PageSpeedCheckModule from "../db/mongo/modules/pageSpeedCheckModule.js";
import RecoveryModule from "../db/mongo/modules/recoveryModule.js";
import SettingsModule from "../db/mongo/modules/settingsModule.js";
@@ -84,18 +78,15 @@ export const initializeServices = async ({ logger, envSettings, settingsService
const stringService = new StringService(translationService);
// Create DB
const checkModule = new CheckModule({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User });
const checkModule = new CheckModule({ logger, Check, Monitor, User });
const inviteModule = new InviteModule({ InviteToken, crypto, stringService });
const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, stringService });
const userModule = new UserModule({ User, Team, GenerateAvatarImage, ParseBoolean, stringService });
const hardwareCheckModule = new HardwareCheckModule({ HardwareCheck, Monitor, logger });
const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow });
const monitorModule = new MonitorModule({
Monitor,
MonitorStats,
Check,
PageSpeedCheck,
HardwareCheck,
stringService,
fs,
path,
@@ -104,9 +95,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService
NormalizeData,
NormalizeDataUptimeDetails,
});
const networkCheckModule = new NetworkCheckModule({ NetworkCheck });
const notificationModule = new NotificationModule({ Notification, Monitor });
const pageSpeedCheckModule = new PageSpeedCheckModule({ PageSpeedCheck });
const recoveryModule = new RecoveryModule({ User, RecoveryToken, crypto, stringService });
const settingsModule = new SettingsModule({ AppSettings });
@@ -117,12 +106,9 @@ export const initializeServices = async ({ logger, envSettings, settingsService
inviteModule,
statusPageModule,
userModule,
hardwareCheckModule,
maintenanceWindowModule,
monitorModule,
networkCheckModule,
notificationModule,
pageSpeedCheckModule,
recoveryModule,
settingsModule,
});
+166 -98
View File
@@ -1,108 +1,176 @@
import mongoose from "mongoose";
const BaseCheckSchema = mongoose.Schema({
/**
* Reference to the associated Monitor document.
*
* @type {mongoose.Schema.Types.ObjectId}
*/
monitorId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
immutable: true,
index: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
index: true,
},
/**
* Status of the check (true for up, false for down).
*
* @type {Boolean}
*/
status: {
type: Boolean,
index: true,
},
/**
* Response time of the check in milliseconds.
*
* @type {Number}
*/
responseTime: {
type: Number,
},
/**
* Timings from Got.
*
* @type {Object}
*/
timings: {
type: Object, // Accepts any object
default: {},
},
/**
* HTTP status code received during the check.
*
* @type {Number}
*/
statusCode: {
type: Number,
index: true,
},
/**
* Message or description of the check result.
*
* @type {String}
*/
message: {
type: String,
},
/**
* Expiry date of the check, auto-calculated to expire after 30 days.
*
* @type {Date}
*/
expiry: {
type: Date,
default: Date.now,
expires: 60 * 60 * 24 * 30, // 30 days
},
/**
* Acknowledgment of the check.
*
* @type {Boolean}
*/
ack: {
type: Boolean,
default: false,
},
/**
* Resolution date of the check (when the check was resolved).
*
* @type {Date}
*/
ackAt: {
type: Date,
},
const cpuSchema = mongoose.Schema({
physical_core: { type: Number, default: 0 },
logical_core: { type: Number, default: 0 },
frequency: { type: Number, default: 0 },
temperature: { type: [Number], default: [] },
free_percent: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
});
/**
* Check Schema for MongoDB collection.
*
* Represents a check associated with a monitor, storing information
* about the status and response of a particular check event.
*/
const CheckSchema = mongoose.Schema({ ...BaseCheckSchema.obj }, { timestamps: true });
const memorySchema = mongoose.Schema({
total_bytes: { type: Number, default: 0 },
available_bytes: { type: Number, default: 0 },
used_bytes: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
});
const diskSchema = mongoose.Schema({
read_speed_bytes: { type: Number, default: 0 },
write_speed_bytes: { type: Number, default: 0 },
total_bytes: { type: Number, default: 0 },
free_bytes: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
});
const hostSchema = mongoose.Schema({
os: { type: String, default: "" },
platform: { type: String, default: "" },
kernel_version: { type: String, default: "" },
});
const errorSchema = mongoose.Schema({
metric: { type: [String], default: [] },
err: { type: String, default: "" },
});
const captureSchema = mongoose.Schema({
version: { type: String, default: "" },
mode: { type: String, default: "" },
});
const networkInterfaceSchema = mongoose.Schema({
name: { type: String },
bytes_sent: { type: Number, default: 0 },
bytes_recv: { type: Number, default: 0 },
packets_sent: { type: Number, default: 0 },
packets_recv: { type: Number, default: 0 },
err_in: { type: Number, default: 0 },
err_out: { type: Number, default: 0 },
drop_in: { type: Number, default: 0 },
drop_out: { type: Number, default: 0 },
fifo_in: { type: Number, default: 0 },
fifo_out: { type: Number, default: 0 },
});
const CheckSchema = new mongoose.Schema(
{
// Common fields
monitorId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
immutable: true,
index: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
index: true,
},
type: {
type: String,
enum: ["http", "hardware", "pagespeed", "distributed"],
required: true,
index: true,
},
status: {
type: Boolean,
index: true,
},
responseTime: {
type: Number,
},
timings: {
type: Object,
default: {},
},
statusCode: {
type: Number,
index: true,
},
message: {
type: String,
},
expiry: {
type: Date,
default: Date.now,
expires: 60 * 60 * 24 * 30, // 30 days
},
ack: {
type: Boolean,
default: false,
},
ackAt: {
type: Date,
},
// Hardware fields
cpu: {
type: cpuSchema,
default: () => ({}),
},
memory: {
type: memorySchema,
default: () => ({}),
},
disk: {
type: [diskSchema],
default: () => [],
},
host: {
type: hostSchema,
default: () => ({}),
},
errors: {
type: [errorSchema],
default: () => [],
},
capture: {
type: captureSchema,
default: () => ({}),
},
net: {
type: [networkInterfaceSchema],
default: () => [],
},
// PageSpeed fields
accessibility: {
type: Number,
},
bestPractices: {
type: Number,
},
seo: {
type: Number,
},
performance: {
type: Number,
},
audits: {
type: Object,
},
},
{ timestamps: true }
);
CheckSchema.index({ updatedAt: 1 });
CheckSchema.index({ monitorId: 1, updatedAt: 1 });
CheckSchema.index({ monitorId: 1, updatedAt: -1 });
CheckSchema.index({ teamId: 1, updatedAt: -1 });
export default mongoose.model("Check", CheckSchema);
export { BaseCheckSchema };
-100
View File
@@ -1,100 +0,0 @@
import mongoose from "mongoose";
import { BaseCheckSchema } from "./Check.js";
const cpuSchema = mongoose.Schema({
physical_core: { type: Number, default: 0 },
logical_core: { type: Number, default: 0 },
frequency: { type: Number, default: 0 },
temperature: { type: [Number], default: [] },
free_percent: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
});
const memorySchema = mongoose.Schema({
total_bytes: { type: Number, default: 0 },
available_bytes: { type: Number, default: 0 },
used_bytes: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
});
const diskSchema = mongoose.Schema({
read_speed_bytes: { type: Number, default: 0 },
write_speed_bytes: { type: Number, default: 0 },
total_bytes: { type: Number, default: 0 },
free_bytes: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
});
const hostSchema = mongoose.Schema({
os: { type: String, default: "" },
platform: { type: String, default: "" },
kernel_version: { type: String, default: "" },
});
const errorSchema = mongoose.Schema({
metric: { type: [String], default: [] },
err: { type: String, default: "" },
});
const captureSchema = mongoose.Schema({
version: { type: String, default: "" },
mode: { type: String, default: "" },
});
const networkInterfaceSchema = mongoose.Schema({
name: { type: String },
bytes_sent: { type: Number, default: 0 },
bytes_recv: { type: Number, default: 0 },
packets_sent: { type: Number, default: 0 },
packets_recv: { type: Number, default: 0 },
err_in: { type: Number, default: 0 },
err_out: { type: Number, default: 0 },
drop_in: { type: Number, default: 0 },
drop_out: { type: Number, default: 0 },
fifo_in: { type: Number, default: 0 },
fifo_out: { type: Number, default: 0 },
});
const HardwareCheckSchema = mongoose.Schema(
{
...BaseCheckSchema.obj,
cpu: {
type: cpuSchema,
default: () => ({}),
},
memory: {
type: memorySchema,
default: () => ({}),
},
disk: {
type: [diskSchema],
default: () => [],
},
host: {
type: hostSchema,
default: () => ({}),
},
errors: {
type: [errorSchema],
default: () => [],
},
capture: {
type: captureSchema,
default: () => ({}),
},
net: {
type: [networkInterfaceSchema],
default: () => [],
required: false,
},
},
{ timestamps: true }
);
HardwareCheckSchema.index({ createdAt: 1 });
HardwareCheckSchema.index({ monitorId: 1, createdAt: 1 });
HardwareCheckSchema.index({ monitorId: 1, createdAt: -1 });
export default mongoose.model("HardwareCheck", HardwareCheckSchema);
+2 -16
View File
@@ -1,6 +1,4 @@
import mongoose from "mongoose";
import HardwareCheck from "./HardwareCheck.js";
import PageSpeedCheck from "./PageSpeedCheck.js";
import Check from "./Check.js";
import MonitorStats from "./MonitorStats.js";
import StatusPage from "./StatusPage.js";
@@ -133,13 +131,7 @@ MonitorSchema.pre("findOneAndDelete", async function (next) {
throw new Error("Monitor not found");
}
if (doc?.type === "pagespeed") {
await PageSpeedCheck.deleteMany({ monitorId: doc._id });
} else if (doc?.type === "hardware") {
await HardwareCheck.deleteMany({ monitorId: doc._id });
} else {
await Check.deleteMany({ monitorId: doc._id });
}
await Check.deleteMany({ monitorId: doc._id });
// Deal with status pages
await StatusPage.updateMany({ monitors: doc?._id }, { $pull: { monitors: doc?._id } });
@@ -156,13 +148,7 @@ MonitorSchema.pre("deleteMany", async function (next) {
const monitors = await this.model.find(filter).select(["_id", "type"]).lean();
for (const monitor of monitors) {
if (monitor.type === "pagespeed") {
await PageSpeedCheck.deleteMany({ monitorId: monitor._id });
} else if (monitor.type === "hardware") {
await HardwareCheck.deleteMany({ monitorId: monitor._id });
} else {
await Check.deleteMany({ monitorId: monitor._id });
}
await Check.deleteMany({ monitorId: monitor._id });
await StatusPage.updateMany({ monitors: monitor._id }, { $pull: { monitors: monitor._id } });
await MonitorStats.deleteMany({ monitorId: monitor._id.toString() });
}
-4
View File
@@ -40,10 +40,6 @@ const MonitorStatsSchema = new mongoose.Schema(
type: Number,
default: 0,
},
uptBurnt: {
type: mongoose.Schema.Types.Decimal128,
required: false,
},
},
{ timestamps: true }
);
-46
View File
@@ -1,46 +0,0 @@
import mongoose from "mongoose";
import { BaseCheckSchema } from "./Check.js";
const networkInterfaceSchema = mongoose.Schema({
name: { type: String, required: true },
bytes_sent: { type: Number, default: 0 },
bytes_recv: { type: Number, default: 0 },
packets_sent: { type: Number, default: 0 },
packets_recv: { type: Number, default: 0 },
err_in: { type: Number, default: 0 },
err_out: { type: Number, default: 0 },
drop_in: { type: Number, default: 0 },
drop_out: { type: Number, default: 0 },
fifo_in: { type: Number, default: 0 },
fifo_out: { type: Number, default: 0 },
});
const captureSchema = mongoose.Schema({
version: { type: String, default: "" },
mode: { type: String, default: "" },
});
const NetworkCheckSchema = mongoose.Schema(
{
...BaseCheckSchema.obj,
data: {
type: [networkInterfaceSchema],
default: () => [],
},
capture: {
type: captureSchema,
default: () => ({}),
},
errors: {
type: mongoose.Schema.Types.Mixed,
default: null,
},
},
{ timestamps: true }
);
NetworkCheckSchema.index({ createdAt: 1 });
NetworkCheckSchema.index({ monitorId: 1, createdAt: 1 });
NetworkCheckSchema.index({ monitorId: 1, createdAt: -1 });
export default mongoose.model("NetworkCheck", NetworkCheckSchema);
-83
View File
@@ -1,83 +0,0 @@
import mongoose from "mongoose";
import { BaseCheckSchema } from "./Check.js";
const AuditSchema = mongoose.Schema({
id: { type: String, required: true },
title: { type: String, required: true },
description: { type: String, required: true },
score: { type: Number, required: true },
scoreDisplayMode: { type: String, required: true },
displayValue: { type: String, required: true },
numericValue: { type: Number, required: true },
numericUnit: { type: String, required: true },
});
const AuditsSchema = mongoose.Schema({
cls: {
type: AuditSchema,
required: true,
},
si: {
type: AuditSchema,
required: true,
},
fcp: {
type: AuditSchema,
required: true,
},
lcp: {
type: AuditSchema,
required: true,
},
tbt: {
type: AuditSchema,
required: true,
},
});
/**
* Mongoose schema for storing metrics from Google Lighthouse.
* @typedef {Object} PageSpeedCheck
* @property {mongoose.Schema.Types.ObjectId} monitorId - Reference to the Monitor model.
* @property {number} accessibility - Accessibility score.
* @property {number} bestPractices - Best practices score.
* @property {number} seo - SEO score.
* @property {number} performance - Performance score.
*/
const PageSpeedCheck = mongoose.Schema(
{
...BaseCheckSchema.obj,
accessibility: {
type: Number,
required: true,
},
bestPractices: {
type: Number,
required: true,
},
seo: {
type: Number,
required: true,
},
performance: {
type: Number,
required: true,
},
audits: {
type: AuditsSchema,
required: true,
},
},
{ timestamps: true }
);
/**
* Mongoose model for storing metrics from Google Lighthouse.
* @typedef {mongoose.Model<PageSpeedCheck>} LighthouseMetricsModel
*/
PageSpeedCheck.index({ createdAt: 1 });
PageSpeedCheck.index({ monitorId: 1, createdAt: 1 });
PageSpeedCheck.index({ monitorId: 1, createdAt: -1 });
export default mongoose.model("PageSpeedCheck", PageSpeedCheck);
+2 -26
View File
@@ -31,7 +31,7 @@ class CheckModule {
}
};
getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
getChecksByMonitor = async ({ monitorId, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
try {
status = status === "true" ? true : status === "false" ? false : undefined;
page = parseInt(page);
@@ -79,19 +79,7 @@ class CheckModule {
skip = page * rowsPerPage;
}
const checkModels = {
http: this.Check,
ping: this.Check,
docker: this.Check,
port: this.Check,
pagespeed: this.PageSpeedCheck,
hardware: this.HardwareCheck,
game: this.Check,
};
const Model = checkModels[type];
const checks = await Model.aggregate([
const checks = await this.Check.aggregate([
{ $match: matchStage },
{ $sort: { createdAt: sortOrder } },
{
@@ -166,18 +154,6 @@ class CheckModule {
const aggregatePipeline = [
{ $match: matchStage },
{
$unionWith: {
coll: "hardwarechecks",
pipeline: [{ $match: matchStage }],
},
},
{
$unionWith: {
coll: "pagespeedchecks",
pipeline: [{ $match: matchStage }],
},
},
{ $sort: { createdAt: sortOrder } },
{
@@ -1,22 +0,0 @@
const SERVICE_NAME = "hardwareCheckModule";
class HardwareCheckModule {
constructor({ HardwareCheck, Monitor, logger }) {
this.HardwareCheck = HardwareCheck;
this.Monitor = Monitor;
this.logger = logger;
}
createHardwareChecks = async (hardwareChecks) => {
try {
await this.HardwareCheck.insertMany(hardwareChecks, { ordered: false });
return true;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "createHardwareChecks";
throw error;
}
};
}
export default HardwareCheckModule;
+10 -36
View File
@@ -11,25 +11,10 @@ import {
const SERVICE_NAME = "monitorModule";
class MonitorModule {
constructor({
Monitor,
MonitorStats,
Check,
PageSpeedCheck,
HardwareCheck,
stringService,
fs,
path,
fileURLToPath,
ObjectId,
NormalizeData,
NormalizeDataUptimeDetails,
}) {
constructor({ Monitor, MonitorStats, Check, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) {
this.Monitor = Monitor;
this.MonitorStats = MonitorStats;
this.Check = Check;
this.PageSpeedCheck = PageSpeedCheck;
this.HardwareCheck = HardwareCheck;
this.stringService = stringService;
this.fs = fs;
this.path = path;
@@ -38,15 +23,6 @@ class MonitorModule {
this.NormalizeData = NormalizeData;
this.NormalizeDataUptimeDetails = NormalizeDataUptimeDetails;
this.CHECK_MODEL_LOOKUP = {
http: Check,
ping: Check,
docker: Check,
port: Check,
pagespeed: PageSpeedCheck,
hardware: HardwareCheck,
};
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -146,19 +122,18 @@ class MonitorModule {
};
//Helper
getMonitorChecks = async (monitorId, model, dateRange, sortOrder) => {
getMonitorChecks = async (monitorId, dateRange, sortOrder) => {
const indexSpec = {
monitorId: 1,
createdAt: sortOrder, // This will be 1 or -1
updatedAt: sortOrder, // This will be 1 or -1
};
const [checksAll, checksForDateRange] = await Promise.all([
model.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
model
.find({
monitorId,
createdAt: { $gte: dateRange.start, $lte: dateRange.end },
})
this.Check.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
this.Check.find({
monitorId,
createdAt: { $gte: dateRange.start, $lte: dateRange.end },
})
.hint(indexSpec)
.lean(),
]);
@@ -295,9 +270,8 @@ class MonitorModule {
const sort = sortOrder === "asc" ? 1 : -1;
// Get Checks for monitor in date range requested
const model = this.CHECK_MODEL_LOOKUP[monitor.type];
const dates = this.getDateRange(dateRange);
const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, model, dates, sort);
const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, dates, sort);
// Build monitor stats
const monitorStats = {
@@ -339,7 +313,7 @@ class MonitorModule {
};
const dateString = formatLookup[dateRange];
const hardwareStats = await this.HardwareCheck.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
const hardwareStats = await this.Check.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
const stats = hardwareStats[0];
@@ -5,12 +5,12 @@ const buildUptimeDetailsPipeline = (monitorId, dates, dateString) => {
{
$match: {
monitorId: new ObjectId(monitorId),
createdAt: { $gte: dates.start, $lte: dates.end },
updatedAt: { $gte: dates.start, $lte: dates.end },
},
},
{
$sort: {
createdAt: 1,
updatedAt: 1,
},
},
{
@@ -173,6 +173,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
{
$match: {
monitorId: monitor._id,
type: "hardware",
createdAt: { $gte: dates.start, $lte: dates.end },
},
},
@@ -225,7 +226,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
{
$lookup: {
from: "hardwarechecks",
from: "checks",
let: {
diskCount: "$diskCount",
netCount: "$netCount",
@@ -987,11 +988,6 @@ const buildMonitorsWithChecksByTeamIdPipeline = ({ matchStage, filter, page, row
// Add checks
if (limit) {
let checksCollection = "checks";
if (type === "pagespeed") {
checksCollection = "pagespeedchecks";
} else if (type === "hardware") {
checksCollection = "hardwarechecks";
}
monitorsPipeline.push({
$lookup: {
from: checksCollection,
@@ -1057,11 +1053,7 @@ const buildFilteredMonitorsByTeamIdPipeline = ({ matchStage, filter, page, rowsP
// Add checks
if (limit) {
let checksCollection = "checks";
if (type === "pagespeed") {
checksCollection = "pagespeedchecks";
} else if (type === "hardware") {
checksCollection = "hardwarechecks";
}
pipeline.push({
$lookup: {
from: checksCollection,
@@ -1175,46 +1167,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
},
]
: []),
...(limit
? [
{
$lookup: {
from: "pagespeedchecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
},
},
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "pagespeedchecks",
},
},
]
: []),
...(limit
? [
{
$lookup: {
from: "hardwarechecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
},
},
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "hardwarechecks",
},
},
]
: []),
{
$addFields: {
@@ -1225,14 +1177,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
case: { $in: ["$type", ["http", "ping", "docker", "port", "game"]] },
then: "$standardchecks",
},
{
case: { $eq: ["$type", "pagespeed"] },
then: "$pagespeedchecks",
},
{
case: { $eq: ["$type", "hardware"] },
then: "$hardwarechecks",
},
],
default: [],
},
@@ -1242,8 +1186,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
{
$project: {
standardchecks: 0,
pagespeedchecks: 0,
hardwarechecks: 0,
},
},
],
@@ -1,30 +0,0 @@
const SERVICE_NAME = "networkCheckModule";
class NetworkCheckModule {
constructor({ NetworkCheck }) {
this.NetworkCheck = NetworkCheck;
}
createNetworkCheck = async (networkCheckData) => {
try {
const networkCheck = await new this.NetworkCheck(networkCheckData);
await networkCheck.save();
return networkCheck;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "createNetworkCheck";
throw error;
}
};
getNetworkChecksByMonitorId = async (monitorId, limit = 100) => {
try {
const networkChecks = await this.NetworkCheck.find({ monitorId }).sort({ createdAt: -1 }).limit(limit);
return networkChecks;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "getNetworkChecksByMonitorId";
throw error;
}
};
}
export default NetworkCheckModule;
@@ -1,31 +0,0 @@
// import PageSpeedCheck from "../../models/PageSpeedCheck.js";
const SERVICE_NAME = "pageSpeedCheckModule";
class PageSpeedCheckModule {
constructor({ PageSpeedCheck }) {
this.PageSpeedCheck = PageSpeedCheck;
}
createPageSpeedChecks = async (pageSpeedChecks) => {
try {
await this.PageSpeedCheck.insertMany(pageSpeedChecks, { ordered: false });
return true;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "createPageSpeedCheck";
throw error;
}
};
deletePageSpeedChecksByMonitorId = async (monitorId) => {
try {
const result = await this.PageSpeedCheck.deleteMany({ monitorId });
return result.deletedCount;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "deletePageSpeedChecksByMonitorId";
throw error;
}
};
}
export default PageSpeedCheckModule;
+1 -2
View File
@@ -33,10 +33,9 @@ class CheckService {
throw this.errorService.createAuthorizationError();
}
let { type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = query;
let { sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = query;
const result = await this.db.checkModule.getChecksByMonitor({
monitorId,
type,
sortOrder,
dateRange,
filter,
@@ -2,6 +2,16 @@ const SERVICE_NAME = "JobQueueHelper";
class SuperSimpleQueueHelper {
static SERVICE_NAME = SERVICE_NAME;
/**
* @param {{
* db: import("../database").Database,
* logger: import("../logger").Logger,
* networkService: import("../networkService").NetworkService,
* statusService: import("../statusService").StatusService,
* notificationService: import("../notificationService").NotificationService
* }}
*/
constructor({ db, logger, networkService, statusService, notificationService }) {
this.db = db;
this.logger = logger;
@@ -1,13 +1,4 @@
const SERVICE_NAME = "BufferService";
const TYPE_MAP = {
http: "checks",
ping: "checks",
port: "checks",
docker: "checks",
pagespeed: "pagespeedChecks",
hardware: "hardwareChecks",
game: "checks",
};
class BufferService {
static SERVICE_NAME = SERVICE_NAME;
@@ -16,17 +7,7 @@ class BufferService {
this.db = db;
this.logger = logger;
this.SERVICE_NAME = SERVICE_NAME;
this.buffers = {
checks: [],
pagespeedChecks: [],
hardwareChecks: [],
};
this.OPERATION_MAP = {
checks: this.db.checkModule.createChecks,
pagespeedChecks: this.db.pageSpeedCheckModule.createPageSpeedChecks,
hardwareChecks: this.db.hardwareCheckModule.createHardwareChecks,
};
this.buffer = [];
this.scheduleNextFlush();
this.logger.info({
message: `Buffer service initialized, flushing every ${this.BUFFER_TIMEOUT / 1000}s`,
@@ -39,9 +20,9 @@ class BufferService {
return BufferService.SERVICE_NAME;
}
addToBuffer({ check, type }) {
addToBuffer({ check }) {
try {
this.buffers[TYPE_MAP[type]].push(check);
this.buffer.push(check);
} catch (error) {
this.logger.error({
message: error.message,
@@ -55,7 +36,7 @@ class BufferService {
scheduleNextFlush() {
this.bufferTimer = setTimeout(async () => {
try {
await this.flushBuffers();
await this.flushBuffer();
} catch (error) {
this.logger.error({
message: `Error in flush cycle: ${error.message}`,
@@ -69,35 +50,24 @@ class BufferService {
}
}, this.BUFFER_TIMEOUT);
}
async flushBuffers() {
let items = 0;
for (const [bufferName, buffer] of Object.entries(this.buffers)) {
items += buffer.length;
const operation = this.OPERATION_MAP[bufferName];
if (!operation) {
this.logger.error({
message: `No operation found for ${bufferName}`,
service: this.SERVICE_NAME,
method: "flushBuffers",
});
continue;
}
try {
await operation(buffer);
} catch (error) {
this.logger.error({
message: error.message,
service: this.SERVICE_NAME,
method: "flushBuffers",
stack: error.stack,
});
}
this.buffers[bufferName] = [];
async flushBuffer() {
let items = this.buffer.length;
try {
await this.db.checkModule.createChecks(this.buffer);
} catch (error) {
this.logger.error({
message: error.message,
service: this.SERVICE_NAME,
method: "flushBuffer",
stack: error.stack,
});
}
this.buffer = [];
this.logger.debug({
message: `Flushed ${items} items`,
service: this.SERVICE_NAME,
method: "flushBuffers",
method: "flushBuffer",
});
items = 0;
}
@@ -1,17 +1,14 @@
import MonitorStats from "../../db/models/MonitorStats.js";
import { safelyParseFloat } from "../../utils/dataUtils.js";
const SERVICE_NAME = "StatusService";
class StatusService {
static SERVICE_NAME = SERVICE_NAME;
/**
* Creates an instance of StatusService.
*
* @param {Object} db - The database instance.
* @param {Object} logger - The logger instance.
*/
constructor({ db, logger, buffer }) {
* @param {{
* buffer: import("./bufferService.js").BufferService
* }}
*/ constructor({ db, logger, buffer }) {
this.db = db;
this.logger = logger;
this.buffer = buffer;
@@ -24,7 +21,7 @@ class StatusService {
async updateRunningStats({ monitor, networkResponse }) {
try {
const monitorId = monitor._id;
const { responseTime, status, upt_burnt } = networkResponse;
const { responseTime, status } = networkResponse;
// Get stats
let stats = await MonitorStats.findOne({ monitorId });
if (!stats) {
@@ -36,8 +33,6 @@ class StatusService {
totalDownChecks: 0,
uptimePercentage: 0,
lastCheck: null,
timeSInceLastCheck: 0,
uptBurnt: 0,
});
}
@@ -82,12 +77,6 @@ class StatusService {
// latest check
stats.lastCheckTimestamp = new Date().getTime();
// UPT burned
if (typeof upt_burnt !== "undefined" && upt_burnt !== null) {
const currentUptBurnt = safelyParseFloat(stats.uptBurnt);
const newUptBurnt = safelyParseFloat(upt_burnt);
stats.uptBurnt = currentUptBurnt + newUptBurnt;
}
await stats.save();
return true;
} catch (error) {
@@ -119,7 +108,8 @@ class StatusService {
* @returns {boolean} returnObject.prevStatus - The previous status of the monitor
*/
updateStatus = async (networkResponse) => {
this.insertCheck(networkResponse);
const check = this.buildCheck(networkResponse);
await this.insertCheck(check);
try {
const { monitorId, status, code } = networkResponse;
const monitor = await this.db.monitorModule.getMonitorById(monitorId);
@@ -198,6 +188,7 @@ class StatusService {
const check = {
monitorId,
teamId,
type,
status,
statusCode: code,
responseTime,
@@ -264,25 +255,24 @@ class StatusService {
* @param {Object} networkResponse.payload - The payload of the response.
* @returns {Promise<void>} A promise that resolves when the check is inserted.
*/
insertCheck = async (networkResponse) => {
insertCheck = async (check) => {
try {
const check = this.buildCheck(networkResponse);
if (typeof check === "undefined") {
this.logger.warn({
message: "Failed to build check",
service: this.SERVICE_NAME,
method: "insertCheck",
details: networkResponse,
});
return;
return false;
}
this.buffer.addToBuffer({ check, type: networkResponse.type });
this.buffer.addToBuffer({ check });
return true;
} catch (error) {
this.logger.error({
message: error.message,
service: error.service || this.SERVICE_NAME,
method: error.method || "insertCheck",
details: error.details || `Error inserting check for monitor: ${networkResponse?.monitorId}`,
details: error.details || `Error inserting check for monitor: ${check?.monitorId}`,
stack: error.stack,
});
}