Merge upstream/develop into fix/3120-data-leak

This commit is contained in:
Craig Lawson committed 2026-01-15 11:06:27 +00:00
commit f0a6fc4fdf
309 files changed
+8413 -4468

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
import mongoose from "mongoose";
const AppSettingsSchema = mongoose.Schema(
{
checkTTL: {
type: Number,
default: 30,
},
language: {
type: String,
default: "gb",
},
pagespeedApiKey: {
type: String,
},
systemEmailHost: {
type: String,
},
systemEmailPort: {
type: Number,
},
systemEmailAddress: {
type: String,
},
systemEmailPassword: {
type: String,
},
systemEmailUser: {
type: String,
},
systemEmailConnectionHost: {
type: String,
default: "localhost",
},
systemEmailTLSServername: {
type: String,
},
systemEmailSecure: {
type: Boolean,
default: false,
},
systemEmailPool: {
type: Boolean,
default: false,
},
systemEmailIgnoreTLS: {
type: Boolean,
default: false,
},
systemEmailRequireTLS: {
type: Boolean,
default: false,
},
systemEmailRejectUnauthorized: {
type: Boolean,
default: true,
},
showURL: {
type: Boolean,
default: false,
},
singleton: {
type: Boolean,
required: true,
unique: true,
default: true,
},
version: {
type: Number,
default: 1,
},
globalThresholds: {
cpu: { type: Number },
memory: { type: Number },
disk: { type: Number },
temperature: { type: Number },
},
},
{
timestamps: true,
}
);
export default mongoose.model("AppSettings", AppSettingsSchema);
+299
View File
@@ -0,0 +1,299 @@
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,
ILighthouseAudit,
} from "@/types/check.js";
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 },
frequency: { type: Number, default: 0 },
temperature: { type: [Number], default: [] },
free_percent: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
},
{ _id: false }
);
const memorySchema = new Schema<CheckMemoryInfo>(
{
total_bytes: { type: Number, default: 0 },
available_bytes: { type: Number, default: 0 },
used_bytes: { type: Number, default: 0 },
usage_percent: { type: Number, default: 0 },
},
{ _id: false }
);
const diskSchema = new Schema<CheckDiskInfo>(
{
device: { type: String, default: "" },
mountpoint: { type: String, default: "" },
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 },
},
{ _id: false }
);
const hostSchema = new Schema<CheckHostInfo>(
{
os: { type: String, default: "" },
platform: { type: String, default: "" },
kernel_version: { type: String, default: "" },
},
{ _id: false }
);
const errorSchema = new Schema<CheckErrorInfo>(
{
metric: { type: [String], default: [] },
err: { type: String, default: "" },
},
{ _id: false }
);
const captureSchema = new Schema<CheckCaptureInfo>(
{
version: { type: String, default: "" },
mode: { type: String, default: "" },
},
{ _id: false }
);
const networkInterfaceSchema = new Schema<CheckNetworkInterfaceInfo>(
{
name: { type: String, default: "" },
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 },
},
{ _id: false }
);
const lighthouseAuditSchema = new Schema<ILighthouseAudit>(
{
id: { type: String },
title: { type: String },
score: { type: Number },
displayValue: { type: String },
numericValue: { type: Number },
numericUnit: { type: String },
},
{ _id: false }
);
const auditsSchema = new Schema<CheckAudits>(
{
cls: { type: lighthouseAuditSchema, default: undefined },
si: { type: lighthouseAuditSchema, default: undefined },
fcp: { type: lighthouseAuditSchema, default: undefined },
lcp: { type: lighthouseAuditSchema, default: undefined },
tbt: { type: lighthouseAuditSchema, default: undefined },
},
{ _id: false }
);
const metadataSchema = new Schema<CheckMetadataDocument>(
{
monitorId: {
type: Schema.Types.ObjectId,
ref: "Monitor",
required: true,
immutable: true,
index: true,
},
teamId: {
type: Schema.Types.ObjectId,
ref: "Team",
required: true,
immutable: true,
index: true,
},
type: {
type: String,
enum: MonitorTypes,
required: true,
index: true,
},
},
{ _id: false }
);
const CheckSchema = new Schema<CheckDocument>(
{
metadata: {
type: metadataSchema,
required: true,
},
status: {
type: Boolean,
index: true,
},
responseTime: {
type: Number,
},
timings: {
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,
},
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: () => [],
},
accessibility: {
type: Number,
},
bestPractices: {
type: Number,
},
seo: {
type: Number,
},
performance: {
type: Number,
},
audits: {
type: auditsSchema,
default: undefined,
},
},
{
timestamps: true,
strict: false,
timeseries: {
timeField: "createdAt",
metaField: "metadata",
granularity: "seconds",
},
}
);
CheckSchema.index({ updatedAt: 1 });
CheckSchema.index({ "metadata.monitorId": 1, updatedAt: 1 });
CheckSchema.index({ "metadata.monitorId": 1, updatedAt: -1 });
CheckSchema.index({ "metadata.monitorId": 1, createdAt: -1 });
CheckSchema.index({ "metadata.teamId": 1, updatedAt: -1 });
CheckSchema.index({ "metadata.teamId": 1, createdAt: -1 });
const CheckModel = model<CheckDocument>("Check", CheckSchema);
export type { CheckDocument, CheckMetadataDocument };
export { CheckModel };
export default CheckModel;
+76
View File
@@ -0,0 +1,76 @@
import mongoose from "mongoose";
const IncidentSchema = mongoose.Schema(
{
monitorId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
required: true,
immutable: true,
index: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
required: true,
immutable: true,
index: true,
},
startTime: {
type: Date,
required: true,
immutable: true,
},
endTime: {
type: Date,
default: null,
},
status: {
type: Boolean,
default: true,
index: true,
},
message: {
type: String,
default: null,
},
statusCode: {
type: Number,
index: true,
default: null,
},
resolutionType: {
type: String,
enum: ["automatic", "manual"],
default: null,
},
resolvedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
default: null,
},
comment: {
type: String,
default: null,
},
checks: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Check",
},
],
},
{ timestamps: true }
);
IncidentSchema.index({ monitorId: 1, status: 1 });
IncidentSchema.index({ teamId: 1, status: 1 });
IncidentSchema.index({ teamId: 1, startTime: -1 });
IncidentSchema.index({ status: 1, startTime: -1 });
IncidentSchema.index({ resolutionType: 1, status: 1 });
IncidentSchema.index({ resolvedBy: 1, status: 1 });
IncidentSchema.index({ createdAt: -1 });
const Incident = mongoose.model("Incident", IncidentSchema);
export default Incident;
+35
View File
@@ -0,0 +1,35 @@
import mongoose from "mongoose";
const InviteTokenSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
required: true,
},
role: {
type: Array,
required: true,
default: ["user"],
},
token: {
type: String,
required: true,
},
expiry: {
type: Date,
default: Date.now,
expires: 3600,
},
},
{
timestamps: true,
}
);
export default mongoose.model("InviteToken", InviteTokenSchema);
+68
View File
@@ -0,0 +1,68 @@
import mongoose from "mongoose";
/**
* MaintenanceWindow Schema
* @module MaintenanceWindow
* @typedef {Object} MaintenanceWindow
* @property {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor. This is a reference to the Monitor model and is immutable.
* @property {Boolean} active - Indicates whether the maintenance window is active.
* @property {Number} repeat - Indicates how often this maintenance window should repeat.
* @property {Date} start - The start date and time of the maintenance window.
* @property {Date} end - The end date and time of the maintenance window.
* @property {Date} expiry - The expiry date and time of the maintenance window. This is used for MongoDB's TTL index to automatically delete the document at this time. This field is set to the same value as `end` when `oneTime` is `true`.
*
* @example
*
* let maintenanceWindow = new MaintenanceWindow({
* monitorId: monitorId,
* active: active,
* repeat: repeat,
* start: start,
* end: end,
* });
*
* if (repeat === 0) {
* maintenanceWindow.expiry = end;
* }
*
*/
const MaintenanceWindow = mongoose.Schema(
{
monitorId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
immutable: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
},
active: {
type: Boolean,
default: true,
},
name: {
type: String,
},
repeat: {
type: Number,
},
start: {
type: Date,
},
end: {
type: Date,
},
expiry: {
type: Date,
index: { expires: "0s" },
},
},
{
timestamps: true,
}
);
export default mongoose.model("MaintenanceWindow", MaintenanceWindow);
+13
View File
@@ -0,0 +1,13 @@
import mongoose from "mongoose";
const MigrationSchema = new mongoose.Schema(
{
name: { type: String, required: true, unique: true },
status: { type: String, enum: ["completed", "failed"], default: "completed" },
completedAt: { type: Date },
error: { type: String },
},
{ timestamps: true }
);
export default mongoose.model("Migration", MigrationSchema);
+236
View File
@@ -0,0 +1,236 @@
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";
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: Schema.Types.ObjectId,
ref: "User",
immutable: true,
required: true,
},
teamId: {
type: Schema.Types.ObjectId,
ref: "Team",
immutable: true,
required: true,
},
name: {
type: String,
required: true,
},
description: {
type: String,
},
status: {
type: Boolean,
default: undefined,
},
statusWindow: {
type: [Boolean],
default: [],
},
statusWindowSize: {
type: Number,
default: 5,
},
statusWindowThreshold: {
type: Number,
default: 60,
},
type: {
type: String,
required: true,
enum: MonitorTypes,
},
ignoreTlsErrors: {
type: Boolean,
default: false,
},
jsonPath: {
type: String,
},
expectedValue: {
type: String,
},
matchMethod: {
type: String,
enum: ["equal", "include", "regex", ""],
},
url: {
type: String,
required: true,
},
port: {
type: Number,
},
isActive: {
type: Boolean,
default: true,
},
interval: {
type: Number,
default: 60000,
},
uptimePercentage: {
type: Number,
default: undefined,
},
notifications: [
{
type: Schema.Types.ObjectId,
ref: "Notification",
},
],
secret: {
type: String,
},
thresholds: {
type: thresholdsSchema,
},
alertThreshold: {
type: Number,
default: 5,
},
cpuAlertThreshold: {
type: Number,
default: function () {
return this.alertThreshold;
},
},
memoryAlertThreshold: {
type: Number,
default: function () {
return this.alertThreshold;
},
},
diskAlertThreshold: {
type: Number,
default: function () {
return this.alertThreshold;
},
},
tempAlertThreshold: {
type: Number,
default: function () {
return this.alertThreshold;
},
},
selectedDisks: {
type: [String],
default: [],
},
gameId: {
type: String,
},
group: {
type: String,
trim: true,
maxLength: 50,
default: null,
set(value: string | null) {
return value && value.trim() ? value.trim() : null;
},
},
},
{
timestamps: true,
}
);
MonitorSchema.pre("findOneAndDelete", async function (next) {
try {
const doc = await this.model.findOne(this.getFilter());
if (!doc) {
throw new Error("Monitor not found");
}
await Check.deleteMany({ monitorId: doc._id });
await StatusPage.updateMany({ monitors: doc?._id }, { $pull: { monitors: doc?._id } });
await MonitorStats.deleteMany({ monitorId: doc?._id.toString() });
next();
} catch (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()) as { _id: Types.ObjectId }[];
for (const monitor of monitors) {
await Check.deleteMany({ monitorId: monitor._id });
await StatusPage.updateMany({ monitors: monitor._id }, { $pull: { monitors: monitor._id } });
await MonitorStats.deleteMany({ monitorId: monitor._id.toString() });
}
next();
});
MonitorSchema.pre("save", function (next) {
if (!this.cpuAlertThreshold || this.isModified("alertThreshold")) {
this.cpuAlertThreshold = this.alertThreshold;
}
if (!this.memoryAlertThreshold || this.isModified("alertThreshold")) {
this.memoryAlertThreshold = this.alertThreshold;
}
if (!this.diskAlertThreshold || this.isModified("alertThreshold")) {
this.diskAlertThreshold = this.alertThreshold;
}
if (!this.tempAlertThreshold || this.isModified("alertThreshold")) {
this.tempAlertThreshold = this.alertThreshold;
}
next();
});
MonitorSchema.pre("findOneAndUpdate", function (next) {
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;
update.tempAlertThreshold = update.alertThreshold;
}
next();
});
MonitorSchema.index({ teamId: 1, type: 1 });
const MonitorModel = model<MonitorDocument>("Monitor", MonitorSchema);
export type { MonitorDocument };
export { MonitorModel };
export default MonitorModel;
+63
View File
@@ -0,0 +1,63 @@
import { Schema, model, type Types } from "mongoose";
import type { MonitorStats as MonitorStatsEntity } from "@/types/monitorStats.js";
type MonitorStatsDocumentBase = Omit<MonitorStatsEntity, "id" | "monitorId" | "createdAt" | "updatedAt"> & {
monitorId: Types.ObjectId;
};
interface MonitorStatsDocument extends MonitorStatsDocumentBase {
_id: Types.ObjectId;
createdAt: Date;
updatedAt: Date;
}
const MonitorStatsSchema = new Schema<MonitorStatsDocument>(
{
monitorId: {
type: Schema.Types.ObjectId,
ref: "Monitor",
immutable: true,
index: true,
required: true,
},
avgResponseTime: {
type: Number,
default: 0,
},
totalChecks: {
type: Number,
default: 0,
},
totalUpChecks: {
type: Number,
default: 0,
},
totalDownChecks: {
type: Number,
default: 0,
},
uptimePercentage: {
type: Number,
default: 0,
},
lastCheckTimestamp: {
type: Number,
default: 0,
},
lastResponseTime: {
type: Number,
default: 0,
},
timeOfLastFailure: {
type: Number,
default: undefined,
},
},
{ timestamps: true }
);
const MonitorStatsModel = model<MonitorStatsDocument>("MonitorStats", MonitorStatsSchema);
export type { MonitorStatsDocument };
export { MonitorStatsModel };
export default MonitorStatsModel;
+74
View File
@@ -0,0 +1,74 @@
import mongoose from "mongoose";
const NotificationSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
immutable: true,
required: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
required: true,
},
type: {
type: String,
enum: ["email", "slack", "discord", "webhook", "pager_duty", "matrix"],
},
notificationName: {
type: String,
required: true,
},
address: {
type: String,
},
phone: {
type: String,
},
// Matrix-specific fields
homeserverUrl: {
type: String,
},
roomId: {
type: String,
},
accessToken: {
type: String,
},
},
{
timestamps: true,
}
);
NotificationSchema.pre("save", function (next) {
if (!this.cpuAlertThreshold || this.isModified("alertThreshold")) {
this.cpuAlertThreshold = this.alertThreshold;
}
if (!this.memoryAlertThreshold || this.isModified("alertThreshold")) {
this.memoryAlertThreshold = this.alertThreshold;
}
if (!this.diskAlertThreshold || this.isModified("alertThreshold")) {
this.diskAlertThreshold = this.alertThreshold;
}
if (!this.tempAlertThreshold || this.isModified("alertThreshold")) {
this.tempAlertThreshold = this.alertThreshold;
}
next();
});
NotificationSchema.pre("findOneAndUpdate", function (next) {
const update = this.getUpdate();
if (update.alertThreshold) {
update.cpuAlertThreshold = update.alertThreshold;
update.memoryAlertThreshold = update.alertThreshold;
update.diskAlertThreshold = update.alertThreshold;
update.tempAlertThreshold = update.alertThreshold;
}
next();
});
export default mongoose.model("Notification", NotificationSchema);
+25
View File
@@ -0,0 +1,25 @@
import mongoose from "mongoose";
const RecoveryTokenSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
token: {
type: String,
required: true,
},
expiry: {
type: Date,
default: Date.now,
expires: 600,
},
},
{
timestamps: true,
}
);
export default mongoose.model("RecoveryToken", RecoveryTokenSchema);
+85
View File
@@ -0,0 +1,85 @@
import mongoose from "mongoose";
const StatusPageSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
immutable: true,
required: true,
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
required: true,
},
type: {
type: String,
required: true,
default: "uptime",
enum: ["uptime"],
},
companyName: {
type: String,
required: true,
default: "",
},
url: {
type: String,
unique: true,
required: true,
default: "",
},
timezone: {
type: String,
required: false,
},
color: {
type: String,
required: false,
default: "#4169E1",
},
monitors: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
required: true,
},
],
subMonitors: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
required: true,
},
],
logo: {
data: Buffer,
contentType: String,
},
isPublished: {
type: Boolean,
default: false,
},
showCharts: {
type: Boolean,
default: true,
},
showUptimePercentage: {
type: Boolean,
default: true,
},
showAdminLoginLink: {
type: Boolean,
default: false,
},
customCSS: {
type: String,
default: "",
},
},
{ timestamps: true }
);
export default mongoose.model("StatusPage", StatusPageSchema);
+14
View File
@@ -0,0 +1,14 @@
import mongoose from "mongoose";
const TeamSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
},
{
timestamps: true,
}
);
export default mongoose.model("Team", TeamSchema);
+105
View File
@@ -0,0 +1,105 @@
import mongoose from "mongoose";
import bcrypt from "bcryptjs";
import Monitor from "./Monitor.js";
import Team from "./Team.js";
import Notification from "./Notification.js";
const UserSchema = mongoose.Schema(
{
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
avatarImage: {
type: String,
},
profileImage: {
data: Buffer,
contentType: String,
},
isActive: {
type: Boolean,
default: true,
},
isVerified: {
type: Boolean,
default: false,
},
role: {
type: [String],
default: "user",
enum: ["user", "admin", "superadmin", "demo"],
},
teamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team",
immutable: true,
},
checkTTL: {
type: Number,
},
},
{
timestamps: true,
}
);
UserSchema.pre("save", function (next) {
if (!this.isModified("password")) {
return next();
}
const salt = bcrypt.genSaltSync(10);
this.password = bcrypt.hashSync(this.password, salt);
next();
});
UserSchema.pre("findOneAndUpdate", function (next) {
const update = this.getUpdate();
if ("password" in update) {
const salt = bcrypt.genSaltSync(10);
update.password = bcrypt.hashSync(update.password, salt);
}
next();
});
UserSchema.pre("findOneAndDelete", async function (next) {
try {
const userToDelete = await this.model.findOne(this.getFilter());
if (!userToDelete) return next();
if (userToDelete.role.includes("superadmin")) {
await Team.deleteOne({ _id: userToDelete.teamId });
await Monitor.deleteMany({ userId: userToDelete._id });
await this.model.deleteMany({
teamId: userToDelete.teamId,
_id: { $ne: userToDelete._id },
});
await Notification.deleteMany({ teamId: userToDelete.teamId });
}
next();
} catch (error) {
next(error);
}
});
UserSchema.methods.comparePassword = async function (submittedPassword) {
const res = await bcrypt.compare(submittedPassword, this.password);
return res;
};
const User = mongoose.model("User", UserSchema);
export default User;
+8
View File
@@ -0,0 +1,8 @@
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";
export * from "@/db/models/MonitorStats.js";
export { default as MonitorStatsModel } from "@/db/models/MonitorStats.js";