Merge branch 'bluewave-labs:develop' into Network-tab-implementation-infra
This commit is contained in:
118 files changed
+10748
-6684
No files matched your search
@@ -65,6 +65,12 @@ const AppSettingsSchema = mongoose.Schema(
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
globalThresholds: {
|
||||
cpu: { type: Number },
|
||||
memory: { type: Number },
|
||||
disk: { type: Number },
|
||||
temperature: { type: Number },
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -74,6 +74,10 @@ const StatusPageSchema = mongoose.Schema(
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
customCSS: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@@ -103,12 +103,4 @@ UserSchema.methods.comparePassword = async function (submittedPassword) {
|
||||
|
||||
const User = mongoose.model("User", UserSchema);
|
||||
|
||||
User.init().then(() => {
|
||||
logger.info({
|
||||
message: "User model initialized",
|
||||
service: "UserModel",
|
||||
method: "init",
|
||||
});
|
||||
});
|
||||
|
||||
export default User;
|
||||
@@ -1,89 +1,39 @@
|
||||
import mongoose from "mongoose";
|
||||
import AppSettings from "../models/AppSettings.js";
|
||||
import logger from "../../utils/logger.js";
|
||||
|
||||
//****************************************
|
||||
// User Operations
|
||||
//****************************************
|
||||
|
||||
import * as userModule from "./modules/userModule.js";
|
||||
|
||||
//****************************************
|
||||
// Invite Token Operations
|
||||
//****************************************
|
||||
|
||||
import * as inviteModule from "./modules/inviteModule.js";
|
||||
|
||||
//****************************************
|
||||
// Recovery Operations
|
||||
//****************************************
|
||||
import * as recoveryModule from "./modules/recoveryModule.js";
|
||||
|
||||
//****************************************
|
||||
// Monitors
|
||||
//****************************************
|
||||
|
||||
import * as monitorModule from "./modules/monitorModule.js";
|
||||
|
||||
//****************************************
|
||||
// Page Speed Checks
|
||||
//****************************************
|
||||
|
||||
import * as pageSpeedCheckModule from "./modules/pageSpeedCheckModule.js";
|
||||
|
||||
//****************************************
|
||||
// Hardware Checks
|
||||
//****************************************
|
||||
import * as hardwareCheckModule from "./modules/hardwareCheckModule.js";
|
||||
|
||||
//****************************************
|
||||
// Checks
|
||||
//****************************************
|
||||
|
||||
import * as checkModule from "./modules/checkModule.js";
|
||||
|
||||
//****************************************
|
||||
// Maintenance Window
|
||||
//****************************************
|
||||
import * as maintenanceWindowModule from "./modules/maintenanceWindowModule.js";
|
||||
|
||||
//****************************************
|
||||
// Notifications
|
||||
//****************************************
|
||||
import * as notificationModule from "./modules/notificationModule.js";
|
||||
|
||||
//****************************************
|
||||
// AppSettings
|
||||
//****************************************
|
||||
import * as settingsModule from "./modules/settingsModule.js";
|
||||
|
||||
//****************************************
|
||||
// Status Page
|
||||
//****************************************
|
||||
import * as statusPageModule from "./modules/statusPageModule.js";
|
||||
|
||||
//****************************************
|
||||
// Diagnostic
|
||||
//****************************************
|
||||
import * as diagnosticModule from "./modules/diagnosticModule.js";
|
||||
|
||||
class MongoDB {
|
||||
static SERVICE_NAME = "MongoDB";
|
||||
|
||||
constructor({ appSettings }) {
|
||||
this.appSettings = appSettings;
|
||||
Object.assign(this, userModule);
|
||||
Object.assign(this, inviteModule);
|
||||
Object.assign(this, recoveryModule);
|
||||
Object.assign(this, monitorModule);
|
||||
Object.assign(this, pageSpeedCheckModule);
|
||||
Object.assign(this, hardwareCheckModule);
|
||||
Object.assign(this, checkModule);
|
||||
Object.assign(this, maintenanceWindowModule);
|
||||
Object.assign(this, notificationModule);
|
||||
Object.assign(this, settingsModule);
|
||||
Object.assign(this, statusPageModule);
|
||||
Object.assign(this, diagnosticModule);
|
||||
constructor({
|
||||
logger,
|
||||
envSettings,
|
||||
checkModule,
|
||||
inviteModule,
|
||||
statusPageModule,
|
||||
userModule,
|
||||
hardwareCheckModule,
|
||||
maintenanceWindowModule,
|
||||
monitorModule,
|
||||
networkCheckModule,
|
||||
notificationModule,
|
||||
pageSpeedCheckModule,
|
||||
recoveryModule,
|
||||
settingsModule,
|
||||
}) {
|
||||
this.logger = logger;
|
||||
this.envSettings = envSettings;
|
||||
this.userModule = userModule;
|
||||
this.inviteModule = inviteModule;
|
||||
this.recoveryModule = recoveryModule;
|
||||
this.pageSpeedCheckModule = pageSpeedCheckModule;
|
||||
this.hardwareCheckModule = hardwareCheckModule;
|
||||
this.checkModule = checkModule;
|
||||
this.maintenanceWindowModule = maintenanceWindowModule;
|
||||
this.monitorModule = monitorModule;
|
||||
this.notificationModule = notificationModule;
|
||||
this.settingsModule = settingsModule;
|
||||
this.statusPageModule = statusPageModule;
|
||||
this.networkCheckModule = networkCheckModule;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
@@ -92,9 +42,9 @@ class MongoDB {
|
||||
|
||||
connect = async () => {
|
||||
try {
|
||||
const connectionString = this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
|
||||
const connectionString = this.envSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
|
||||
await mongoose.connect(connectionString);
|
||||
// If there are no AppSettings, create one
|
||||
// If there are no AppSettings, create one // TODO why is this here?
|
||||
await AppSettings.findOneAndUpdate(
|
||||
{}, // empty filter to match any document
|
||||
{}, // empty update
|
||||
@@ -110,13 +60,13 @@ class MongoDB {
|
||||
await model.syncIndexes();
|
||||
}
|
||||
|
||||
logger.info({
|
||||
this.logger.info({
|
||||
message: "Connected to MongoDB",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "connect",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: this.SERVICE_NAME,
|
||||
method: "connect",
|
||||
@@ -128,12 +78,12 @@ class MongoDB {
|
||||
|
||||
disconnect = async () => {
|
||||
try {
|
||||
logger.info({ message: "Disconnecting from MongoDB" });
|
||||
this.logger.info({ message: "Disconnecting from MongoDB" });
|
||||
await mongoose.disconnect();
|
||||
logger.info({ message: "Disconnected from MongoDB" });
|
||||
this.logger.info({ message: "Disconnected from MongoDB" });
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: this.SERVICE_NAME,
|
||||
method: "disconnect",
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import Check from "../../models/Check.js";
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import HardwareCheck from "../../models/HardwareCheck.js";
|
||||
import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
import User from "../../models/User.js";
|
||||
import logger from "../../../utils/logger.js";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { buildChecksSummaryByTeamIdPipeline } from "./checkModuleQueries.js";
|
||||
|
||||
@@ -17,369 +11,296 @@ const dateRangeLookup = {
|
||||
all: undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a check for a monitor
|
||||
* @async
|
||||
* @param {Object} checkData
|
||||
* @param {string} checkData.monitorId
|
||||
* @param {boolean} checkData.status
|
||||
* @param {number} checkData.responseTime
|
||||
* @param {number} checkData.statusCode
|
||||
* @param {string} checkData.message
|
||||
* @returns {Promise<Check>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const createCheck = async (checkData) => {
|
||||
try {
|
||||
const check = await new Check({ ...checkData }).save();
|
||||
return check;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
class CheckModule {
|
||||
constructor({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User }) {
|
||||
this.logger = logger;
|
||||
this.Check = Check;
|
||||
this.HardwareCheck = HardwareCheck;
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
this.Monitor = Monitor;
|
||||
this.User = User;
|
||||
}
|
||||
};
|
||||
|
||||
const createChecks = async (checks) => {
|
||||
try {
|
||||
await Check.insertMany(checks, { ordered: false });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all checks for a monitor
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {Promise<Array<Check>>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
||||
try {
|
||||
status = status === "true" ? true : status === "false" ? false : undefined;
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
// Match
|
||||
const matchStage = {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
...(typeof status !== "undefined" && { status }),
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecks",
|
||||
});
|
||||
break;
|
||||
}
|
||||
createChecks = async (checks) => {
|
||||
try {
|
||||
await this.Check.insertMany(checks, { ordered: false });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
//Sort
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
||||
try {
|
||||
status = status === "true" ? true : status === "false" ? false : undefined;
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const checkModels = {
|
||||
http: Check,
|
||||
ping: Check,
|
||||
docker: Check,
|
||||
port: Check,
|
||||
pagespeed: PageSpeedCheck,
|
||||
hardware: HardwareCheck,
|
||||
};
|
||||
|
||||
const Model = checkModels[type];
|
||||
|
||||
const checks = await Model.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
checksCount: {
|
||||
$ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
|
||||
// Match
|
||||
const matchStage = {
|
||||
monitorId: new ObjectId(monitorId),
|
||||
...(typeof status !== "undefined" && { status }),
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
},
|
||||
checks: {
|
||||
$ifNull: ["$checks", []],
|
||||
}),
|
||||
};
|
||||
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
this.logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecks",
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Sort
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
const checkModels = {
|
||||
http: this.Check,
|
||||
ping: this.Check,
|
||||
docker: this.Check,
|
||||
port: this.Check,
|
||||
pagespeed: this.PageSpeedCheck,
|
||||
hardware: this.HardwareCheck,
|
||||
};
|
||||
|
||||
const Model = checkModels[type];
|
||||
|
||||
const checks = await Model.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getChecksByTeam = async ({ sortOrder, dateRange, filter, ack, page, rowsPerPage, teamId }) => {
|
||||
try {
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
status: false,
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
{
|
||||
$project: {
|
||||
checksCount: {
|
||||
$ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
|
||||
},
|
||||
checks: {
|
||||
$ifNull: ["$checks", []],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
// Add filter to match stage
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecksByTeam",
|
||||
});
|
||||
break;
|
||||
]);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getChecksByTeam = async ({ sortOrder, dateRange, filter, ack, page, rowsPerPage, teamId }) => {
|
||||
try {
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] };
|
||||
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
status: false,
|
||||
...(typeof ack !== "undefined" && ackStage),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
createdAt: {
|
||||
$gte: dateRangeLookup[dateRange],
|
||||
},
|
||||
}),
|
||||
};
|
||||
// Add filter to match stage
|
||||
if (filter !== undefined) {
|
||||
switch (filter) {
|
||||
case "all":
|
||||
break;
|
||||
case "down":
|
||||
break;
|
||||
case "resolve":
|
||||
matchStage.statusCode = 5000;
|
||||
break;
|
||||
default:
|
||||
this.logger.warn({
|
||||
message: "invalid filter",
|
||||
service: SERVICE_NAME,
|
||||
method: "getChecksByTeam",
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
const aggregatePipeline = [
|
||||
{ $match: matchStage },
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "hardwarechecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "pagespeedchecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
checksCount: { $arrayElemAt: ["$summary.checksCount", 0] },
|
||||
checks: "$checks",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const checks = await this.Check.aggregate(aggregatePipeline);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksByTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
ackCheck = async (checkId, teamId, ack) => {
|
||||
try {
|
||||
const updatedCheck = await this.Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true });
|
||||
|
||||
if (!updatedCheck) {
|
||||
throw new Error("Check not found");
|
||||
}
|
||||
|
||||
return updatedCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
ackAllChecks = async (monitorId, teamId, ack, path) => {
|
||||
try {
|
||||
const updatedChecks = await this.Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $set: { ack, ackAt: new Date() } });
|
||||
return updatedChecks.modifiedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackAllChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getChecksSummaryByTeamId = async ({ teamId }) => {
|
||||
try {
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
};
|
||||
const checks = await this.Check.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage }));
|
||||
return checks[0].summary;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksSummaryByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteChecks = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.Check.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
deleteChecksByTeamId = async (teamId) => {
|
||||
try {
|
||||
// Find all monitor IDs for this team (only get _id field for efficiency)
|
||||
const teamMonitors = await this.Monitor.find({ teamId }, { _id: 1 });
|
||||
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 } });
|
||||
|
||||
return deleteResult.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecksByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateChecksTTL = async (teamId, ttl) => {
|
||||
try {
|
||||
await this.Check.collection.dropIndex("expiry_1");
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "updateChecksTTL",
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
sortOrder = sortOrder === "asc" ? 1 : -1;
|
||||
|
||||
// pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
try {
|
||||
await this.Check.collection.createIndex(
|
||||
{ expiry: 1 },
|
||||
{ expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary
|
||||
);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const aggregatePipeline = [
|
||||
{ $match: matchStage },
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "hardwarechecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$unionWith: {
|
||||
coll: "pagespeedchecks",
|
||||
pipeline: [{ $match: matchStage }],
|
||||
},
|
||||
},
|
||||
|
||||
{ $sort: { createdAt: sortOrder } },
|
||||
{
|
||||
$facet: {
|
||||
summary: [{ $count: "checksCount" }],
|
||||
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
checksCount: { $arrayElemAt: ["$summary.checksCount", 0] },
|
||||
checks: "$checks",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const checks = await Check.aggregate(aggregatePipeline);
|
||||
return checks[0];
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksByTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the acknowledgment status of a check
|
||||
* @async
|
||||
* @param {string} checkId - The ID of the check to update
|
||||
* @param {string} teamId - The ID of the team
|
||||
* @param {boolean} ack - The acknowledgment status to set
|
||||
* @returns {Promise<Check>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const ackCheck = async (checkId, teamId, ack) => {
|
||||
try {
|
||||
const updatedCheck = await Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true });
|
||||
|
||||
if (!updatedCheck) {
|
||||
throw new Error("Check not found");
|
||||
// Update user
|
||||
try {
|
||||
await this.User.updateMany({ teamId: teamId }, { checkTTL: ttl });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return updatedCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the acknowledgment status of all checks for a monitor or team
|
||||
* @async
|
||||
* @param {string} id - The monitor ID or team ID
|
||||
* @param {boolean} ack - The acknowledgment status to set
|
||||
* @param {string} path - The path type ('monitor' or 'team')
|
||||
* @returns {Promise<number>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const ackAllChecks = async (monitorId, teamId, ack, path) => {
|
||||
try {
|
||||
const updatedChecks = await Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $set: { ack, ackAt: new Date() } });
|
||||
return updatedChecks.modifiedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "ackAllChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get checks and summary by team ID
|
||||
* @async
|
||||
* @param {string} teamId
|
||||
* @returns {Promise<Object>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getChecksSummaryByTeamId = async ({ teamId }) => {
|
||||
try {
|
||||
const matchStage = {
|
||||
teamId: new ObjectId(teamId),
|
||||
};
|
||||
const checks = await Check.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage }));
|
||||
return checks[0].summary;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getChecksSummaryByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all checks for a monitor
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deleteChecks = async (monitorId) => {
|
||||
try {
|
||||
const result = await Check.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all checks for a team
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deleteChecksByTeamId = async (teamId) => {
|
||||
try {
|
||||
// Find all monitor IDs for this team (only get _id field for efficiency)
|
||||
const teamMonitors = await Monitor.find({ teamId }, { _id: 1 });
|
||||
const monitorIds = teamMonitors.map((monitor) => monitor._id);
|
||||
|
||||
// Delete all checks for these monitors in one operation
|
||||
const deleteResult = await Check.deleteMany({ monitorId: { $in: monitorIds } });
|
||||
|
||||
return deleteResult.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteChecksByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const updateChecksTTL = async (teamId, ttl) => {
|
||||
try {
|
||||
await Check.collection.dropIndex("expiry_1");
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "updateChecksTTL",
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await Check.collection.createIndex(
|
||||
{ expiry: 1 },
|
||||
{ expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary
|
||||
);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
// Update user
|
||||
try {
|
||||
await User.updateMany({ teamId: teamId }, { checkTTL: ttl });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateChecksTTL";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createCheck,
|
||||
createChecks,
|
||||
getChecksByMonitor,
|
||||
getChecksByTeam,
|
||||
ackCheck,
|
||||
ackAllChecks,
|
||||
getChecksSummaryByTeamId,
|
||||
deleteChecks,
|
||||
deleteChecksByTeamId,
|
||||
updateChecksTTL,
|
||||
};
|
||||
export default CheckModule;
|
||||
@@ -1,48 +0,0 @@
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
const SERVICE_NAME = "diagnosticModule";
|
||||
import { buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline } from "./monitorModuleQueries.js";
|
||||
|
||||
const getMonitorsByTeamIdExecutionStats = async (req) => {
|
||||
try {
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
limit = parseInt(limit);
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
if (field === undefined) {
|
||||
field = "name";
|
||||
order = "asc";
|
||||
}
|
||||
// Build match stage
|
||||
const matchStage = { teamId: new ObjectId(req.params.teamId) };
|
||||
if (type !== undefined) {
|
||||
matchStage.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
|
||||
const summary = await Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })).explain("executionStats");
|
||||
|
||||
const monitors = await Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })).explain("executionStats");
|
||||
|
||||
const filteredMonitors = await Monitor.aggregate(
|
||||
buildFilteredMonitorsByTeamIdPipeline({
|
||||
matchStage,
|
||||
filter,
|
||||
page,
|
||||
rowsPerPage,
|
||||
field,
|
||||
order,
|
||||
limit,
|
||||
type,
|
||||
})
|
||||
).explain("executionStats");
|
||||
|
||||
return { summary, monitors, filteredMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMonitorSummaryByTeamIdExecutionStats";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { getMonitorsByTeamIdExecutionStats };
|
||||
@@ -1,70 +1,22 @@
|
||||
import HardwareCheck from "../../models/HardwareCheck.js";
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import logger from "../../../utils/logger.js";
|
||||
|
||||
const SERVICE_NAME = "hardwareCheckModule";
|
||||
const createHardwareCheck = async (hardwareCheckData) => {
|
||||
try {
|
||||
const { monitorId, status } = hardwareCheckData;
|
||||
const n = (await HardwareCheck.countDocuments({ monitorId })) + 1;
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
|
||||
if (!monitor) {
|
||||
logger.error({
|
||||
message: "Monitor not found",
|
||||
service: SERVICE_NAME,
|
||||
method: "createHardwareCheck",
|
||||
details: `monitor ID: ${monitorId}`,
|
||||
});
|
||||
return null;
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let newUptimePercentage;
|
||||
if (monitor.uptimePercentage === undefined) {
|
||||
newUptimePercentage = status === true ? 1 : 0;
|
||||
} else {
|
||||
newUptimePercentage = (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n;
|
||||
}
|
||||
|
||||
await Monitor.findOneAndUpdate({ _id: monitorId }, { uptimePercentage: newUptimePercentage });
|
||||
|
||||
const hardwareCheck = await new HardwareCheck({
|
||||
...hardwareCheckData,
|
||||
}).save();
|
||||
return hardwareCheck;
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
message: "Error creating hardware check",
|
||||
service: SERVICE_NAME,
|
||||
method: "createHardwareCheck",
|
||||
stack: error.stack,
|
||||
});
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createHardwareCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const createHardwareChecks = async (hardwareChecks) => {
|
||||
try {
|
||||
await HardwareCheck.insertMany(hardwareChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createHardwareChecks";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteHardwareChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await HardwareCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteHardwareChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createHardwareCheck, createHardwareChecks, deleteHardwareChecksByMonitorId };
|
||||
export default HardwareCheckModule;
|
||||
@@ -1,89 +1,56 @@
|
||||
import InviteToken from "../../models/InviteToken.js";
|
||||
import crypto from "crypto";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "inviteModule";
|
||||
/**
|
||||
* Request an invite token for a user.
|
||||
*
|
||||
* This function deletes any existing invite tokens for the user's email,
|
||||
* generates a new token, saves it, and then returns the new token.
|
||||
*
|
||||
* @param {Object} userData - The user data.
|
||||
* @param {string} userData.email - The user's email.
|
||||
* @param {mongoose.Schema.Types.ObjectId} userData.teamId - The ID of the team.
|
||||
* @param {Array} userData.role - The user's role(s).
|
||||
* @param {Date} [userData.expiry=Date.now] - The expiry date of the token. Defaults to the current date and time.
|
||||
* @returns {Promise<InviteToken>} The invite token.
|
||||
* @throws {Error} If there is an error.
|
||||
*/
|
||||
const requestInviteToken = async (userData) => {
|
||||
try {
|
||||
await InviteToken.deleteMany({ email: userData.email });
|
||||
userData.token = crypto.randomBytes(32).toString("hex");
|
||||
let inviteToken = new InviteToken(userData);
|
||||
await inviteToken.save();
|
||||
return inviteToken;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestInviteToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves an invite token
|
||||
*
|
||||
* This function searches for an invite token in the database and deletes it.
|
||||
* If the invite token is not found, it throws an error.
|
||||
*
|
||||
* @param {string} token - The invite token to search for.
|
||||
* @returns {Promise<InviteToken>} The invite token data.
|
||||
* @throws {Error} If the invite token is not found or there is another error.
|
||||
*/
|
||||
const getInviteToken = async (token) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const invite = await InviteToken.findOne({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(stringService.authInviteNotFound);
|
||||
class InviteModule {
|
||||
constructor({ InviteToken, crypto, stringService }) {
|
||||
this.InviteToken = InviteToken;
|
||||
this.crypto = crypto;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
requestInviteToken = async (userData) => {
|
||||
try {
|
||||
await this.InviteToken.deleteMany({ email: userData.email });
|
||||
userData.token = this.crypto.randomBytes(32).toString("hex");
|
||||
let inviteToken = new this.InviteToken(userData);
|
||||
await inviteToken.save();
|
||||
return inviteToken;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestInviteToken";
|
||||
throw error;
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves and deletes an invite token
|
||||
*
|
||||
* This function searches for an invite token in the database and deletes it.
|
||||
* If the invite token is not found, it throws an error.
|
||||
*
|
||||
* @param {string} token - The invite token to search for.
|
||||
* @returns {Promise<InviteToken>} The invite token data.
|
||||
* @throws {Error} If the invite token is not found or there is another error.
|
||||
*/
|
||||
const getInviteTokenAndDelete = async (token) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const invite = await InviteToken.findOneAndDelete({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(stringService.authInviteNotFound);
|
||||
getInviteToken = async (token) => {
|
||||
try {
|
||||
const invite = await this.InviteToken.findOne({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(this.stringService.authInviteNotFound);
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteToken";
|
||||
throw error;
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteTokenAndDelete";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
getInviteTokenAndDelete = async (token) => {
|
||||
try {
|
||||
const invite = await this.InviteToken.findOneAndDelete({
|
||||
token,
|
||||
});
|
||||
if (invite === null) {
|
||||
throw new Error(this.stringService.authInviteNotFound);
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getInviteTokenAndDelete";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { requestInviteToken, getInviteToken, getInviteTokenAndDelete };
|
||||
export default InviteModule;
|
||||
@@ -1,192 +1,106 @@
|
||||
import MaintenanceWindow from "../../models/MaintenanceWindow.js";
|
||||
const SERVICE_NAME = "maintenanceWindowModule";
|
||||
|
||||
/**
|
||||
* Asynchronously creates a new MaintenanceWindow document and saves it to the database.
|
||||
* If the maintenance window is a one-time event, the expiry field is set to the same value as the end field.
|
||||
* @async
|
||||
* @function createMaintenanceWindow
|
||||
* @param {Object} maintenanceWindowData - The data for the new MaintenanceWindow document.
|
||||
* @param {mongoose.Schema.Types.ObjectId} maintenanceWindowData.monitorId - The ID of the monitor.
|
||||
* @param {Boolean} maintenanceWindowData.active - Indicates whether the maintenance window is active.
|
||||
* @param {Boolean} maintenanceWindowData.oneTime - Indicates whether the maintenance window is a one-time event.
|
||||
* @param {Date} maintenanceWindowData.start - The start date and time of the maintenance window.
|
||||
* @param {Date} maintenanceWindowData.end - The end date and time of the maintenance window.
|
||||
* @returns {Promise<MaintenanceWindow>} The saved MaintenanceWindow document.
|
||||
* @throws {Error} If there is an error saving the document.
|
||||
*/
|
||||
const createMaintenanceWindow = async (maintenanceWindowData) => {
|
||||
try {
|
||||
const maintenanceWindow = new MaintenanceWindow({
|
||||
...maintenanceWindowData,
|
||||
});
|
||||
class MaintenanceWindowModule {
|
||||
constructor({ MaintenanceWindow }) {
|
||||
this.MaintenanceWindow = MaintenanceWindow;
|
||||
}
|
||||
createMaintenanceWindow = async (maintenanceWindowData) => {
|
||||
try {
|
||||
const maintenanceWindow = new this.MaintenanceWindow({
|
||||
...maintenanceWindowData,
|
||||
});
|
||||
|
||||
// If the maintenance window is a one time window, set the expiry to the end date
|
||||
if (maintenanceWindowData.oneTime) {
|
||||
maintenanceWindow.expiry = maintenanceWindowData.end;
|
||||
// If the maintenance window is a one time window, set the expiry to the end date
|
||||
if (maintenanceWindowData.oneTime) {
|
||||
maintenanceWindow.expiry = maintenanceWindowData.end;
|
||||
}
|
||||
const result = await maintenanceWindow.save();
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createMaintenanceWindow";
|
||||
throw error;
|
||||
}
|
||||
const result = await maintenanceWindow.save();
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createMaintenanceWindow";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await MaintenanceWindow.findOne({
|
||||
_id: id,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves all MaintenanceWindow documents associated with a specific team ID.
|
||||
* @async
|
||||
* @function getMaintenanceWindowByUserId
|
||||
* @param {String} teamId - The ID of the team.
|
||||
* @param {Object} query - The request body.
|
||||
* @returns {Promise<Array<MaintenanceWindow>>} An array of MaintenanceWindow documents.
|
||||
* @throws {Error} If there is an error retrieving the documents.
|
||||
*/
|
||||
const getMaintenanceWindowsByTeamId = async (teamId, query) => {
|
||||
try {
|
||||
let { active, page, rowsPerPage, field, order } = query || {};
|
||||
const maintenanceQuery = { teamId };
|
||||
|
||||
if (active !== undefined) maintenanceQuery.active = active;
|
||||
|
||||
const maintenanceWindowCount = await MaintenanceWindow.countDocuments(maintenanceQuery);
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
};
|
||||
getMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await this.MaintenanceWindow.findOne({
|
||||
_id: id,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Sorting
|
||||
let sort = {};
|
||||
if (field !== undefined && order !== undefined) {
|
||||
sort[field] = order === "asc" ? 1 : -1;
|
||||
getMaintenanceWindowsByTeamId = async (teamId, query) => {
|
||||
try {
|
||||
let { active, page, rowsPerPage, field, order } = query || {};
|
||||
const maintenanceQuery = { teamId };
|
||||
|
||||
if (active !== undefined) maintenanceQuery.active = active;
|
||||
|
||||
const maintenanceWindowCount = await this.MaintenanceWindow.countDocuments(maintenanceQuery);
|
||||
|
||||
// Pagination
|
||||
let skip = 0;
|
||||
if (page && rowsPerPage) {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
// Sorting
|
||||
let sort = {};
|
||||
if (field !== undefined && order !== undefined) {
|
||||
sort[field] = order === "asc" ? 1 : -1;
|
||||
}
|
||||
|
||||
const maintenanceWindows = await this.MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort);
|
||||
|
||||
return { maintenanceWindows, maintenanceWindowCount };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowByUserId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindows = await this.MaintenanceWindow.find({
|
||||
monitorId: monitorId,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindows;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort);
|
||||
deleteMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await this.MaintenanceWindow.findOneAndDelete({ _id: id, teamId });
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return { maintenanceWindows, maintenanceWindowCount };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowByUserId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
editMaintenanceWindowById = async ({ id, body }) => {
|
||||
try {
|
||||
const editedMaintenanceWindow = await this.MaintenanceWindow.findByIdAndUpdate(id, body, { new: true });
|
||||
return editedMaintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves all MaintenanceWindow documents associated with a specific monitor ID.
|
||||
* @async
|
||||
* @function getMaintenanceWindowsByMonitorId
|
||||
* @param {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor.
|
||||
* @returns {Promise<Array<MaintenanceWindow>>} An array of MaintenanceWindow documents.
|
||||
* @throws {Error} If there is an error retrieving the documents.
|
||||
*/
|
||||
const getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindows = await MaintenanceWindow.find({
|
||||
monitorId: monitorId,
|
||||
teamId: teamId,
|
||||
});
|
||||
return maintenanceWindows;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMaintenanceWindowsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously deletes a MaintenanceWindow document by its ID.
|
||||
* @async
|
||||
* @function deleteMaintenanceWindowById
|
||||
* @param {mongoose.Schema.Types.ObjectId} maintenanceWindowId - The ID of the MaintenanceWindow document to delete.
|
||||
* @returns {Promise<MaintenanceWindow>} The deleted MaintenanceWindow document.
|
||||
* @throws {Error} If there is an error deleting the document.
|
||||
*/
|
||||
const deleteMaintenanceWindowById = async ({ id, teamId }) => {
|
||||
try {
|
||||
const maintenanceWindow = await MaintenanceWindow.findOneAndDelete({ _id: id, teamId });
|
||||
return maintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously deletes all MaintenanceWindow documents associated with a specific monitor ID.
|
||||
* @async
|
||||
* @function deleteMaintenanceWindowByMonitorId
|
||||
* @param {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor.
|
||||
* @returns {Promise<Object>} The result of the delete operation. This object contains information about the operation, such as the number of documents deleted.
|
||||
* @throws {Error} If there is an error deleting the documents.
|
||||
* @example
|
||||
*/
|
||||
const deleteMaintenanceWindowByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await MaintenanceWindow.deleteMany({ monitorId: monitorId });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously deletes all MaintenanceWindow documents associated with a specific user ID.
|
||||
* @async
|
||||
* @function deleteMaintenanceWindowByUserId
|
||||
* @param {String} userId - The ID of the user.
|
||||
* @returns {Promise<Object>} The result of the delete operation. This object contains information about the operation, such as the number of documents deleted.
|
||||
* @throws {Error} If there is an error deleting the documents.
|
||||
* @example
|
||||
*/
|
||||
const deleteMaintenanceWindowByUserId = async (userId) => {
|
||||
try {
|
||||
const result = await MaintenanceWindow.deleteMany({ userId: userId });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteMaintenanceWindowByUserId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editMaintenanceWindowById = async ({ id, body, teamId }) => {
|
||||
try {
|
||||
const editedMaintenanceWindow = await MaintenanceWindow.findByIdAndUpdate(id, body, { new: true });
|
||||
return editedMaintenanceWindow;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editMaintenanceWindowById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createMaintenanceWindow,
|
||||
getMaintenanceWindowById,
|
||||
getMaintenanceWindowsByTeamId,
|
||||
getMaintenanceWindowsByMonitorId,
|
||||
deleteMaintenanceWindowById,
|
||||
deleteMaintenanceWindowByMonitorId,
|
||||
deleteMaintenanceWindowByUserId,
|
||||
editMaintenanceWindowById,
|
||||
};
|
||||
export default MaintenanceWindowModule;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,44 +1,30 @@
|
||||
import NetworkCheck from "../../models/NetworkCheck.js";
|
||||
|
||||
const SERVICE_NAME = "networkCheckModule";
|
||||
|
||||
/**
|
||||
* Creates and saves a new network check document to the database.
|
||||
* @async
|
||||
* @param {object} networkCheckData - The data for the new network check. This should conform to the NetworkCheckSchema.
|
||||
* @param {string} networkCheckData.monitorId - The ID of the monitor associated with this check.
|
||||
* @returns {Promise<object>} A promise that resolves to the newly created network check document.
|
||||
* @throws {Error} Throws an error if the database operation fails.
|
||||
*/
|
||||
const createNetworkCheck = async (networkCheckData) => {
|
||||
try {
|
||||
const networkCheck = await new NetworkCheck(networkCheckData);
|
||||
await networkCheck.save();
|
||||
return networkCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNetworkCheck";
|
||||
throw error;
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of network checks for a specific monitor, sorted by most recent.
|
||||
* @async
|
||||
* @param {string} monitorId - The ID of the monitor to retrieve checks for.
|
||||
* @param {number} [limit=100] - The maximum number of checks to return. Defaults to 100.
|
||||
* @returns {Promise<Array<object>>} A promise that resolves to an array of network check documents.
|
||||
* @throws {Error} Throws an error if the database operation fails.
|
||||
*/
|
||||
const getNetworkChecksByMonitorId = async (monitorId, limit = 100) => {
|
||||
try {
|
||||
const networkChecks = await NetworkCheck.find({ monitorId }).sort({ createdAt: -1 }).limit(limit);
|
||||
return networkChecks;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNetworkChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createNetworkCheck, getNetworkChecksByMonitorId };
|
||||
export default NetworkCheckModule;
|
||||
@@ -1,125 +1,100 @@
|
||||
import Notification from "../../models/Notification.js";
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
// import Notification from "../../models/Notification.js";
|
||||
// import Monitor from "../../models/Monitor.js";
|
||||
const SERVICE_NAME = "notificationModule";
|
||||
/**
|
||||
* Creates a new notification.
|
||||
* @param {Object} notificationData - The data for the new notification.
|
||||
* @param {mongoose.Types.ObjectId} notificationData.monitorId - The ID of the monitor.
|
||||
* @param {string} notificationData.type - The type of the notification (e.g., "email", "sms").
|
||||
* @param {string} [notificationData.address] - The address for the notification (if applicable).
|
||||
* @param {string} [notificationData.phone] - The phone number for the notification (if applicable).
|
||||
* @returns {Promise<Object>} The created notification.
|
||||
* @throws Will throw an error if the notification cannot be created.
|
||||
*/
|
||||
const createNotification = async (notificationData) => {
|
||||
try {
|
||||
const notification = await new Notification({ ...notificationData }).save();
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationsByTeamId = async (teamId) => {
|
||||
try {
|
||||
const notifications = await Notification.find({ teamId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByTeamId";
|
||||
throw error;
|
||||
class NotificationModule {
|
||||
constructor({ Notification, Monitor }) {
|
||||
this.Notification = Notification;
|
||||
this.Monitor = Monitor;
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationsByIds = async (notificationIds) => {
|
||||
try {
|
||||
const notifications = await Notification.find({ _id: { $in: notificationIds } });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByIds";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves notifications by monitor ID.
|
||||
* @param {mongoose.Types.ObjectId} monitorId - The ID of the monitor.
|
||||
* @returns {Promise<Array<Object>>} An array of notifications.
|
||||
* @throws Will throw an error if the notifications cannot be retrieved.
|
||||
*/
|
||||
const getNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const notifications = await Notification.find({ monitorId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await Notification.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await Notification.findById(id);
|
||||
if (!notification) {
|
||||
throw new Error("Notification not found");
|
||||
createNotification = async (notificationData) => {
|
||||
try {
|
||||
const notification = await new this.Notification({ ...notificationData }).save();
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationsByTeamId = async (teamId) => {
|
||||
try {
|
||||
const notifications = await this.Notification.find({ teamId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationsByIds = async (notificationIds) => {
|
||||
try {
|
||||
const notifications = await this.Notification.find({ _id: { $in: notificationIds } });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByIds";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const notifications = await this.Notification.find({ monitorId });
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteNotificationsByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await this.Notification.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationsByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await this.Notification.findById(id);
|
||||
if (!notification) {
|
||||
throw new Error("Notification not found");
|
||||
}
|
||||
|
||||
const result = await Notification.findByIdAndDelete(id);
|
||||
await Monitor.updateMany({ notifications: id }, { $pull: { notifications: id } });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const result = await this.Notification.findByIdAndDelete(id);
|
||||
await this.Monitor.updateMany({ notifications: id }, { $pull: { notifications: id } });
|
||||
return result;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await this.Notification.findById(id);
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
editNotification = async (id, notificationData) => {
|
||||
try {
|
||||
const notification = await this.Notification.findByIdAndUpdate(id, notificationData, {
|
||||
new: true,
|
||||
});
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const getNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await Notification.findById(id);
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editNotification = async (id, notificationData) => {
|
||||
try {
|
||||
const notification = await Notification.findByIdAndUpdate(id, notificationData, {
|
||||
new: true,
|
||||
});
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createNotification,
|
||||
getNotificationsByTeamId,
|
||||
getNotificationsByIds,
|
||||
getNotificationsByMonitorId,
|
||||
deleteNotificationsByMonitorId,
|
||||
deleteNotificationById,
|
||||
getNotificationById,
|
||||
editNotification,
|
||||
};
|
||||
export default NotificationModule;
|
||||
@@ -1,57 +1,31 @@
|
||||
import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
// import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
const SERVICE_NAME = "pageSpeedCheckModule";
|
||||
/**
|
||||
* Create a PageSpeed check for a monitor
|
||||
* @async
|
||||
* @param {Object} pageSpeedCheckData
|
||||
* @param {string} pageSpeedCheckData.monitorId
|
||||
* @param {number} pageSpeedCheckData.accessibility
|
||||
* @param {number} pageSpeedCheckData.bestPractices
|
||||
* @param {number} pageSpeedCheckData.seo
|
||||
* @param {number} pageSpeedCheckData.performance
|
||||
* @returns {Promise<PageSpeedCheck>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const createPageSpeedCheck = async (pageSpeedCheckData) => {
|
||||
try {
|
||||
const pageSpeedCheck = await new PageSpeedCheck({
|
||||
...pageSpeedCheckData,
|
||||
}).save();
|
||||
return pageSpeedCheck;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createPageSpeedCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const createPageSpeedChecks = async (pageSpeedChecks) => {
|
||||
try {
|
||||
await PageSpeedCheck.insertMany(pageSpeedChecks, { ordered: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createPageSpeedCheck";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all PageSpeed checks for a monitor
|
||||
* @async
|
||||
* @param {string} monitorId
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
|
||||
const deletePageSpeedChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await PageSpeedCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deletePageSpeedChecksByMonitorId";
|
||||
throw error;
|
||||
class PageSpeedCheckModule {
|
||||
constructor({ PageSpeedCheck }) {
|
||||
this.PageSpeedCheck = PageSpeedCheck;
|
||||
}
|
||||
};
|
||||
|
||||
export { createPageSpeedCheck, createPageSpeedChecks, deletePageSpeedChecksByMonitorId };
|
||||
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,86 +1,80 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import RecoveryToken from "../../models/RecoveryToken.js";
|
||||
import crypto from "crypto";
|
||||
import serviceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "recoveryModule";
|
||||
|
||||
/**
|
||||
* Request a recovery token
|
||||
* @async
|
||||
* @param {string} email
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const requestRecoveryToken = async (email) => {
|
||||
try {
|
||||
// Delete any existing tokens
|
||||
await RecoveryToken.deleteMany({ email });
|
||||
let recoveryToken = new RecoveryToken({
|
||||
email,
|
||||
token: crypto.randomBytes(32).toString("hex"),
|
||||
});
|
||||
await recoveryToken.save();
|
||||
return recoveryToken;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestRecoveryToken";
|
||||
throw error;
|
||||
class RecoveryModule {
|
||||
constructor({ User, RecoveryToken, crypto, stringService }) {
|
||||
this.User = User;
|
||||
this.RecoveryToken = RecoveryToken;
|
||||
this.crypto = crypto;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
};
|
||||
|
||||
const validateRecoveryToken = async (candidateToken) => {
|
||||
const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const recoveryToken = await RecoveryToken.findOne({
|
||||
token: candidateToken,
|
||||
});
|
||||
if (recoveryToken !== null) {
|
||||
requestRecoveryToken = async (email) => {
|
||||
try {
|
||||
// Delete any existing tokens
|
||||
await this.RecoveryToken.deleteMany({ email });
|
||||
let recoveryToken = new this.RecoveryToken({
|
||||
email,
|
||||
token: this.crypto.randomBytes(32).toString("hex"),
|
||||
});
|
||||
await recoveryToken.save();
|
||||
return recoveryToken;
|
||||
} else {
|
||||
throw new Error(stringService.dbTokenNotFound);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "requestRecoveryToken";
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "validateRecoveryToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const resetPassword = async (password, candidateToken) => {
|
||||
const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const newPassword = password;
|
||||
|
||||
// Validate token again
|
||||
const recoveryToken = await validateRecoveryToken(candidateToken);
|
||||
const user = await UserModel.findOne({ email: recoveryToken.email });
|
||||
|
||||
if (user === null) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
};
|
||||
validateRecoveryToken = async (candidateToken) => {
|
||||
try {
|
||||
const recoveryToken = await this.RecoveryToken.findOne({
|
||||
token: candidateToken,
|
||||
});
|
||||
if (recoveryToken !== null) {
|
||||
return recoveryToken;
|
||||
} else {
|
||||
throw new Error(this.stringService.dbTokenNotFound);
|
||||
}
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "validateRecoveryToken";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const match = await user.comparePassword(newPassword);
|
||||
if (match === true) {
|
||||
throw new Error(stringService.dbResetPasswordBadMatch);
|
||||
resetPassword = async (password, candidateToken) => {
|
||||
try {
|
||||
const newPassword = password;
|
||||
|
||||
// Validate token again
|
||||
const recoveryToken = await this.validateRecoveryToken(candidateToken);
|
||||
const user = await this.User.findOne({ email: recoveryToken.email });
|
||||
|
||||
if (user === null) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
|
||||
const match = await user.comparePassword(newPassword);
|
||||
|
||||
if (match === true) {
|
||||
throw new Error("Password cannot be the same as the old password");
|
||||
}
|
||||
|
||||
user.password = newPassword;
|
||||
await user.save();
|
||||
await this.RecoveryToken.deleteMany({ email: recoveryToken.email });
|
||||
// Fetch the user again without the password
|
||||
const userWithoutPassword = await this.User.findOne({
|
||||
email: recoveryToken.email,
|
||||
})
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return userWithoutPassword;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "resetPassword";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
user.password = newPassword;
|
||||
await user.save();
|
||||
await RecoveryToken.deleteMany({ email: recoveryToken.email });
|
||||
// Fetch the user again without the password
|
||||
const userWithoutPassword = await UserModel.findOne({
|
||||
email: recoveryToken.email,
|
||||
})
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return userWithoutPassword;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "resetPassword";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { requestRecoveryToken, validateRecoveryToken, resetPassword };
|
||||
export default RecoveryModule;
|
||||
@@ -1,41 +1,36 @@
|
||||
import AppSettings from "../../models/AppSettings.js";
|
||||
// import AppSettings from "../../models/AppSettings.js";
|
||||
const SERVICE_NAME = "SettingsModule";
|
||||
|
||||
const getAppSettings = async () => {
|
||||
try {
|
||||
const settings = AppSettings.findOne();
|
||||
return settings;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getSettings";
|
||||
throw error;
|
||||
class SettingsModule {
|
||||
constructor({ AppSettings }) {
|
||||
this.AppSettings = AppSettings;
|
||||
}
|
||||
};
|
||||
|
||||
const updateAppSettings = async (newSettings) => {
|
||||
try {
|
||||
const update = { $set: { ...newSettings } };
|
||||
updateAppSettings = async (newSettings) => {
|
||||
try {
|
||||
const update = { $set: { ...newSettings } };
|
||||
|
||||
if (newSettings.pagespeedApiKey === "") {
|
||||
update.$unset = { pagespeedApiKey: "" };
|
||||
delete update.$set.pagespeedApiKey;
|
||||
if (newSettings.pagespeedApiKey === "") {
|
||||
update.$unset = { pagespeedApiKey: "" };
|
||||
delete update.$set.pagespeedApiKey;
|
||||
}
|
||||
|
||||
if (newSettings.systemEmailPassword === "") {
|
||||
update.$unset = { systemEmailPassword: "" };
|
||||
delete update.$set.systemEmailPassword;
|
||||
}
|
||||
|
||||
await this.AppSettings.findOneAndUpdate({}, update, {
|
||||
upsert: true,
|
||||
});
|
||||
const settings = await this.AppSettings.findOne().select("-__v -_id -createdAt -updatedAt -singleton").lean();
|
||||
return settings;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateAppSettings";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (newSettings.systemEmailPassword === "") {
|
||||
update.$unset = { systemEmailPassword: "" };
|
||||
delete update.$set.systemEmailPassword;
|
||||
}
|
||||
|
||||
await AppSettings.findOneAndUpdate({}, update, {
|
||||
upsert: true,
|
||||
});
|
||||
const settings = await AppSettings.findOne().select("-__v -_id -createdAt -updatedAt -singleton").lean();
|
||||
return settings;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateAppSettings";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { getAppSettings, updateAppSettings };
|
||||
export default SettingsModule;
|
||||
@@ -1,292 +1,287 @@
|
||||
import StatusPage from "../../models/StatusPage.js";
|
||||
import { NormalizeData } from "../../../utils/dataUtils.js";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
// import StatusPage from "../../models/StatusPage.js";
|
||||
// import { NormalizeData } from "../../../utils/dataUtils.js";
|
||||
// import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
// import StringService from "../../../service/system/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageModule";
|
||||
|
||||
const createStatusPage = async ({ statusPageData, image, userId, teamId }) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const statusPage = new StatusPage({
|
||||
...statusPageData,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
if (image) {
|
||||
statusPage.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}
|
||||
await statusPage.save();
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
if (error?.code === 11000) {
|
||||
// Handle duplicate URL errors
|
||||
error.status = 400;
|
||||
error.message = stringService.statusPageUrlNotUnique;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createStatusPage";
|
||||
throw error;
|
||||
class StatusPageModule {
|
||||
constructor({ StatusPage, NormalizeData, stringService }) {
|
||||
this.StatusPage = StatusPage;
|
||||
this.NormalizeData = NormalizeData;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
};
|
||||
|
||||
const updateStatusPage = async (statusPageData, image) => {
|
||||
try {
|
||||
if (image) {
|
||||
statusPageData.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
} else {
|
||||
statusPageData.logo = null;
|
||||
}
|
||||
|
||||
if (statusPageData.deleteSubmonitors === "true") {
|
||||
statusPageData.subMonitors = [];
|
||||
}
|
||||
const statusPage = await StatusPage.findOneAndUpdate({ url: statusPageData.url }, statusPageData, {
|
||||
new: true,
|
||||
});
|
||||
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPageByUrl = async (url, type) => {
|
||||
// TODO This is deprecated, can remove and have controller call getStatusPage
|
||||
try {
|
||||
return getStatusPage(url);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPagesByTeamId = async (teamId) => {
|
||||
try {
|
||||
const statusPages = await StatusPage.find({ teamId });
|
||||
return statusPages;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPagesByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusPage = async (url) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const preliminaryStatusPage = await StatusPage.findOne({ url });
|
||||
if (!preliminaryStatusPage) {
|
||||
const error = new Error(stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
createStatusPage = async ({ statusPageData, image, userId, teamId }) => {
|
||||
try {
|
||||
const statusPage = new this.StatusPage({
|
||||
...statusPageData,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
if (image) {
|
||||
statusPage.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}
|
||||
await statusPage.save();
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
if (error?.code === 11000) {
|
||||
// Handle duplicate URL errors
|
||||
error.status = 400;
|
||||
error.message = this.stringService.statusPageUrlNotUnique;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
if (!preliminaryStatusPage.monitors || preliminaryStatusPage.monitors.length === 0) {
|
||||
const { _id, color, companyName, isPublished, logo, originalMonitors, showCharts, showUptimePercentage, timezone, showAdminLoginLink, url } =
|
||||
preliminaryStatusPage;
|
||||
return {
|
||||
statusPage: {
|
||||
_id,
|
||||
color,
|
||||
companyName,
|
||||
isPublished,
|
||||
logo,
|
||||
originalMonitors,
|
||||
showCharts,
|
||||
showUptimePercentage,
|
||||
timezone,
|
||||
showAdminLoginLink,
|
||||
url,
|
||||
},
|
||||
monitors: [],
|
||||
};
|
||||
updateStatusPage = async (statusPageData, image) => {
|
||||
try {
|
||||
if (image) {
|
||||
statusPageData.logo = {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
} else {
|
||||
statusPageData.logo = null;
|
||||
}
|
||||
|
||||
if (statusPageData.deleteSubmonitors === "true") {
|
||||
statusPageData.subMonitors = [];
|
||||
}
|
||||
const statusPage = await this.StatusPage.findOneAndUpdate({ url: statusPageData.url }, statusPageData, {
|
||||
new: true,
|
||||
});
|
||||
|
||||
return statusPage;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const statusPageQuery = await StatusPage.aggregate([
|
||||
{ $match: { url: url } },
|
||||
{
|
||||
$set: {
|
||||
originalMonitors: "$monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "monitors",
|
||||
localField: "monitors",
|
||||
foreignField: "_id",
|
||||
as: "monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: {
|
||||
path: "$monitors",
|
||||
preserveNullAndEmptyArrays: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "maintenancewindows",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [{ $match: { $expr: { $eq: ["$monitorId", "$$monitorId"] } } }],
|
||||
as: "monitors.maintenanceWindows",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
{ $limit: 25 },
|
||||
],
|
||||
as: "monitors.checks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"monitors.orderIndex": {
|
||||
$indexOfArray: ["$originalMonitors", "$monitors._id"],
|
||||
},
|
||||
"monitors.isMaintenance": {
|
||||
$reduce: {
|
||||
input: "$monitors.maintenanceWindows",
|
||||
initialValue: false,
|
||||
in: {
|
||||
$or: [
|
||||
"$$value",
|
||||
{
|
||||
$and: [{ $eq: ["$$this.active", true] }, { $lte: ["$$this.start", "$$NOW"] }, { $gte: ["$$this.end", "$$NOW"] }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $match: { "monitors.orderIndex": { $ne: -1 } } },
|
||||
{ $sort: { "monitors.orderIndex": 1 } },
|
||||
getStatusPageByUrl = async (url) => {
|
||||
// TODO This is deprecated, can remove and have controller call getStatusPage
|
||||
try {
|
||||
return this.getStatusPage(url);
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
statusPage: { $first: "$$ROOT" },
|
||||
monitors: { $push: "$monitors" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
getStatusPagesByTeamId = async (teamId) => {
|
||||
try {
|
||||
const statusPages = await this.StatusPage.find({ teamId });
|
||||
return statusPages;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPagesByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getStatusPage = async (url) => {
|
||||
try {
|
||||
const preliminaryStatusPage = await this.StatusPage.findOne({ url });
|
||||
if (!preliminaryStatusPage) {
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!preliminaryStatusPage.monitors || preliminaryStatusPage.monitors.length === 0) {
|
||||
const { _id, color, companyName, isPublished, logo, originalMonitors, showCharts, showUptimePercentage, timezone, showAdminLoginLink, url } =
|
||||
preliminaryStatusPage;
|
||||
return {
|
||||
statusPage: {
|
||||
_id: 1,
|
||||
color: 1,
|
||||
companyName: 1,
|
||||
isPublished: 1,
|
||||
logo: 1,
|
||||
originalMonitors: 1,
|
||||
showCharts: 1,
|
||||
showUptimePercentage: 1,
|
||||
timezone: 1,
|
||||
showAdminLoginLink: 1,
|
||||
url: 1,
|
||||
_id,
|
||||
color,
|
||||
companyName,
|
||||
isPublished,
|
||||
logo,
|
||||
originalMonitors,
|
||||
showCharts,
|
||||
showUptimePercentage,
|
||||
timezone,
|
||||
showAdminLoginLink,
|
||||
url,
|
||||
},
|
||||
monitors: {
|
||||
_id: 1,
|
||||
userId: 1,
|
||||
teamId: 1,
|
||||
name: 1,
|
||||
description: 1,
|
||||
status: 1,
|
||||
type: 1,
|
||||
ignoreTlsErrors: 1,
|
||||
jsonPath: 1,
|
||||
expectedValue: 1,
|
||||
matchMethod: 1,
|
||||
url: 1,
|
||||
port: 1,
|
||||
isActive: 1,
|
||||
interval: 1,
|
||||
uptimePercentage: 1,
|
||||
notifications: 1,
|
||||
secret: 1,
|
||||
thresholds: 1,
|
||||
alertThreshold: 1,
|
||||
cpuAlertThreshold: 1,
|
||||
memoryAlertThreshold: 1,
|
||||
diskAlertThreshold: 1,
|
||||
tempAlertThreshold: 1,
|
||||
checks: 1,
|
||||
isMaintenance: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
monitors: [],
|
||||
};
|
||||
}
|
||||
|
||||
const statusPageQuery = await this.StatusPage.aggregate([
|
||||
{ $match: { url: url } },
|
||||
{
|
||||
$set: {
|
||||
originalMonitors: "$monitors",
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
if (!statusPageQuery.length) {
|
||||
const error = new Error(stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
{
|
||||
$lookup: {
|
||||
from: "monitors",
|
||||
localField: "monitors",
|
||||
foreignField: "_id",
|
||||
as: "monitors",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: {
|
||||
path: "$monitors",
|
||||
preserveNullAndEmptyArrays: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "maintenancewindows",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [{ $match: { $expr: { $eq: ["$monitorId", "$$monitorId"] } } }],
|
||||
as: "monitors.maintenanceWindows",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$monitors._id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
{ $limit: 25 },
|
||||
],
|
||||
as: "monitors.checks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
"monitors.orderIndex": {
|
||||
$indexOfArray: ["$originalMonitors", "$monitors._id"],
|
||||
},
|
||||
"monitors.isMaintenance": {
|
||||
$reduce: {
|
||||
input: "$monitors.maintenanceWindows",
|
||||
initialValue: false,
|
||||
in: {
|
||||
$or: [
|
||||
"$$value",
|
||||
{
|
||||
$and: [{ $eq: ["$$this.active", true] }, { $lte: ["$$this.start", "$$NOW"] }, { $gte: ["$$this.end", "$$NOW"] }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $match: { "monitors.orderIndex": { $ne: -1 } } },
|
||||
{ $sort: { "monitors.orderIndex": 1 } },
|
||||
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
statusPage: { $first: "$$ROOT" },
|
||||
monitors: { $push: "$monitors" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
statusPage: {
|
||||
_id: 1,
|
||||
color: 1,
|
||||
companyName: 1,
|
||||
isPublished: 1,
|
||||
logo: 1,
|
||||
originalMonitors: 1,
|
||||
showCharts: 1,
|
||||
showUptimePercentage: 1,
|
||||
timezone: 1,
|
||||
showAdminLoginLink: 1,
|
||||
url: 1,
|
||||
},
|
||||
monitors: {
|
||||
_id: 1,
|
||||
userId: 1,
|
||||
teamId: 1,
|
||||
name: 1,
|
||||
description: 1,
|
||||
status: 1,
|
||||
type: 1,
|
||||
ignoreTlsErrors: 1,
|
||||
jsonPath: 1,
|
||||
expectedValue: 1,
|
||||
matchMethod: 1,
|
||||
url: 1,
|
||||
port: 1,
|
||||
isActive: 1,
|
||||
interval: 1,
|
||||
uptimePercentage: 1,
|
||||
notifications: 1,
|
||||
secret: 1,
|
||||
thresholds: 1,
|
||||
alertThreshold: 1,
|
||||
cpuAlertThreshold: 1,
|
||||
memoryAlertThreshold: 1,
|
||||
diskAlertThreshold: 1,
|
||||
tempAlertThreshold: 1,
|
||||
checks: 1,
|
||||
isMaintenance: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
if (!statusPageQuery.length) {
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const { statusPage, monitors } = statusPageQuery[0];
|
||||
|
||||
const normalizedMonitors = monitors.map((monitor) => {
|
||||
return {
|
||||
...monitor,
|
||||
checks: this.NormalizeData(monitor.checks, 10, 100),
|
||||
};
|
||||
});
|
||||
|
||||
return { statusPage, monitors: normalizedMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const { statusPage, monitors } = statusPageQuery[0];
|
||||
deleteStatusPage = async (url) => {
|
||||
try {
|
||||
await this.StatusPage.deleteOne({ url });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
deleteStatusPagesByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
await this.StatusPage.deleteMany({ monitors: { $in: [monitorId] } });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPageByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const normalizedMonitors = monitors.map((monitor) => {
|
||||
return {
|
||||
...monitor,
|
||||
checks: NormalizeData(monitor.checks, 10, 100),
|
||||
};
|
||||
});
|
||||
|
||||
return { statusPage, monitors: normalizedMonitors };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getStatusPageByUrl";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteStatusPage = async (url) => {
|
||||
try {
|
||||
await StatusPage.deleteOne({ url });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPage";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteStatusPagesByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
await StatusPage.deleteMany({ monitors: { $in: [monitorId] } });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPageByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createStatusPage,
|
||||
updateStatusPage,
|
||||
getStatusPagesByTeamId,
|
||||
getStatusPage,
|
||||
getStatusPageByUrl,
|
||||
deleteStatusPage,
|
||||
deleteStatusPagesByMonitorId,
|
||||
};
|
||||
export default StatusPageModule;
|
||||
@@ -1,261 +1,171 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import TeamModel from "../../models/Team.js";
|
||||
import { GenerateAvatarImage } from "../../../utils/imageProcessing.js";
|
||||
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
import { ParseBoolean } from "../../../utils/utils.js";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
const SERVICE_NAME = "userModule";
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
|
||||
const checkSuperadmin = async () => {
|
||||
const superAdmin = await UserModel.findOne({ role: "superadmin" });
|
||||
if (superAdmin !== null) {
|
||||
return true;
|
||||
class UserModule {
|
||||
constructor({ User, Team, GenerateAvatarImage, ParseBoolean, stringService }) {
|
||||
this.User = User;
|
||||
this.Team = Team;
|
||||
this.GenerateAvatarImage = GenerateAvatarImage;
|
||||
this.ParseBoolean = ParseBoolean;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert a User
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const insertUser = async (userData, imageFile, generateAvatarImage = GenerateAvatarImage) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
userData.profileImage = {
|
||||
data: imageFile.buffer,
|
||||
contentType: imageFile.mimetype,
|
||||
};
|
||||
checkSuperadmin = async () => {
|
||||
try {
|
||||
const superAdmin = await this.User.findOne({ role: "superadmin" });
|
||||
if (superAdmin !== null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "checkSuperadmin";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await generateAvatarImage(imageFile);
|
||||
userData.avatarImage = avatar;
|
||||
insertUser = async (userData, imageFile) => {
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
userData.profileImage = {
|
||||
data: imageFile.buffer,
|
||||
contentType: imageFile.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await this.GenerateAvatarImage(imageFile);
|
||||
userData.avatarImage = avatar;
|
||||
}
|
||||
|
||||
// Handle creating team if superadmin
|
||||
if (userData.role.includes("superadmin")) {
|
||||
const team = new this.Team({
|
||||
email: userData.email,
|
||||
});
|
||||
userData.teamId = team._id;
|
||||
userData.checkTTL = 60 * 60 * 24 * 30;
|
||||
await team.save();
|
||||
}
|
||||
|
||||
const newUser = new this.User(userData);
|
||||
await newUser.save();
|
||||
return await this.User.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = this.stringService.dbUserExists;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getUserByEmail = async (email) => {
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await this.User.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserByEmail";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateUser = async ({ userId, user, file }) => {
|
||||
if (!userId) {
|
||||
throw new Error("No user in request");
|
||||
}
|
||||
|
||||
// Handle creating team if superadmin
|
||||
if (userData.role.includes("superadmin")) {
|
||||
const team = new TeamModel({
|
||||
email: userData.email,
|
||||
});
|
||||
userData.teamId = team._id;
|
||||
userData.checkTTL = 60 * 60 * 24 * 30;
|
||||
await team.save();
|
||||
try {
|
||||
const candidateUser = { ...user };
|
||||
|
||||
if (this.ParseBoolean(candidateUser.deleteProfileImage) === true) {
|
||||
candidateUser.profileImage = null;
|
||||
candidateUser.avatarImage = null;
|
||||
} else if (file) {
|
||||
// 1. Save the full size image
|
||||
candidateUser.profileImage = {
|
||||
data: file.buffer,
|
||||
contentType: file.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await this.GenerateAvatarImage(file);
|
||||
candidateUser.avatarImage = avatar;
|
||||
}
|
||||
|
||||
const updatedUser = await this.User.findByIdAndUpdate(
|
||||
userId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateUser";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const newUser = new UserModel(userData);
|
||||
await newUser.save();
|
||||
return await UserModel.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = stringService.dbUserExists;
|
||||
};
|
||||
deleteUser = async (userId) => {
|
||||
try {
|
||||
const deletedUser = await this.User.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteUser";
|
||||
throw error;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get User by Email
|
||||
* Gets a user by Email. Not sure if we'll ever need this except for login.
|
||||
* If not needed except for login, we can move password comparison here
|
||||
* Throws error if user not found
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getUserByEmail = async (email) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await UserModel.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
getAllUsers = async () => {
|
||||
try {
|
||||
const users = await this.User.find().select("-password").select("-profileImage");
|
||||
return users;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllUsers";
|
||||
throw error;
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserByEmail";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
getUserById = async (roles, userId) => {
|
||||
try {
|
||||
if (!roles.includes("superadmin")) {
|
||||
throw new Error("User is not a superadmin");
|
||||
}
|
||||
|
||||
const updateUser = async ({ userId, user, file }) => {
|
||||
if (!userId) {
|
||||
throw new Error("No user in request");
|
||||
}
|
||||
const user = await this.User.findById(userId).select("-password").select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
try {
|
||||
const candidateUser = { ...user };
|
||||
|
||||
if (ParseBoolean(candidateUser.deleteProfileImage) === true) {
|
||||
candidateUser.profileImage = null;
|
||||
candidateUser.avatarImage = null;
|
||||
} else if (file) {
|
||||
// 1. Save the full size image
|
||||
candidateUser.profileImage = {
|
||||
data: file.buffer,
|
||||
contentType: file.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await GenerateAvatarImage(file);
|
||||
candidateUser.avatarImage = avatar;
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// ******************************************
|
||||
// End handling profile image
|
||||
// ******************************************
|
||||
|
||||
const updatedUser = await UserModel.findByIdAndUpdate(
|
||||
userId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteUser = async (userId) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const deletedUser = await UserModel.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
editUserById = async (userId, user) => {
|
||||
try {
|
||||
await this.User.findByIdAndUpdate(userId, user, { new: true }).select("-password").select("-profileImage");
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editUserById";
|
||||
throw error;
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @async
|
||||
* @param {string} teamId
|
||||
* @returns {void}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteTeam = async (teamId) => {
|
||||
try {
|
||||
await TeamModel.findByIdAndDelete(teamId);
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAllOtherUsers = async () => {
|
||||
try {
|
||||
await UserModel.deleteMany({ role: { $ne: "superadmin" } });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteAllOtherUsers";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getAllUsers = async () => {
|
||||
try {
|
||||
const users = await UserModel.find().select("-password").select("-profileImage");
|
||||
return users;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllUsers";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const logoutUser = async (userId) => {
|
||||
try {
|
||||
await UserModel.updateOne({ _id: userId }, { $unset: { authToken: null } });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "logoutUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getUserById = async (roles, userId) => {
|
||||
try {
|
||||
if (!roles.includes("superadmin")) {
|
||||
throw new Error("User is not a superadmin");
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(userId).select("-password").select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editUserById = async (userId, user) => {
|
||||
try {
|
||||
await UserModel.findByIdAndUpdate(userId, user, { new: true }).select("-password").select("-profileImage");
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
checkSuperadmin,
|
||||
insertUser,
|
||||
getUserByEmail,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
deleteTeam,
|
||||
deleteAllOtherUsers,
|
||||
getAllUsers,
|
||||
logoutUser,
|
||||
getUserById,
|
||||
editUserById,
|
||||
};
|
||||
export default UserModule;
|
||||
@@ -1,6 +1,6 @@
|
||||
import Monitor from "../../models/Monitor.js";
|
||||
import Check from "../../models/Check.js";
|
||||
import logger from "../../../utils/logger.js";
|
||||
import { logger } from "../../../utils/logger.js";
|
||||
|
||||
const generateRandomUrl = () => {
|
||||
const domains = ["example.com", "test.org", "demo.net", "sample.io", "mock.dev"];
|
||||
|
||||
Reference in new issue
Block a user