mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-23 02:29:30 -05:00
refactoring
This commit is contained in:
Generated
+11
@@ -48,6 +48,7 @@
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@types/express": "5.0.3",
|
||||
"@types/jsonwebtoken": "9.0.10",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/nodemailer": "7.0.1",
|
||||
"@types/ping": "0.4.4",
|
||||
"c8": "10.1.3",
|
||||
@@ -3301,6 +3302,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/multer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz",
|
||||
"integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@types/express": "5.0.3",
|
||||
"@types/jsonwebtoken": "9.0.10",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/nodemailer": "7.0.1",
|
||||
"@types/ping": "0.4.4",
|
||||
"c8": "10.1.3",
|
||||
|
||||
@@ -47,7 +47,7 @@ import { GenerateAvatarImage } from "../utils/imageProcessing.js";
|
||||
import { ParseBoolean } from "../utils/utils.js";
|
||||
|
||||
// Models
|
||||
import Check from "../db/models/Check.js";
|
||||
import { CheckModel } from "@/db/models/index.js";
|
||||
import Monitor from "../db/models/Monitor.js";
|
||||
import User from "../db/models/User.js";
|
||||
import InviteToken from "../db/models/InviteToken.js";
|
||||
@@ -71,6 +71,9 @@ import RecoveryModule from "../db/modules/recoveryModule.js";
|
||||
import SettingsModule from "../db/modules/settingsModule.js";
|
||||
import IncidentModule from "../db/modules/incidentModule.js";
|
||||
|
||||
// repositories
|
||||
import { MongoMonitorsRepository, MongoChecksRepository } from "@/repositories/index.js";
|
||||
|
||||
export const initializeServices = async ({ logger, envSettings, settingsService }) => {
|
||||
const serviceRegistry = new ServiceRegistry({ logger });
|
||||
ServiceRegistry.instance = serviceRegistry;
|
||||
@@ -81,7 +84,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
const stringService = new StringService(translationService);
|
||||
|
||||
// Create DB
|
||||
const checkModule = new CheckModule({ logger, Check, Monitor, User });
|
||||
const checkModule = new CheckModule({ logger, CheckModel, 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 });
|
||||
@@ -89,7 +92,6 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
const monitorModule = new MonitorModule({
|
||||
Monitor,
|
||||
MonitorStats,
|
||||
Check,
|
||||
stringService,
|
||||
fs,
|
||||
path,
|
||||
@@ -120,6 +122,10 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
|
||||
await db.connect();
|
||||
|
||||
// Repositories
|
||||
const monitorsRepository = new MongoMonitorsRepository();
|
||||
const checksRepository = new MongoChecksRepository();
|
||||
|
||||
const networkService = new NetworkService({
|
||||
axios,
|
||||
got,
|
||||
@@ -220,6 +226,8 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
logger,
|
||||
errorService,
|
||||
games,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
});
|
||||
|
||||
const services = {
|
||||
|
||||
Executable → Regular
+108
-32
@@ -1,7 +1,78 @@
|
||||
import mongoose from "mongoose";
|
||||
import { MonitorTypes } from "@/types/monitor.js";
|
||||
import { Schema, model, Types } from "mongoose";
|
||||
import {
|
||||
MonitorTypes,
|
||||
type MonitorType,
|
||||
} from "@/types/monitor.js";
|
||||
import type {
|
||||
Check,
|
||||
CheckAudits,
|
||||
CheckCaptureInfo,
|
||||
CheckCpuInfo,
|
||||
CheckDiskInfo,
|
||||
CheckErrorInfo,
|
||||
CheckHostInfo,
|
||||
CheckMemoryInfo,
|
||||
CheckMetadata,
|
||||
CheckNetworkInterfaceInfo,
|
||||
CheckTimings,
|
||||
CheckTimingPhases,
|
||||
} from "@/types/check.js";
|
||||
|
||||
const cpuSchema = new mongoose.Schema(
|
||||
type CheckMetadataDocument = Omit<CheckMetadata, "monitorId" | "teamId"> & {
|
||||
monitorId: Types.ObjectId;
|
||||
teamId: Types.ObjectId;
|
||||
type: MonitorType;
|
||||
};
|
||||
|
||||
type CheckDocumentBase = Omit<
|
||||
Check,
|
||||
"id" | "metadata" | "expiry" | "ackAt" | "createdAt" | "updatedAt"
|
||||
> & {
|
||||
metadata: CheckMetadataDocument;
|
||||
expiry: Date;
|
||||
ackAt?: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
__v: number;
|
||||
};
|
||||
|
||||
interface CheckDocument extends CheckDocumentBase {
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
||||
const timingPhasesSchema = new Schema<CheckTimingPhases>(
|
||||
{
|
||||
wait: { type: Number, default: 0 },
|
||||
dns: { type: Number, default: 0 },
|
||||
tcp: { type: Number, default: 0 },
|
||||
tls: { type: Number, default: 0 },
|
||||
request: { type: Number, default: 0 },
|
||||
firstByte: { type: Number, default: 0 },
|
||||
download: { type: Number, default: 0 },
|
||||
total: { type: Number, default: 0 },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const timingsSchema = new Schema<CheckTimings>(
|
||||
{
|
||||
start: { type: Number, default: 0 },
|
||||
socket: { type: Number, default: 0 },
|
||||
lookup: { type: Number, default: 0 },
|
||||
connect: { type: Number, default: 0 },
|
||||
secureConnect: { type: Number, default: 0 },
|
||||
upload: { type: Number, default: 0 },
|
||||
response: { type: Number, default: 0 },
|
||||
end: { type: Number, default: 0 },
|
||||
phases: {
|
||||
type: timingPhasesSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const cpuSchema = new Schema<CheckCpuInfo>(
|
||||
{
|
||||
physical_core: { type: Number, default: 0 },
|
||||
logical_core: { type: Number, default: 0 },
|
||||
@@ -13,7 +84,7 @@ const cpuSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const memorySchema = new mongoose.Schema(
|
||||
const memorySchema = new Schema<CheckMemoryInfo>(
|
||||
{
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
available_bytes: { type: Number, default: 0 },
|
||||
@@ -23,7 +94,7 @@ const memorySchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const diskSchema = new mongoose.Schema(
|
||||
const diskSchema = new Schema<CheckDiskInfo>(
|
||||
{
|
||||
device: { type: String, default: "" },
|
||||
mountpoint: { type: String, default: "" },
|
||||
@@ -36,7 +107,7 @@ const diskSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const hostSchema = new mongoose.Schema(
|
||||
const hostSchema = new Schema<CheckHostInfo>(
|
||||
{
|
||||
os: { type: String, default: "" },
|
||||
platform: { type: String, default: "" },
|
||||
@@ -45,7 +116,7 @@ const hostSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const errorSchema = new mongoose.Schema(
|
||||
const errorSchema = new Schema<CheckErrorInfo>(
|
||||
{
|
||||
metric: { type: [String], default: [] },
|
||||
err: { type: String, default: "" },
|
||||
@@ -53,7 +124,7 @@ const errorSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const captureSchema = new mongoose.Schema(
|
||||
const captureSchema = new Schema<CheckCaptureInfo>(
|
||||
{
|
||||
version: { type: String, default: "" },
|
||||
mode: { type: String, default: "" },
|
||||
@@ -61,9 +132,9 @@ const captureSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const networkInterfaceSchema = new mongoose.Schema(
|
||||
const networkInterfaceSchema = new Schema<CheckNetworkInterfaceInfo>(
|
||||
{
|
||||
name: { type: String },
|
||||
name: { type: String, default: "" },
|
||||
bytes_sent: { type: Number, default: 0 },
|
||||
bytes_recv: { type: Number, default: 0 },
|
||||
packets_sent: { type: Number, default: 0 },
|
||||
@@ -78,17 +149,30 @@ const networkInterfaceSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const metadataSchema = new mongoose.Schema(
|
||||
const auditsSchema = new Schema<CheckAudits>(
|
||||
{
|
||||
cls: { type: Number, default: 0 },
|
||||
si: { type: Number, default: 0 },
|
||||
fcp: { type: Number, default: 0 },
|
||||
lcp: { type: Number, default: 0 },
|
||||
tbt: { type: Number, default: 0 },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const metadataSchema = new Schema<CheckMetadataDocument>(
|
||||
{
|
||||
monitorId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Monitor",
|
||||
required: true,
|
||||
immutable: true,
|
||||
index: true,
|
||||
},
|
||||
teamId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Team",
|
||||
required: true,
|
||||
immutable: true,
|
||||
index: true,
|
||||
},
|
||||
@@ -102,7 +186,7 @@ const metadataSchema = new mongoose.Schema(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const CheckSchema = new mongoose.Schema(
|
||||
const CheckSchema = new Schema<CheckDocument>(
|
||||
{
|
||||
metadata: {
|
||||
type: metadataSchema,
|
||||
@@ -112,40 +196,32 @@ const CheckSchema = new mongoose.Schema(
|
||||
type: Boolean,
|
||||
index: true,
|
||||
},
|
||||
|
||||
responseTime: {
|
||||
type: Number,
|
||||
},
|
||||
|
||||
timings: {
|
||||
type: Object,
|
||||
default: {},
|
||||
type: timingsSchema,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
statusCode: {
|
||||
type: Number,
|
||||
index: true,
|
||||
},
|
||||
|
||||
message: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
expiry: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
|
||||
ack: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
ackAt: {
|
||||
type: Date,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
// Hardware fields
|
||||
cpu: {
|
||||
type: cpuSchema,
|
||||
default: () => ({}),
|
||||
@@ -162,23 +238,18 @@ const CheckSchema = new mongoose.Schema(
|
||||
type: hostSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
errors: {
|
||||
type: [errorSchema],
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
capture: {
|
||||
type: captureSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
net: {
|
||||
type: [networkInterfaceSchema],
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
// PageSpeed fields
|
||||
accessibility: {
|
||||
type: Number,
|
||||
},
|
||||
@@ -192,7 +263,8 @@ const CheckSchema = new mongoose.Schema(
|
||||
type: Number,
|
||||
},
|
||||
audits: {
|
||||
type: Object,
|
||||
type: auditsSchema,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -211,4 +283,8 @@ CheckSchema.index({ "metadata.monitorId": 1, updatedAt: 1 });
|
||||
CheckSchema.index({ "metadata.monitorId": 1, updatedAt: -1 });
|
||||
CheckSchema.index({ "metadata.teamId": 1, updatedAt: -1 });
|
||||
|
||||
export default mongoose.model("Check", CheckSchema);
|
||||
const CheckModel = model<CheckDocument>("Check", CheckSchema);
|
||||
|
||||
export type { CheckDocument, CheckMetadataDocument };
|
||||
export { CheckModel };
|
||||
export default CheckModel;
|
||||
|
||||
Executable → Regular
+48
-24
@@ -1,18 +1,49 @@
|
||||
import mongoose from "mongoose";
|
||||
import { Schema, model, Types, type UpdateQuery } from "mongoose";
|
||||
import type { Monitor, MonitorMatchMethod, MonitorThresholds } from "@/types/monitor.js";
|
||||
import { MonitorTypes } from "@/types/monitor.js";
|
||||
import Check from "./Check.js";
|
||||
import MonitorStats from "./MonitorStats.js";
|
||||
import StatusPage from "./StatusPage.js";
|
||||
|
||||
const MonitorSchema = mongoose.Schema(
|
||||
type MonitorDocumentBase = Omit<
|
||||
Monitor,
|
||||
"id" | "userId" | "teamId" | "notifications" | "selectedDisks" | "statusWindow" | "createdAt" | "updatedAt"
|
||||
> & {
|
||||
statusWindow: boolean[];
|
||||
notifications: Types.ObjectId[];
|
||||
selectedDisks: string[];
|
||||
matchMethod?: MonitorMatchMethod;
|
||||
thresholds?: MonitorThresholds;
|
||||
};
|
||||
|
||||
interface MonitorDocument extends MonitorDocumentBase {
|
||||
_id: Types.ObjectId;
|
||||
userId: Types.ObjectId;
|
||||
teamId: Types.ObjectId;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
const thresholdsSchema = new Schema<MonitorThresholds>(
|
||||
{
|
||||
usage_cpu: { type: Number },
|
||||
usage_memory: { type: Number },
|
||||
usage_disk: { type: Number },
|
||||
usage_temperature: { type: Number },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const MonitorSchema = new Schema<MonitorDocument>(
|
||||
{
|
||||
userId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
immutable: true,
|
||||
required: true,
|
||||
},
|
||||
teamId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Team",
|
||||
immutable: true,
|
||||
required: true,
|
||||
@@ -43,7 +74,7 @@ const MonitorSchema = mongoose.Schema(
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: ["http", "ping", "pagespeed", "hardware", "docker", "port", "game"],
|
||||
enum: MonitorTypes,
|
||||
},
|
||||
ignoreTlsErrors: {
|
||||
type: Boolean,
|
||||
@@ -71,7 +102,6 @@ const MonitorSchema = mongoose.Schema(
|
||||
default: true,
|
||||
},
|
||||
interval: {
|
||||
// in milliseconds
|
||||
type: Number,
|
||||
default: 60000,
|
||||
},
|
||||
@@ -81,7 +111,7 @@ const MonitorSchema = mongoose.Schema(
|
||||
},
|
||||
notifications: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Notification",
|
||||
},
|
||||
],
|
||||
@@ -89,13 +119,7 @@ const MonitorSchema = mongoose.Schema(
|
||||
type: String,
|
||||
},
|
||||
thresholds: {
|
||||
type: {
|
||||
usage_cpu: { type: Number },
|
||||
usage_memory: { type: Number },
|
||||
usage_disk: { type: Number },
|
||||
usage_temperature: { type: Number },
|
||||
},
|
||||
_id: false,
|
||||
type: thresholdsSchema,
|
||||
},
|
||||
alertThreshold: {
|
||||
type: Number,
|
||||
@@ -137,7 +161,7 @@ const MonitorSchema = mongoose.Schema(
|
||||
trim: true,
|
||||
maxLength: 50,
|
||||
default: null,
|
||||
set: function (value) {
|
||||
set(value: string | null) {
|
||||
return value && value.trim() ? value.trim() : null;
|
||||
},
|
||||
},
|
||||
@@ -148,7 +172,6 @@ const MonitorSchema = mongoose.Schema(
|
||||
);
|
||||
|
||||
MonitorSchema.pre("findOneAndDelete", async function (next) {
|
||||
// Delete checks and stats
|
||||
try {
|
||||
const doc = await this.model.findOne(this.getFilter());
|
||||
|
||||
@@ -157,20 +180,17 @@ MonitorSchema.pre("findOneAndDelete", async function (next) {
|
||||
}
|
||||
|
||||
await Check.deleteMany({ monitorId: doc._id });
|
||||
|
||||
// Deal with status pages
|
||||
await StatusPage.updateMany({ monitors: doc?._id }, { $pull: { monitors: doc?._id } });
|
||||
|
||||
await MonitorStats.deleteMany({ monitorId: doc?._id.toString() });
|
||||
next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
next(error as Error);
|
||||
}
|
||||
});
|
||||
|
||||
MonitorSchema.pre("deleteMany", async function (next) {
|
||||
const filter = this.getFilter();
|
||||
const monitors = await this.model.find(filter).select(["_id", "type"]).lean();
|
||||
const monitors = (await this.model.find(filter).select(["_id", "type"]).lean()) as { _id: Types.ObjectId }[];
|
||||
|
||||
for (const monitor of monitors) {
|
||||
await Check.deleteMany({ monitorId: monitor._id });
|
||||
@@ -197,8 +217,8 @@ MonitorSchema.pre("save", function (next) {
|
||||
});
|
||||
|
||||
MonitorSchema.pre("findOneAndUpdate", function (next) {
|
||||
const update = this.getUpdate();
|
||||
if (update.alertThreshold) {
|
||||
const update = this.getUpdate() as UpdateQuery<MonitorDocument> | null;
|
||||
if (update && !Array.isArray(update) && update.alertThreshold !== undefined) {
|
||||
update.cpuAlertThreshold = update.alertThreshold;
|
||||
update.memoryAlertThreshold = update.alertThreshold;
|
||||
update.diskAlertThreshold = update.alertThreshold;
|
||||
@@ -209,4 +229,8 @@ MonitorSchema.pre("findOneAndUpdate", function (next) {
|
||||
|
||||
MonitorSchema.index({ teamId: 1, type: 1 });
|
||||
|
||||
export default mongoose.model("Monitor", MonitorSchema);
|
||||
const MonitorModel = model<MonitorDocument>("Monitor", MonitorSchema);
|
||||
|
||||
export type { MonitorDocument };
|
||||
export { MonitorModel };
|
||||
export default MonitorModel;
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "@/db/models/Monitor.js";
|
||||
export { default as MonitorModel } from "@/db/models/Monitor.js";
|
||||
|
||||
export * from "@/db/models/Check.js";
|
||||
export { default as CheckModel } from "@/db/models/Check.js";
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
import mongoose from "mongoose";
|
||||
import { CheckModel } from "@/db/models/index.js";
|
||||
import { buildChecksSummaryByTeamIdPipeline } from "./checkModuleQueries.js";
|
||||
|
||||
const SERVICE_NAME = "checkModule";
|
||||
@@ -12,18 +14,15 @@ const dateRangeLookup = {
|
||||
};
|
||||
|
||||
class CheckModule {
|
||||
constructor({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User }) {
|
||||
constructor({ logger, Monitor, User }) {
|
||||
this.logger = logger;
|
||||
this.Check = Check;
|
||||
this.HardwareCheck = HardwareCheck;
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
this.Monitor = Monitor;
|
||||
this.User = User;
|
||||
}
|
||||
|
||||
createChecks = async (checks) => {
|
||||
try {
|
||||
await this.Check.insertMany(checks, { ordered: false });
|
||||
await CheckModel.insertMany(checks, { ordered: false });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
@@ -41,7 +40,7 @@ class CheckModule {
|
||||
|
||||
// Match
|
||||
const matchStage = {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
"metadata.monitorId": new ObjectId(monitorId),
|
||||
...(typeof status !== "undefined" && { status }),
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
@@ -79,7 +78,7 @@ class CheckModule {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
const checks = await this.Check.aggregate([
|
||||
const checks = await CheckModel.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
@@ -115,7 +114,7 @@ class CheckModule {
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
"metadata.teamId": new ObjectId(teamId),
|
||||
status: false,
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
@@ -170,7 +169,7 @@ class CheckModule {
|
||||
},
|
||||
];
|
||||
|
||||
const checks = await this.Check.aggregate(aggregatePipeline);
|
||||
const checks = await CheckModel.aggregate(aggregatePipeline);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -181,7 +180,11 @@ class CheckModule {
|
||||
|
||||
ackCheck = async (checkId, teamId, ack) => {
|
||||
try {
|
||||
const updatedCheck = await this.Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true });
|
||||
const updatedCheck = await CheckModel.findOneAndUpdate(
|
||||
{ _id: checkId, "metadata.teamId": teamId },
|
||||
{ $set: { ack, ackAt: new Date() } },
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!updatedCheck) {
|
||||
throw new Error("Check not found");
|
||||
@@ -197,7 +200,11 @@ class CheckModule {
|
||||
|
||||
ackAllChecks = async (monitorId, teamId, ack, path) => {
|
||||
try {
|
||||
const updatedChecks = await this.Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $set: { ack, ackAt: new Date() } });
|
||||
const filter =
|
||||
path === "monitor"
|
||||
? { "metadata.monitorId": new mongoose.Types.ObjectId(monitorId) }
|
||||
: { "metadata.teamId": new mongoose.Types.ObjectId(teamId) };
|
||||
const updatedChecks = await CheckModel.updateMany(filter, { $set: { ack, ackAt: new Date() } });
|
||||
return updatedChecks.modifiedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -209,9 +216,9 @@ class CheckModule {
|
||||
getChecksSummaryByTeamId = async ({ teamId }) => {
|
||||
try {
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
"metadata.teamId": new ObjectId(teamId),
|
||||
};
|
||||
const checks = await this.Check.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage }));
|
||||
const checks = await CheckModel.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage }));
|
||||
return checks[0].summary;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -221,7 +228,7 @@ class CheckModule {
|
||||
};
|
||||
deleteChecks = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.Check.deleteMany({ monitorId });
|
||||
const result = await CheckModel.deleteMany({ "metadata.monitorId": monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -237,7 +244,7 @@ class CheckModule {
|
||||
const monitorIds = teamMonitors.map((monitor) => monitor._id);
|
||||
|
||||
// Delete all checks for these monitors in one operation
|
||||
const deleteResult = await this.Check.deleteMany({ monitorId: { $in: monitorIds } });
|
||||
const deleteResult = await CheckModel.deleteMany({ "metadata.monitorId": { $in: monitorIds } });
|
||||
|
||||
return deleteResult.deletedCount;
|
||||
} catch (error) {
|
||||
@@ -249,7 +256,7 @@ class CheckModule {
|
||||
|
||||
updateChecksTTL = async (teamId, ttl) => {
|
||||
try {
|
||||
await this.Check.collection.dropIndex("expiry_1");
|
||||
await CheckModel.collection.dropIndex("expiry_1");
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
@@ -260,9 +267,9 @@ class CheckModule {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.Check.collection.createIndex(
|
||||
await CheckModel.collection.createIndex(
|
||||
{ expiry: 1 },
|
||||
{ expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary
|
||||
{ expireAfterSeconds: ttl, partialFilterExpression: { "metadata.mode": { $exists: true } } }
|
||||
);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
buildFilteredMonitorsByTeamIdPipeline,
|
||||
} from "./monitorModuleQueries.js";
|
||||
|
||||
import { CheckModel } from "@/db/models/index.js";
|
||||
|
||||
const SERVICE_NAME = "monitorModule";
|
||||
|
||||
class MonitorModule {
|
||||
constructor({ Monitor, MonitorStats, Check, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) {
|
||||
constructor({ Monitor, MonitorStats, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) {
|
||||
this.Monitor = Monitor;
|
||||
this.MonitorStats = MonitorStats;
|
||||
this.Check = Check;
|
||||
this.stringService = stringService;
|
||||
this.fs = fs;
|
||||
this.path = path;
|
||||
@@ -123,15 +124,18 @@ class MonitorModule {
|
||||
|
||||
//Helper
|
||||
getMonitorChecks = async (monitorId, dateRange, sortOrder) => {
|
||||
const objectId = new this.ObjectId(monitorId);
|
||||
const indexSpec = {
|
||||
monitorId: 1,
|
||||
updatedAt: sortOrder, // This will be 1 or -1
|
||||
"metadata.monitorId": 1,
|
||||
updatedAt: sortOrder,
|
||||
};
|
||||
|
||||
const matchBase = { "metadata.monitorId": objectId };
|
||||
|
||||
const [checksAll, checksForDateRange] = await Promise.all([
|
||||
this.Check.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
|
||||
this.Check.find({
|
||||
monitorId,
|
||||
CheckModel.find(matchBase).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
|
||||
CheckModel.find({
|
||||
...matchBase,
|
||||
createdAt: { $gte: dateRange.start, $lte: dateRange.end },
|
||||
})
|
||||
.hint(indexSpec)
|
||||
@@ -238,7 +242,8 @@ class MonitorModule {
|
||||
|
||||
const dateString = formatLookup[dateRange];
|
||||
|
||||
const results = await this.Check.aggregate(buildUptimeDetailsPipeline(monitorId, dates, dateString));
|
||||
console.log("WTTTTFFF");
|
||||
const results = await CheckModel.aggregate(buildUptimeDetailsPipeline(monitorId, dates, dateString));
|
||||
|
||||
const monitorData = results[0];
|
||||
|
||||
@@ -313,7 +318,7 @@ class MonitorModule {
|
||||
};
|
||||
const dateString = formatLookup[dateRange];
|
||||
|
||||
const hardwareStats = await this.Check.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
|
||||
const hardwareStats = await CheckModel.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
|
||||
|
||||
const stats = hardwareStats[0];
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const buildUptimeDetailsPipeline = (monitorId, dates, dateString) => {
|
||||
return [
|
||||
{
|
||||
$match: {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
"metadata.monitorId": new ObjectId(monitorId),
|
||||
updatedAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
@@ -172,8 +172,8 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
return [
|
||||
{
|
||||
$match: {
|
||||
monitorId: monitor._id,
|
||||
type: "hardware",
|
||||
"metadata.monitorId": monitor._id,
|
||||
"metadata.type": "hardware",
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
@@ -233,11 +233,16 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [{ $eq: ["$monitorId", monitor._id] }, { $gte: ["$createdAt", dates.start] }, { $lte: ["$createdAt", dates.end] }],
|
||||
},
|
||||
},
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ["$metadata.monitorId", monitor._id] },
|
||||
{ $eq: ["$metadata.type", "hardware"] },
|
||||
{ $gte: ["$createdAt", dates.start] },
|
||||
{ $lte: ["$createdAt", dates.end] },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { LatestChecksMap } from "@/repositories/checks/MongoChecksRepistory.js";
|
||||
|
||||
export interface IChecksRepository {
|
||||
// create
|
||||
// single fetch
|
||||
// collection fetch
|
||||
findLatestChecksByMonitorIds(monitorIds: string[]): Promise<LatestChecksMap>;
|
||||
// update
|
||||
// delete
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import { IChecksRepository } from "@/repositories/index.js";
|
||||
import type {
|
||||
Check,
|
||||
CheckAudits,
|
||||
CheckCaptureInfo,
|
||||
CheckCpuInfo,
|
||||
CheckDiskInfo,
|
||||
CheckErrorInfo,
|
||||
CheckHostInfo,
|
||||
CheckMemoryInfo,
|
||||
CheckMetadata,
|
||||
CheckNetworkInterfaceInfo,
|
||||
CheckTimings,
|
||||
} from "@/types/index.js";
|
||||
import { CheckModel, type CheckDocument } from "@/db/models/index.js";
|
||||
import mongoose from "mongoose";
|
||||
|
||||
export type LatestChecksMap = Record<string, Check[]>;
|
||||
|
||||
class MongoChecksRepistory implements IChecksRepository {
|
||||
private toEntity = (doc: CheckDocument): Check => {
|
||||
const toStringId = (value: mongoose.Types.ObjectId | string | undefined | null): string => {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
return value instanceof mongoose.Types.ObjectId ? value.toString() : String(value);
|
||||
};
|
||||
|
||||
const toDateString = (value?: Date | string | null): string => {
|
||||
if (!value) {
|
||||
return new Date(0).toISOString();
|
||||
}
|
||||
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
||||
};
|
||||
|
||||
const toOptionalDateString = (value?: Date | string | null): string | undefined => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return toDateString(value);
|
||||
};
|
||||
|
||||
const mapTimings = (timings?: CheckTimings): CheckTimings => {
|
||||
const phases = timings?.phases ?? {
|
||||
wait: 0,
|
||||
dns: 0,
|
||||
tcp: 0,
|
||||
tls: 0,
|
||||
request: 0,
|
||||
firstByte: 0,
|
||||
download: 0,
|
||||
total: 0,
|
||||
};
|
||||
|
||||
return {
|
||||
start: timings?.start ?? 0,
|
||||
socket: timings?.socket ?? 0,
|
||||
lookup: timings?.lookup ?? 0,
|
||||
connect: timings?.connect ?? 0,
|
||||
secureConnect: timings?.secureConnect ?? 0,
|
||||
upload: timings?.upload ?? 0,
|
||||
response: timings?.response ?? 0,
|
||||
end: timings?.end ?? 0,
|
||||
phases,
|
||||
};
|
||||
};
|
||||
|
||||
const mapCpu = (cpu?: CheckCpuInfo): CheckCpuInfo => ({
|
||||
physical_core: cpu?.physical_core ?? 0,
|
||||
logical_core: cpu?.logical_core ?? 0,
|
||||
frequency: cpu?.frequency ?? 0,
|
||||
temperature: cpu?.temperature ?? [],
|
||||
free_percent: cpu?.free_percent ?? 0,
|
||||
usage_percent: cpu?.usage_percent ?? 0,
|
||||
});
|
||||
|
||||
const mapMemory = (memory?: CheckMemoryInfo): CheckMemoryInfo => ({
|
||||
total_bytes: memory?.total_bytes ?? 0,
|
||||
available_bytes: memory?.available_bytes ?? 0,
|
||||
used_bytes: memory?.used_bytes ?? 0,
|
||||
usage_percent: memory?.usage_percent ?? 0,
|
||||
});
|
||||
|
||||
const mapHost = (host?: CheckHostInfo): CheckHostInfo => ({
|
||||
os: host?.os ?? "",
|
||||
platform: host?.platform ?? "",
|
||||
kernel_version: host?.kernel_version ?? "",
|
||||
});
|
||||
|
||||
const mapCapture = (capture?: CheckCaptureInfo): CheckCaptureInfo => ({
|
||||
version: capture?.version ?? "",
|
||||
mode: capture?.mode ?? "",
|
||||
});
|
||||
|
||||
const mapDisks = (disks?: CheckDiskInfo[]): CheckDiskInfo[] =>
|
||||
(disks ?? []).map((disk) => ({
|
||||
device: disk?.device ?? "",
|
||||
mountpoint: disk?.mountpoint ?? "",
|
||||
read_speed_bytes: disk?.read_speed_bytes ?? 0,
|
||||
write_speed_bytes: disk?.write_speed_bytes ?? 0,
|
||||
total_bytes: disk?.total_bytes ?? 0,
|
||||
free_bytes: disk?.free_bytes ?? 0,
|
||||
usage_percent: disk?.usage_percent ?? 0,
|
||||
}));
|
||||
|
||||
const mapErrors = (errors?: CheckErrorInfo[]): CheckErrorInfo[] =>
|
||||
(errors ?? []).map((error) => ({
|
||||
metric: error?.metric ?? [],
|
||||
err: error?.err ?? "",
|
||||
}));
|
||||
|
||||
const mapNet = (net?: CheckNetworkInterfaceInfo[]): CheckNetworkInterfaceInfo[] =>
|
||||
(net ?? []).map((iface) => ({
|
||||
name: iface?.name ?? "",
|
||||
bytes_sent: iface?.bytes_sent ?? 0,
|
||||
bytes_recv: iface?.bytes_recv ?? 0,
|
||||
packets_sent: iface?.packets_sent ?? 0,
|
||||
packets_recv: iface?.packets_recv ?? 0,
|
||||
err_in: iface?.err_in ?? 0,
|
||||
err_out: iface?.err_out ?? 0,
|
||||
drop_in: iface?.drop_in ?? 0,
|
||||
drop_out: iface?.drop_out ?? 0,
|
||||
fifo_in: iface?.fifo_in ?? 0,
|
||||
fifo_out: iface?.fifo_out ?? 0,
|
||||
}));
|
||||
|
||||
const mapAudits = (audits?: CheckAudits): CheckAudits | undefined => {
|
||||
if (!audits) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
cls: audits.cls ?? 0,
|
||||
si: audits.si ?? 0,
|
||||
fcp: audits.fcp ?? 0,
|
||||
lcp: audits.lcp ?? 0,
|
||||
tbt: audits.tbt ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
const mapMetadata = (metadata: CheckDocument["metadata"]): CheckMetadata => ({
|
||||
monitorId: toStringId(metadata.monitorId),
|
||||
teamId: toStringId(metadata.teamId),
|
||||
type: metadata.type,
|
||||
});
|
||||
|
||||
return {
|
||||
id: toStringId(doc._id),
|
||||
metadata: mapMetadata(doc.metadata),
|
||||
status: doc.status ?? false,
|
||||
responseTime: doc.responseTime ?? 0,
|
||||
timings: mapTimings(doc.timings),
|
||||
statusCode: doc.statusCode ?? 0,
|
||||
message: doc.message ?? "",
|
||||
ack: doc.ack ?? false,
|
||||
ackAt: toOptionalDateString(doc.ackAt),
|
||||
expiry: toDateString(doc.expiry),
|
||||
cpu: mapCpu(doc.cpu),
|
||||
memory: mapMemory(doc.memory),
|
||||
disk: mapDisks(doc.disk),
|
||||
host: mapHost(doc.host),
|
||||
errors: mapErrors(doc.errors),
|
||||
capture: mapCapture(doc.capture),
|
||||
net: mapNet(doc.net),
|
||||
accessibility: doc.accessibility,
|
||||
bestPractices: doc.bestPractices,
|
||||
seo: doc.seo,
|
||||
performance: doc.performance,
|
||||
audits: mapAudits(doc.audits),
|
||||
__v: doc.__v ?? 0,
|
||||
createdAt: toDateString(doc.createdAt),
|
||||
updatedAt: toDateString(doc.updatedAt),
|
||||
};
|
||||
};
|
||||
|
||||
findLatestChecksByMonitorIds = async (monitorIds: string[]): Promise<LatestChecksMap> => {
|
||||
const mongoIds = monitorIds.map((id) => new mongoose.Types.ObjectId(id));
|
||||
const checkMap = await CheckModel.aggregate([
|
||||
{
|
||||
$match: {
|
||||
"metadata.monitorId": { $in: mongoIds },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
{
|
||||
$group: {
|
||||
_id: "$metadata.monitorId",
|
||||
latestChecks: { $push: "$$ROOT" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
latestChecks: { $slice: [{ $ifNull: ["$latestChecks", []] }, 25] },
|
||||
},
|
||||
},
|
||||
]);
|
||||
return checkMap.reduce<LatestChecksMap>((acc, cm) => {
|
||||
acc[cm._id.toString()] = cm.latestChecks.map((c: CheckDocument) => this.toEntity(c));
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoChecksRepistory;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "@/repositories/monitors/IMonitorsRepository.js";
|
||||
export { default as MongoMonitorsRepository } from "@/repositories/monitors/MongoMonitorsRepository.js";
|
||||
|
||||
export * from "@/repositories/checks/IChecksRepository.js";
|
||||
export { default as MongoChecksRepository } from "@/repositories/checks/MongoChecksRepistory.js";
|
||||
@@ -1,9 +1,24 @@
|
||||
import type { Monitor } from "@/types/index.js";
|
||||
import { type MonitorType, type Monitor } from "@/types/index.js";
|
||||
|
||||
export interface TeamQueryConfig {
|
||||
limit?: number;
|
||||
type?: MonitorType | MonitorType[];
|
||||
page?: number;
|
||||
rowsPerPage?: number;
|
||||
filter?: string;
|
||||
field?: string;
|
||||
order?: "asc" | "desc";
|
||||
}
|
||||
|
||||
export interface IMonitorsRepository {
|
||||
// create
|
||||
// single fetch
|
||||
// collection fetch
|
||||
findAll(): Promise<Monitor[] | null>;
|
||||
findByTeamId(teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null>;
|
||||
// update
|
||||
// delete
|
||||
|
||||
// counts
|
||||
findMonitorCountByTeamIdAndType(teamId: string, config: TeamQueryConfig): Promise<number>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,125 @@
|
||||
import { MonitorModel } from "@/db/models/index.js";
|
||||
import type { MonitorDocument } from "@/db/models/Monitor.js";
|
||||
import type { Monitor, MonitorType } from "@/types/monitor.js";
|
||||
import mongoose, { type FilterQuery } from "mongoose";
|
||||
import type { IMonitorsRepository, TeamQueryConfig } from "./IMonitorsRepository.js";
|
||||
|
||||
class MongoMonitorsRepository implements IMonitorsRepository {
|
||||
findAll = async () => {
|
||||
return null;
|
||||
findAll = async (): Promise<Monitor[] | null> => {
|
||||
const documents = await MonitorModel.find().exec();
|
||||
return this.mapDocuments(documents);
|
||||
};
|
||||
|
||||
findByTeamId = async (teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null> => {
|
||||
const { page = 0, rowsPerPage = 25, filter, field = "createdAt", order = "desc", type, limit } = config ?? {};
|
||||
|
||||
const query: Record<string, unknown> = {
|
||||
teamId: new mongoose.Types.ObjectId(teamId),
|
||||
};
|
||||
|
||||
if (type !== undefined) {
|
||||
query.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
|
||||
if (filter !== undefined) {
|
||||
switch (field) {
|
||||
case "name":
|
||||
query.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }];
|
||||
break;
|
||||
case "isActive":
|
||||
query.isActive = filter === "true";
|
||||
break;
|
||||
case "status":
|
||||
query.status = filter === "true";
|
||||
break;
|
||||
case "type":
|
||||
query.type = filter;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const sort = { [field]: order === "asc" ? 1 : -1 } as const;
|
||||
const skip = Math.max(page, 0) * rowsPerPage;
|
||||
const limitValue = limit ?? rowsPerPage;
|
||||
|
||||
const documents = await MonitorModel.find(query).sort(sort).skip(skip).limit(limitValue).exec();
|
||||
|
||||
return this.mapDocuments(documents);
|
||||
};
|
||||
|
||||
findMonitorCountByTeamIdAndType = async (teamId: string, config?: TeamQueryConfig): Promise<number> => {
|
||||
const { type } = config ?? {};
|
||||
|
||||
const query: FilterQuery<MonitorDocument> = {
|
||||
teamId: new mongoose.Types.ObjectId(teamId),
|
||||
};
|
||||
|
||||
if (type !== undefined) {
|
||||
query.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
|
||||
const count = await MonitorModel.countDocuments(query);
|
||||
return count;
|
||||
};
|
||||
|
||||
private mapDocuments = (documents: MonitorDocument[]): Monitor[] | null => {
|
||||
if (!documents?.length) {
|
||||
return null;
|
||||
}
|
||||
return documents.map((doc) => this.toEntity(doc));
|
||||
};
|
||||
|
||||
private toEntity = (doc: MonitorDocument): Monitor => {
|
||||
const toStringId = (value: unknown): string => {
|
||||
if (value instanceof mongoose.Types.ObjectId) {
|
||||
return value.toString();
|
||||
}
|
||||
return value?.toString() ?? "";
|
||||
};
|
||||
|
||||
const toDateString = (value: Date | string): string => {
|
||||
return value instanceof Date ? value.toISOString() : value;
|
||||
};
|
||||
|
||||
const notificationIds = (doc.notifications ?? []).map((notification) => toStringId(notification));
|
||||
|
||||
return {
|
||||
id: toStringId(doc._id),
|
||||
userId: toStringId(doc.userId),
|
||||
teamId: toStringId(doc.teamId),
|
||||
name: doc.name,
|
||||
description: doc.description ?? undefined,
|
||||
status: doc.status ?? undefined,
|
||||
statusWindow: doc.statusWindow ?? [],
|
||||
statusWindowSize: doc.statusWindowSize,
|
||||
statusWindowThreshold: doc.statusWindowThreshold,
|
||||
type: doc.type,
|
||||
ignoreTlsErrors: doc.ignoreTlsErrors,
|
||||
jsonPath: doc.jsonPath ?? undefined,
|
||||
expectedValue: doc.expectedValue ?? undefined,
|
||||
matchMethod: doc.matchMethod ?? undefined,
|
||||
url: doc.url,
|
||||
port: doc.port ?? undefined,
|
||||
isActive: doc.isActive,
|
||||
interval: doc.interval,
|
||||
uptimePercentage: doc.uptimePercentage ?? undefined,
|
||||
notifications: notificationIds,
|
||||
secret: doc.secret ?? undefined,
|
||||
thresholds: doc.thresholds ?? undefined,
|
||||
alertThreshold: doc.alertThreshold,
|
||||
cpuAlertThreshold: doc.cpuAlertThreshold,
|
||||
memoryAlertThreshold: doc.memoryAlertThreshold,
|
||||
diskAlertThreshold: doc.diskAlertThreshold,
|
||||
tempAlertThreshold: doc.tempAlertThreshold,
|
||||
selectedDisks: doc.selectedDisks ?? [],
|
||||
gameId: doc.gameId ?? undefined,
|
||||
group: doc.group ?? null,
|
||||
createdAt: toDateString(doc.createdAt),
|
||||
updatedAt: toDateString(doc.updatedAt),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoMonitorsRepository;
|
||||
|
||||
@@ -7,7 +7,9 @@ const upload = multer({
|
||||
});
|
||||
|
||||
class MonitorRoutes {
|
||||
constructor(monitorController) {
|
||||
private router: Router;
|
||||
private monitorController: any;
|
||||
constructor(monitorController: any) {
|
||||
this.router = Router();
|
||||
this.monitorController = monitorController;
|
||||
this.initRoutes();
|
||||
@@ -1,10 +1,23 @@
|
||||
import { createMonitorsBodyValidation } from "@/validation/joi.js";
|
||||
import { NormalizeData } from "@/utils/dataUtils.js";
|
||||
|
||||
const SERVICE_NAME = "MonitorService";
|
||||
class MonitorService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService, games }) {
|
||||
constructor({
|
||||
db,
|
||||
settingsService,
|
||||
jobQueue,
|
||||
stringService,
|
||||
emailService,
|
||||
papaparse,
|
||||
logger,
|
||||
errorService,
|
||||
games,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
}) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.jobQueue = jobQueue;
|
||||
@@ -14,6 +27,8 @@ class MonitorService {
|
||||
this.logger = logger;
|
||||
this.errorService = errorService;
|
||||
this.games = games;
|
||||
this.monitorsRepository = monitorsRepository;
|
||||
this.checksRepository = checksRepository;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
@@ -205,6 +220,7 @@ class MonitorService {
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async ({ teamId, limit, type, page, rowsPerPage, filter, field, order }) => {
|
||||
console.log("que");
|
||||
const monitors = await this.db.monitorModule.getMonitorsByTeamId({
|
||||
limit,
|
||||
type,
|
||||
@@ -228,7 +244,8 @@ class MonitorService {
|
||||
};
|
||||
|
||||
getMonitorsWithChecksByTeamId = async ({ teamId, limit, type, page, rowsPerPage, filter, field, order, explain }) => {
|
||||
const result = await this.db.monitorModule.getMonitorsWithChecksByTeamId({
|
||||
const count = await this.monitorsRepository.findMonitorCountByTeamIdAndType(teamId, { type, filter });
|
||||
const monitors = await this.monitorsRepository.findByTeamId(teamId, {
|
||||
limit,
|
||||
type,
|
||||
page,
|
||||
@@ -236,10 +253,19 @@ class MonitorService {
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
teamId,
|
||||
explain,
|
||||
});
|
||||
return result;
|
||||
|
||||
const monitorIds = monitors?.map((m) => m.id) ?? [];
|
||||
const checksMap = await this.checksRepository.findLatestChecksByMonitorIds(monitorIds);
|
||||
const monitorsWithChecks = (monitors ?? []).map((monitor) => {
|
||||
const checks = NormalizeData(checksMap[monitor.id] ?? [], 10, 100);
|
||||
return {
|
||||
...monitor,
|
||||
checks,
|
||||
};
|
||||
});
|
||||
|
||||
return { count, monitors: monitorsWithChecks };
|
||||
};
|
||||
|
||||
exportMonitorsToCSV = async ({ teamId }) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import MonitorStats from "../../db/models/MonitorStats.js";
|
||||
import Check from "../../db/models/Check.js";
|
||||
import { CheckModel } from "@/db/models/index.js";
|
||||
const SERVICE_NAME = "StatusService";
|
||||
|
||||
class StatusService {
|
||||
@@ -115,7 +115,7 @@ class StatusService {
|
||||
|
||||
if (!check._id) {
|
||||
try {
|
||||
const checkModel = new Check(check);
|
||||
const checkModel = new CheckModel(check);
|
||||
savedCheck = await checkModel.save();
|
||||
|
||||
this.buffer.removeCheckFromBuffer(check);
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { MonitorType } from "@/types/index.js";
|
||||
|
||||
export interface CheckMetadata {
|
||||
monitorId: string;
|
||||
teamId: string;
|
||||
type: MonitorType;
|
||||
}
|
||||
|
||||
export interface CheckTimingPhases {
|
||||
wait: number;
|
||||
dns: number;
|
||||
@@ -50,29 +56,66 @@ export interface CheckCaptureInfo {
|
||||
mode: string;
|
||||
}
|
||||
|
||||
export interface CheckDiskInfo {}
|
||||
export interface CheckDiskInfo {
|
||||
device: string;
|
||||
mountpoint: string;
|
||||
read_speed_bytes: number;
|
||||
write_speed_bytes: number;
|
||||
total_bytes: number;
|
||||
free_bytes: number;
|
||||
usage_percent: number;
|
||||
}
|
||||
|
||||
export interface CheckNetInfo {}
|
||||
export interface CheckErrorInfo {
|
||||
metric: string[];
|
||||
err: string;
|
||||
}
|
||||
|
||||
export interface CheckNetworkInterfaceInfo {
|
||||
name: string;
|
||||
bytes_sent: number;
|
||||
bytes_recv: number;
|
||||
packets_sent: number;
|
||||
packets_recv: number;
|
||||
err_in: number;
|
||||
err_out: number;
|
||||
drop_in: number;
|
||||
drop_out: number;
|
||||
fifo_in: number;
|
||||
fifo_out: number;
|
||||
}
|
||||
|
||||
export interface CheckAudits {
|
||||
cls: number;
|
||||
si: number;
|
||||
fcp: number;
|
||||
lcp: number;
|
||||
tbt: number;
|
||||
}
|
||||
|
||||
export interface Check {
|
||||
id: string;
|
||||
monitorId: string;
|
||||
teamId: string;
|
||||
type: MonitorType;
|
||||
metadata: CheckMetadata;
|
||||
status: boolean;
|
||||
responseTime: number;
|
||||
timings: CheckTimings;
|
||||
statusCode: number;
|
||||
message: string;
|
||||
ack: boolean;
|
||||
ackAt?: string | null;
|
||||
expiry: string;
|
||||
cpu: CheckCpuInfo;
|
||||
memory: CheckMemoryInfo;
|
||||
disk: CheckDiskInfo[];
|
||||
host: CheckHostInfo;
|
||||
errors: string[];
|
||||
errors: CheckErrorInfo[];
|
||||
capture: CheckCaptureInfo;
|
||||
net: CheckNetInfo[];
|
||||
net: CheckNetworkInterfaceInfo[];
|
||||
accessibility?: number;
|
||||
bestPractices?: number;
|
||||
seo?: number;
|
||||
performance?: number;
|
||||
audits?: CheckAudits;
|
||||
__v: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
|
||||
@@ -1,29 +1,46 @@
|
||||
export const MonitorTypes = ["http", "ping", "pagespeed", "hardware", "docker", "port", "game"] as const;
|
||||
export type MonitorType = (typeof MonitorTypes)[number];
|
||||
|
||||
export interface MonitorThresholds {
|
||||
usage_cpu?: number;
|
||||
usage_memory?: number;
|
||||
usage_disk?: number;
|
||||
usage_temperature?: number;
|
||||
}
|
||||
|
||||
export type MonitorMatchMethod = "equal" | "include" | "regex" | "";
|
||||
|
||||
export interface Monitor {
|
||||
id: string;
|
||||
userId: string;
|
||||
teamId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
status?: boolean;
|
||||
statusWindow: boolean[];
|
||||
statusWindowSize: number;
|
||||
statusWindowThreshold: number;
|
||||
type: MonitorType;
|
||||
ignoreTlsErrors: boolean;
|
||||
jsonPath?: string;
|
||||
expectedValue?: string;
|
||||
matchMethod?: MonitorMatchMethod;
|
||||
url: string;
|
||||
port?: number;
|
||||
isActive: boolean;
|
||||
interval: number;
|
||||
uptimePercentage?: number;
|
||||
notifications: string[];
|
||||
secret?: string;
|
||||
thresholds?: MonitorThresholds;
|
||||
alertThreshold: number;
|
||||
selectedDisks: string[];
|
||||
group: string | null;
|
||||
cpuAlertThreshold: number;
|
||||
memoryAlertThreshold: number;
|
||||
diskAlertThreshold: number;
|
||||
tempAlertThreshold: number;
|
||||
selectedDisks: string[];
|
||||
gameId?: string;
|
||||
group: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
status: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user