diff --git a/.gitignore b/.gitignore
index ebf2039ec..a0420fec9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.vite
.vscode
*.sh
-!uptime.sh
\ No newline at end of file
+!uptime.sh
+*.idea
\ No newline at end of file
diff --git a/Client/src/Pages/AdvancedSettings/index.jsx b/Client/src/Pages/AdvancedSettings/index.jsx
index aa13db0e7..6af21cc31 100644
--- a/Client/src/Pages/AdvancedSettings/index.jsx
+++ b/Client/src/Pages/AdvancedSettings/index.jsx
@@ -110,9 +110,9 @@ const AdvancedSettings = ({ isAdmin }) => {
>
- Client Settings
+ Client settings
- Modify client settings here
+ Modify client settings here.
@@ -124,7 +124,7 @@ const AdvancedSettings = ({ isAdmin }) => {
/>
- Email Settings
+ Email settings
Set your host email settings here. These settings are used for
- sending system emails
+ sending system emails.
{
{
{
{
- Server Settings
+ Server settings
- Modify server settings here
+ Modify server settings here.
{
{
{
{
{
>
- General Settings
+ General settings
Display timezone- The
timezone of the dashboard you publicly display.
@@ -218,7 +218,7 @@ const Settings = ({ isAdmin }) => {
{isAdmin && (
- Demo Monitors
+ Demo monitors
Here you can add and remove demo monitors
@@ -254,7 +254,7 @@ const Settings = ({ isAdmin }) => {
{isAdmin && (
- Advanced Settings
+ Advanced settings
Click here to modify advanced settings
diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js
index 7e2a56209..a8d2d77c9 100644
--- a/Client/src/Utils/NetworkService.js
+++ b/Client/src/Utils/NetworkService.js
@@ -1,6 +1,6 @@
import axios from "axios";
-const BASE_URL =
- import.meta.env.VITE_APP_API_BASE_URL || "http://localhost:5000/api/v1";
+const DEFAULT_API_URL = "http://localhost:5000/api/v1";
+const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
import { logger } from "./Logger";
class NetworkService {
constructor(store) {
@@ -10,8 +10,10 @@ class NetworkService {
this.setBaseUrl(baseURL);
this.unsubscribe = store.subscribe(() => {
const state = store.getState();
- if (state.settings.apiBaseUrl) {
+ if (BASE_URL === "undefined" && state.settings.apiBaseUrl) {
baseURL = state.settings.apiBaseUrl;
+ } else {
+ baseURL = DEFAULT_URL;
}
this.setBaseUrl(baseURL);
});
diff --git a/Docker/.gitignore b/Docker/.gitignore
index 3035bcf7a..9b501e10d 100644
--- a/Docker/.gitignore
+++ b/Docker/.gitignore
@@ -1,7 +1,11 @@
*.sh
!quickstart.sh
!build_images.sh
-mongo/data/*
-redis/data/*
+dev/mongo/data/*
+dev/redis/data/*
+dist/mongo/data/*
+dist/redis/data/*
+prod/mongo/data/*
+prod/redis/data/*
*.env
-certbot/*
\ No newline at end of file
+prod/certbot/*
\ No newline at end of file
diff --git a/Docker/client.Dockerfile b/Docker/client.Dockerfile
deleted file mode 100644
index 0f7935cd1..000000000
--- a/Docker/client.Dockerfile
+++ /dev/null
@@ -1,22 +0,0 @@
-FROM node:20-alpine as build
-
-WORKDIR /app
-
-COPY ./Client/package*.json ./
-
-RUN npm install
-
-COPY ./Client .
-
-RUN npm run build
-
-# RUN npm install -g serve
-
-# CMD ["serve","-s", "dist", "-l", "5173"]
-
-FROM nginx:1.27.1-alpine
-
-# COPY ./Docker/nginx/default.conf /etc/nginx/conf.d/default.conf
-COPY --from=build /app/dist /usr/share/nginx/html
-
-CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/Docker/build_images.sh b/Docker/dev/build_images.sh
similarity index 65%
rename from Docker/build_images.sh
rename to Docker/dev/build_images.sh
index 72540168c..2de827dfe 100755
--- a/Docker/build_images.sh
+++ b/Docker/dev/build_images.sh
@@ -2,19 +2,19 @@
#!/bin/bash
# Change directory to root Server directory for correct Docker Context
-cd ..
+cd ../..
#Client
-client="./Docker/client.Dockerfile"
+client="./Docker/dev/client.Dockerfile"
# MongoDB
-mongoDB="./Docker/mongoDB.Dockerfile"
+mongoDB="./Docker/dev/mongoDB.Dockerfile"
# Redis
-redis="./Docker/redis.Dockerfile"
+redis="./Docker/dev/redis.Dockerfile"
# Server
-server="./Docker/server.Dockerfile"
+server="./Docker/dev/server.Dockerfile"
docker build -f $client -t uptime_client .
docker build -f $mongoDB -t uptime_database_mongo .
diff --git a/Docker/dev/client.Dockerfile b/Docker/dev/client.Dockerfile
new file mode 100644
index 000000000..a650e0f72
--- /dev/null
+++ b/Docker/dev/client.Dockerfile
@@ -0,0 +1,15 @@
+FROM node:20-alpine AS build
+
+WORKDIR /app
+
+COPY ../../Client/package*.json ./
+
+RUN npm install
+
+COPY ../../Client .
+
+RUN npm run build
+
+RUN npm install -g serve
+
+CMD ["serve","-s", "dist", "-l", "5173"]
diff --git a/Docker/dev/docker-compose.yaml b/Docker/dev/docker-compose.yaml
new file mode 100644
index 000000000..5cae3f5f3
--- /dev/null
+++ b/Docker/dev/docker-compose.yaml
@@ -0,0 +1,30 @@
+services:
+ client:
+ image: uptime_client:latest
+ ports:
+ - "5173:5173"
+
+ depends_on:
+ - server
+ server:
+ image: uptime_server:latest
+ ports:
+ - "5000:5000"
+ env_file:
+ - server.env
+ depends_on:
+ - redis
+ - mongodb
+ redis:
+ image: uptime_redis:latest
+ ports:
+ - "6379:6379"
+ volumes:
+ - ./redis/data:/data
+ mongodb:
+ image: uptime_database_mongo:latest
+ command: ["mongod", "--quiet"]
+ ports:
+ - "27017:27017"
+ volumes:
+ - ./mongo/data:/data/db
diff --git a/Docker/dev/mongoDB.Dockerfile b/Docker/dev/mongoDB.Dockerfile
new file mode 100644
index 000000000..969a320c2
--- /dev/null
+++ b/Docker/dev/mongoDB.Dockerfile
@@ -0,0 +1,3 @@
+FROM mongo
+EXPOSE 27017
+CMD ["mongod"]
diff --git a/Docker/redis.Dockerfile b/Docker/dev/redis.Dockerfile
similarity index 100%
rename from Docker/redis.Dockerfile
rename to Docker/dev/redis.Dockerfile
diff --git a/Docker/dev/server.Dockerfile b/Docker/dev/server.Dockerfile
new file mode 100644
index 000000000..3a9ed5907
--- /dev/null
+++ b/Docker/dev/server.Dockerfile
@@ -0,0 +1,13 @@
+FROM node:20-alpine
+
+WORKDIR /app
+
+COPY ../../Server/package*.json ./
+
+RUN npm install
+
+COPY ../../Server/ ./
+
+EXPOSE 5000
+
+CMD ["node", "index.js"]
\ No newline at end of file
diff --git a/Docker/dist/client.Dockerfile b/Docker/dist/client.Dockerfile
index 46ce12f3c..1edbfa522 100644
--- a/Docker/dist/client.Dockerfile
+++ b/Docker/dist/client.Dockerfile
@@ -1,19 +1,19 @@
-FROM node:20-alpine as build
+FROM node:20-alpine AS build
WORKDIR /app
-COPY ./Client/package*.json ./
+COPY ../../Client/package*.json ./
RUN npm install
-COPY ./Client .
+COPY ../../Client .
RUN npm run build
FROM nginx:1.27.1-alpine
-COPY ./Docker/nginx/conf.d/dist_default.conf /etc/nginx/conf.d/default.conf
+COPY ./Docker/dist/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/Docker/nginx/conf.d/dist_default.conf b/Docker/dist/nginx/conf.d/default.conf
similarity index 100%
rename from Docker/nginx/conf.d/dist_default.conf
rename to Docker/dist/nginx/conf.d/default.conf
diff --git a/Docker/dist/server.Dockerfile b/Docker/dist/server.Dockerfile
index 5d4ea7364..3a9ed5907 100644
--- a/Docker/dist/server.Dockerfile
+++ b/Docker/dist/server.Dockerfile
@@ -2,11 +2,11 @@ FROM node:20-alpine
WORKDIR /app
-COPY ./Server/package*.json ./
+COPY ../../Server/package*.json ./
RUN npm install
-COPY ./Server/ ./
+COPY ../../Server/ ./
EXPOSE 5000
diff --git a/Docker/mongoDB.Dockerfile b/Docker/mongoDB.Dockerfile
deleted file mode 100644
index 19fc6e358..000000000
--- a/Docker/mongoDB.Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM mongo
-COPY ./Docker/mongo/init/create_users.js /docker-entrypoint-initdb.d/
-EXPOSE 27017
-CMD ["mongod"]
diff --git a/Docker/prod/build_images.sh b/Docker/prod/build_images.sh
new file mode 100755
index 000000000..98d77e660
--- /dev/null
+++ b/Docker/prod/build_images.sh
@@ -0,0 +1,24 @@
+
+#!/bin/bash
+
+# Change directory to root directory for correct Docker Context
+cd ../..
+
+#Client
+client="./Docker/prod/client.Dockerfile"
+
+# MongoDB
+mongoDB="./Docker/prod/mongoDB.Dockerfile"
+
+# Redis
+redis="./Docker/prod/redis.Dockerfile"
+
+# Server
+server="./Docker/prod/server.Dockerfile"
+
+docker build -f $client -t uptime_client .
+docker build -f $mongoDB -t uptime_database_mongo .
+docker build -f $redis -t uptime_redis .
+docker build -f $server -t uptime_server .
+
+echo "All images built"
\ No newline at end of file
diff --git a/Docker/prod/client.Dockerfile b/Docker/prod/client.Dockerfile
new file mode 100644
index 000000000..cbdb75aa8
--- /dev/null
+++ b/Docker/prod/client.Dockerfile
@@ -0,0 +1,17 @@
+FROM node:20-alpine AS build
+
+WORKDIR /app
+
+COPY ../../Client/package*.json ./
+
+RUN npm install
+
+COPY ../../Client .
+
+RUN npm run build
+
+FROM nginx:1.27.1-alpine
+
+COPY --from=build /app/dist /usr/share/nginx/html
+
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/Docker/docker-compose.yaml b/Docker/prod/docker-compose.yaml
similarity index 100%
rename from Docker/docker-compose.yaml
rename to Docker/prod/docker-compose.yaml
diff --git a/Docker/mongo/init/create_users.js b/Docker/prod/mongo/init/create_users.js
similarity index 100%
rename from Docker/mongo/init/create_users.js
rename to Docker/prod/mongo/init/create_users.js
diff --git a/Docker/prod/mongoDB.Dockerfile b/Docker/prod/mongoDB.Dockerfile
new file mode 100644
index 000000000..80c8b9c8f
--- /dev/null
+++ b/Docker/prod/mongoDB.Dockerfile
@@ -0,0 +1,4 @@
+FROM mongo
+COPY ./Docker/prod/mongo/init/create_users.js /docker-entrypoint-initdb.d/
+EXPOSE 27017
+CMD ["mongod"]
diff --git a/Docker/nginx/conf.d/default.conf b/Docker/prod/nginx/conf.d/default.conf
similarity index 100%
rename from Docker/nginx/conf.d/default.conf
rename to Docker/prod/nginx/conf.d/default.conf
diff --git a/Docker/prod/redis.Dockerfile b/Docker/prod/redis.Dockerfile
new file mode 100644
index 000000000..af68ec61e
--- /dev/null
+++ b/Docker/prod/redis.Dockerfile
@@ -0,0 +1,2 @@
+FROM redis
+EXPOSE 6379
\ No newline at end of file
diff --git a/Docker/server.Dockerfile b/Docker/prod/server.Dockerfile
similarity index 100%
rename from Docker/server.Dockerfile
rename to Docker/prod/server.Dockerfile
diff --git a/README.md b/README.md
index 7a85e8d0e..2eebd3a21 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,10 @@ BlueWave Uptime is an open source server monitoring application used to track th
See [BlueWave Uptime](https://uptime-demo.bluewavelabs.ca/) in action. The username is uptimedemo@demo.com and the password is Demouser1!
+## Questions & Ideas
+
+We've just launched our [Discussions](https://github.com/bluewave-labs/bluewave-uptime/discussions) page! Feel free to ask questions or share your ideas—we'd love to hear from you!
+
## Features
- [x] Completely open source, deployable on your servers
diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js
index 8d41f916b..f3ad6fba4 100644
--- a/Server/controllers/authController.js
+++ b/Server/controllers/authController.js
@@ -1,459 +1,371 @@
-const express = require("express");
const {
- registrationBodyValidation,
- loginValidation,
- editUserParamValidation,
- editUserBodyValidation,
- recoveryValidation,
- recoveryTokenValidation,
- newPasswordValidation,
- deleteUserParamValidation,
- inviteRoleValidation,
- inviteBodyValidation,
- inviteVerifciationBodyValidation,
+ registrationBodyValidation,
+ loginValidation,
+ editUserParamValidation,
+ editUserBodyValidation,
+ recoveryValidation,
+ recoveryTokenValidation,
+ newPasswordValidation,
} = require("../validation/joi");
const logger = require("../utils/logger");
require("dotenv").config();
-const { errorMessages, successMessages } = require("../utils/messages");
-var jwt = require("jsonwebtoken");
+const {errorMessages, successMessages} = require("../utils/messages");
+const jwt = require("jsonwebtoken");
const SERVICE_NAME = "AuthController";
-const { getTokenFromHeaders } = require("../utils/utils");
+const {getTokenFromHeaders} = require("../utils/utils");
const crypto = require("crypto");
/**
* Creates and returns JWT token with an arbitrary payload
* @function
* @param {Object} payload
+ * @param {Object} appSettings
* @returns {String}
+ * @throws {Error}
*/
const issueToken = (payload, appSettings) => {
- try {
- const tokenTTL = appSettings.jwtTTL ? appSettings.jwtTTL : "2h";
- return jwt.sign(payload, appSettings.jwtSecret, { expiresIn: tokenTTL });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "issueToken") : null;
- next(error);
- }
-};
-
-/**
- * @function
- * @param {express.Request} req
- * @param {express.Response} res
- * @returns {{success: Boolean, msg: String}}
- */
-const registerController = async (req, res, next) => {
- // joi validation
- try {
- await registrationBodyValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
- // Create a new user
- try {
- const { inviteToken } = req.body;
- // If superAdmin exists, a token should be attached to all further register requests
- const superAdminExists = await req.db.checkSuperadmin(req, res);
- if (superAdminExists) {
- await req.db.getInviteTokenAndDelete(inviteToken);
- } else {
- // This is the first account, create JWT secret to use if one is not supplied by env
- const jwtSecret = crypto.randomBytes(64).toString("hex");
- await req.db.updateAppSettings({ jwtSecret });
- }
-
- const newUser = await req.db.insertUser({ ...req.body }, req.file);
- logger.info(successMessages.AUTH_CREATE_USER, {
- service: SERVICE_NAME,
- userId: newUser._id,
- });
-
- const userForToken = { ...newUser._doc };
- delete userForToken.profileImage;
- delete userForToken.avatarImage;
-
- const appSettings = await req.settingsService.getSettings();
- const token = issueToken(userForToken, appSettings);
- req.emailService
- .buildAndSendEmail(
- "welcomeEmailTemplate",
- { name: newUser.firstName },
- newUser.email,
- "Welcome to Uptime Monitor"
- )
- .catch((error) => {
- logger.error("Error sending welcome email", {
- service: SERVICE_NAME,
- error: error.message,
- });
- });
-
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_CREATE_USER,
- data: { user: newUser, token: token },
- });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "registerController") : null;
- next(error);
- }
-};
-
-/**
- * Returns JWT with User info
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- */
-const loginController = async (req, res, next) => {
- try {
- await loginValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
- try {
- const { email, password } = req.body;
- // Check if user exists
- const user = await req.db.getUserByEmail(email);
-
- // Compare password
- const match = await user.comparePassword(password);
- if (match !== true) {
- throw new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
- }
-
- // Remove password from user object. Should this be abstracted to DB layer?
- const userWithoutPassword = { ...user._doc };
- delete userWithoutPassword.password;
- delete userWithoutPassword.avatarImage;
-
- // Happy path, return token
- const appSettings = req.settingsService.getSettings();
- const token = issueToken(userWithoutPassword, appSettings);
- // reset avatar image
- userWithoutPassword.avatarImage = user.avatarImage;
-
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_LOGIN_USER,
- data: { user: userWithoutPassword, token: token },
- });
- } catch (error) {
- error.status = 500;
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "loginController") : null;
- next(error);
- }
-};
-
-const userEditController = async (req, res, next) => {
- try {
- await editUserParamValidation.validateAsync(req.params);
- await editUserBodyValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
-
- // TODO is this neccessary any longer? Verify ownership middleware should handle this
- if (req.params.userId !== req.user._id.toString()) {
- const error = new Error(errorMessages.AUTH_UNAUTHORIZED);
- error.status = 401;
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
-
- try {
- // Change Password check
- if (req.body.password && req.body.newPassword) {
- const newPassword = req.body.newPassword;
- const password = req.body.password;
-
- // Compare password
- // Get token from headers
- const token = getTokenFromHeaders(req.headers);
- // Get email from token
- const { jwtSecret } = req.settingsService.getSettings();
- const { email } = jwt.verify(token, jwtSecret);
- // Add user email to body for DB operation
- req.body.email = email;
- // Get user
- const user = await req.db.getUserByEmail(email);
- // Compare passwords
- const match = await user.comparePassword(req.body.password);
- // If not a match, throw a 403
- if (!match) {
- const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
- error.status = 403;
- throw error;
- }
- // If a match, update the password
- req.body.password = req.body.newPassword;
- }
-
- const updatedUser = await req.db.updateUser(req, res);
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_UPDATE_USER,
- data: updatedUser,
- });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "userEditController") : null;
- next(error);
- return;
- }
-};
-
-const inviteController = async (req, res, next) => {
- try {
- // Only admins can invite
- const token = getTokenFromHeaders(req.headers);
- const { role, firstname, teamId } = jwt.decode(token);
- req.body.teamId = teamId;
try {
- await inviteRoleValidation.validateAsync({ roles: role });
- await inviteBodyValidation.validateAsync(req.body);
+ const tokenTTL = appSettings.jwtTTL ? appSettings.jwtTTL : "2h";
+ return jwt.sign(payload, appSettings.jwtSecret, {expiresIn: tokenTTL});
} catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "issueToken") : null;
+ throw error;
}
+};
- const inviteToken = await req.db.requestInviteToken(req, res);
- const { clientHost } = req.settingsService.getSettings();
- req.emailService
- .buildAndSendEmail(
- "employeeActivationTemplate",
- {
- name: firstname,
- link: `${clientHost}/register/${inviteToken.token}`,
- },
- req.body.email,
- "Welcome to Uptime Monitor"
- )
- .catch((error) => {
- logger.error("Error sending invite email", {
- service: SERVICE_NAME,
- error: error.message,
+/**
+ * Registers a new user. If the user is the first account, a JWT secret is created. If not, an invite token is required.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.inviteToken - The invite token for registration.
+ * @property {Object} req.file - The file object for the user's profile image.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the creation of the user, the created user data, and a JWT token.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
+ */
+const registerUser = async (req, res, next) => {
+ // joi validation
+ try {
+ await registrationBodyValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message = error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
+ // Create a new user
+ try {
+ const {inviteToken} = req.body;
+ // If superAdmin exists, a token should be attached to all further register requests
+ const superAdminExists = await req.db.checkSuperadmin(req, res);
+ if (superAdminExists) {
+ await req.db.getInviteTokenAndDelete(inviteToken);
+ } else {
+ // This is the first account, create JWT secret to use if one is not supplied by env
+ const jwtSecret = crypto.randomBytes(64).toString("hex");
+ await req.db.updateAppSettings({jwtSecret});
+ }
+
+ const newUser = await req.db.insertUser({...req.body}, req.file);
+ logger.info(successMessages.AUTH_CREATE_USER, {
+ service: SERVICE_NAME, userId: newUser._id,
});
- });
- return res
- .status(200)
- .json({ success: true, msg: "Invite sent", data: inviteToken });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "inviteController") : null;
- next(error);
- return;
- }
-};
+ const userForToken = {...newUser._doc};
+ delete userForToken.profileImage;
+ delete userForToken.avatarImage;
-const inviteVerifyController = async (req, res, next) => {
- try {
- await inviteVerifciationBodyValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
+ const appSettings = await req.settingsService.getSettings();
+ const token = issueToken(userForToken, appSettings);
+ req.emailService
+ .buildAndSendEmail("welcomeEmailTemplate", {name: newUser.firstName}, newUser.email, "Welcome to Uptime Monitor")
+ .catch((error) => {
+ logger.error("Error sending welcome email", {
+ service: SERVICE_NAME, error: error.message,
+ });
+ });
- try {
- const invite = await req.db.getInviteToken(req, res);
- res
- .status(200)
- .json({ status: "success", msg: "Invite verified", data: invite });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "inviteVerifyController")
- : null;
- next(error);
- return;
- }
-};
-
-/**
- * Checks to see if an admin account exists
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- */
-
-const checkSuperadminController = async (req, res) => {
- try {
- const superAdminExists = await req.db.checkSuperadmin(req, res);
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_ADMIN_EXISTS,
- data: superAdminExists,
- });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "checkSuperadminController")
- : null;
- next(error);
- return;
- }
-};
-
-/**
- * Returns a recovery token
- * @async
- * @param {Express.Request} req
- * @property {Object} req.body
- * @property {string} req.body.email
- * @property {EmailService} req.body.emailService
- * @returns {Promise}
- */
-const recoveryRequestController = async (req, res, next) => {
- try {
- await recoveryValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
-
- try {
- const { email } = req.body;
- const user = await req.db.getUserByEmail(email);
- if (user) {
- const recoveryToken = await req.db.requestRecoveryToken(req, res);
- const name = user.firstName;
- const email = req.body.email;
- const { clientHost } = req.settingsService.getSettings();
- const url = `${clientHost}/set-new-password/${recoveryToken.token}`;
-
- const msgId = await req.emailService.buildAndSendEmail(
- "passwordResetTemplate",
- { name, email, url },
- email,
- "Bluewaves Uptime Password Resest"
- );
-
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN,
- data: msgId,
- });
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_CREATE_USER, data: {user: newUser, token: token},
+ });
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "registerController") : null;
+ next(error);
}
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "recoveryRequestController")
- : null;
- next(error);
- return;
- }
};
/**
- * Returns a recovery token
+ * Logs in a user by validating the user's credentials and issuing a JWT token.
* @async
- * @param {Express.Request} req
- * @property {Object} req.body
- * @property {string} req.body.token
- * @param {Express.Response} res
- * @returns {Promise}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.email - The email of the user.
+ * @property {string} req.body.password - The password of the user.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the login of the user, the user data (without password and avatar image), and a JWT token.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422) or the password is incorrect.
*/
-const validateRecoveryTokenController = async (req, res, next) => {
- try {
- await recoveryTokenValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
+const loginUser = async (req, res, next) => {
+ try {
+ await loginValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message = error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
+ try {
+ const {email, password} = req.body;
+ // Check if user exists
+ const user = await req.db.getUserByEmail(email);
- try {
- await req.db.validateRecoveryToken(req, res);
- // TODO Redirect user to reset password after validating token
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
- });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "validateRecoveryTokenController")
- : null;
- next(error);
- return;
- }
+ // Compare password
+ const match = await user.comparePassword(password);
+ if (match !== true) {
+ next(new Error(errorMessages.AUTH_INCORRECT_PASSWORD));
+ return;
+ }
+
+ // Remove password from user object. Should this be abstracted to DB layer?
+ const userWithoutPassword = {...user._doc};
+ delete userWithoutPassword.password;
+ delete userWithoutPassword.avatarImage;
+
+ // Happy path, return token
+ const appSettings = req.settingsService.getSettings();
+ const token = issueToken(userWithoutPassword, appSettings);
+ // reset avatar image
+ userWithoutPassword.avatarImage = user.avatarImage;
+
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_LOGIN_USER, data: {user: userWithoutPassword, token: token},
+ });
+ } catch (error) {
+ error.status = 500;
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "loginController") : null;
+ next(error);
+ }
};
/**
- * Returns an updated user
+ * Edits a user's information. If the user wants to change their password, the current password is checked before updating to the new password.
* @async
- * @param {Express.Request} req
- * @property {Object} req.body
- * @property {string} req.body.token
- * @property {string} req.body.password
- * @param {Express.Response} res
- * @returns {Promise}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.userId - The ID of the user to be edited.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.password - The current password of the user.
+ * @property {string} req.body.newPassword - The new password of the user.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the update of the user, and the updated user data.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403).
*/
-const resetPasswordController = async (req, res, next) => {
- try {
- await newPasswordValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
- try {
- const user = await req.db.resetPassword(req, res);
+const editUser = async (req, res, next) => {
+ try {
+ await editUserParamValidation.validateAsync(req.params);
+ await editUserBodyValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message = error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
- const appSettings = await req.settingsService.getSettings();
- const token = issueToken(user._doc, appSettings);
- res.status(200).json({
- success: true,
- msg: successMessages.AUTH_RESET_PASSWORD,
- data: { user, token },
- });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "resetPasswordController")
- : null;
- next(error);
- }
+ // TODO is this neccessary any longer? Verify ownership middleware should handle this
+ if (req.params.userId !== req.user._id.toString()) {
+ const error = new Error(errorMessages.AUTH_UNAUTHORIZED);
+ error.status = 401;
+ error.service = SERVICE_NAME;
+ next(error);
+ return;
+ }
+
+ try {
+ // Change Password check
+ if (req.body.password && req.body.newPassword) {
+ // Get token from headers
+ const token = getTokenFromHeaders(req.headers);
+ // Get email from token
+ const {jwtSecret} = req.settingsService.getSettings();
+ const {email} = jwt.verify(token, jwtSecret);
+ // Add user email to body for DB operation
+ req.body.email = email;
+ // Get user
+ const user = await req.db.getUserByEmail(email);
+ // Compare passwords
+ const match = await user.comparePassword(req.body.password);
+ // If not a match, throw a 403
+ if (!match) {
+ const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
+ error.status = 403;
+ next(error)
+ return;
+ }
+ // If a match, update the password
+ req.body.password = req.body.newPassword;
+ }
+
+ const updatedUser = await req.db.updateUser(req, res);
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_UPDATE_USER, data: updatedUser,
+ });
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "userEditController") : null;
+ next(error);
+ }
+};
+
+/**
+ * Checks if a superadmin account exists in the database.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the existence of a superadmin, and a boolean indicating the existence of a superadmin.
+ * @throws {Error} If there is an error during the process.
+ */
+const checkSuperAdminExists = async (req, res, next) => {
+ try {
+ const superAdminExists = await req.db.checkSuperadmin(req, res);
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_ADMIN_EXISTS, data: superAdminExists,
+ });
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "checkSuperadminController") : null;
+ next(error);
+ }
+};
+
+/**
+ * Requests a recovery token for a user. The user's email is validated and a recovery token is created and sent via email.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.email - The email of the user requesting recovery.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the creation of the recovery token, and the message ID of the sent email.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
+ */
+const requestRecovery = async (req, res, next) => {
+ try {
+ await recoveryValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message = error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
+
+ try {
+ const {email} = req.body;
+ const user = await req.db.getUserByEmail(email);
+ if (user) {
+ const recoveryToken = await req.db.requestRecoveryToken(req, res);
+ const name = user.firstName;
+ const email = req.body.email;
+ const {clientHost} = req.settingsService.getSettings();
+ const url = `${clientHost}/set-new-password/${recoveryToken.token}`;
+
+ const msgId = await req.emailService.buildAndSendEmail("passwordResetTemplate", {
+ name,
+ email,
+ url
+ }, email, "Bluewave Uptime Password Reset");
+
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, data: msgId,
+ });
+ }
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "recoveryRequestController") : null;
+ next(error);
+ }
+};
+
+/**
+ * Validates a recovery token. The recovery token is validated and if valid, a success message is returned.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.token - The recovery token to be validated.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status and a message indicating the validation of the recovery token.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
+ */
+const validateRecovery = async (req, res, next) => {
+ try {
+ await recoveryTokenValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message = error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
+
+ try {
+ await req.db.validateRecoveryToken(req, res);
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
+ });
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "validateRecoveryTokenController") : null;
+ next(error);
+ }
+};
+
+/**
+ * Resets a user's password. The new password is validated and if valid, the user's password is updated in the database and a new JWT token is issued.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.token - The recovery token.
+ * @property {string} req.body.password - The new password of the user.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the reset of the password, the updated user data (without password and avatar image), and a new JWT token.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
+ */
+const resetPassword = async (req, res, next) => {
+ try {
+ await newPasswordValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message = error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
+ try {
+ const user = await req.db.resetPassword(req, res);
+
+ const appSettings = await req.settingsService.getSettings();
+ const token = issueToken(user._doc, appSettings);
+ res.status(200).json({
+ success: true, msg: successMessages.AUTH_RESET_PASSWORD, data: {user, token},
+ });
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "resetPasswordController") : null;
+ next(error);
+ }
};
/**
@@ -465,81 +377,74 @@ const resetPasswordController = async (req, res, next) => {
* @returns {Object} The response object with success status and message.
* @throws {Error} If user validation fails or user is not found in the database.
*/
-const deleteUserController = async (req, res, next) => {
- try {
- const token = getTokenFromHeaders(req.headers);
- const decodedToken = jwt.decode(token);
- const { _id, email } = decodedToken;
+const deleteUser = async (req, res, next) => {
+ try {
+ const token = getTokenFromHeaders(req.headers);
+ const decodedToken = jwt.decode(token);
+ const {email} = decodedToken;
- // Check if the user exists
- const user = await req.db.getUserByEmail(email);
- if (!user) {
- throw new Error(errorMessages.DB_USER_NOT_FOUND);
+ // Check if the user exists
+ const user = await req.db.getUserByEmail(email);
+ if (!user) {
+ next(new Error(errorMessages.DB_USER_NOT_FOUND));
+ return;
+ }
+
+ // 1. Find all the monitors associated with the team ID if superadmin
+
+ const result = await req.db.getMonitorsByTeamId({
+ params: {teamId: user.teamId},
+ });
+ if (user.role.includes("superadmin")) {
+ //2. Remove all jobs, delete checks and alerts
+ result?.monitors.length > 0 && (await Promise.all(result.monitors.map(async (monitor) => {
+ await req.jobQueue.deleteJob(monitor);
+ await req.db.deleteChecks(monitor._id);
+ await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
+ await req.db.deleteNotificationsByMonitorId(monitor._id);
+ })));
+
+ // 3. Delete team
+ await req.db.deleteTeam(user.teamId);
+ // 4. Delete all other team members
+ await req.db.deleteAllOtherUsers();
+ // 5. Delete each monitor
+ await req.db.deleteMonitorsByUserId(user._id);
+ }
+ // 6. Delete the user by id
+ await req.db.deleteUser(user._id);
+
+ return res.status(200).json({
+ success: true, msg: successMessages.AUTH_DELETE_USER,
+ });
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "deleteUserController") : null;
+ next(error);
}
-
- // 1. Find all the monitors associated with the team ID if superadmin
-
- const result = await req.db.getMonitorsByTeamId({
- params: { teamId: user.teamId },
- });
- if (user.role.includes("superadmin")) {
- //2. Remove all jobs, delete checks and alerts
- result?.monitors.length > 0 &&
- (await Promise.all(
- result.monitors.map(async (monitor) => {
- await req.jobQueue.deleteJob(monitor);
- await req.db.deleteChecks(monitor._id);
- await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
- await req.db.deleteNotificationsByMonitorId(monitor._id);
- })
- ));
-
- // 3. Delete team
- await req.db.deleteTeam(user.teamId);
- // 4. Delete all other team members
- await req.db.deleteAllOtherUsers();
- // 5. Delete each monitor
- await req.db.deleteMonitorsByUserId(user._id);
- }
- // 6. Delete the user by id
- await req.db.deleteUser(user._id);
-
- return res.status(200).json({
- success: true,
- msg: successMessages.AUTH_DELETE_USER,
- });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "deleteUserController") : null;
- next(error);
- }
};
-const getAllUsersController = async (req, res) => {
- try {
- const allUsers = await req.db.getAllUsers(req, res);
- res
- .status(200)
- .json({ success: true, msg: "Got all users", data: allUsers });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "getAllUsersController")
- : null;
- next(error);
- }
+const getAllUsers = async (req, res) => {
+ try {
+ const allUsers = await req.db.getAllUsers(req, res);
+ res
+ .status(200)
+ .json({success: true, msg: "Got all users", data: allUsers});
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "getAllUsersController") : null;
+ next(error);
+ }
};
module.exports = {
- registerController,
- loginController,
- userEditController,
- inviteController,
- inviteVerifyController,
- checkSuperadminController,
- recoveryRequestController,
- validateRecoveryTokenController,
- resetPasswordController,
- deleteUserController,
- getAllUsersController,
+ registerUser,
+ loginUser,
+ editUser,
+ checkSuperadminExists: checkSuperAdminExists,
+ requestRecovery,
+ validateRecovery,
+ resetPassword,
+ deleteUser,
+ getAllUsers,
};
diff --git a/Server/controllers/inviteController.js b/Server/controllers/inviteController.js
index da9b2496a..9863f9666 100644
--- a/Server/controllers/inviteController.js
+++ b/Server/controllers/inviteController.js
@@ -1,101 +1,111 @@
const {
- inviteRoleValidation,
- inviteBodyValidation,
- inviteVerifciationBodyValidation,
+ inviteRoleValidation,
+ inviteBodyValidation,
+ inviteVerificationBodyValidation,
} = require("../validation/joi");
const logger = require("../utils/logger");
require("dotenv").config();
-const { errorMessages, successMessages } = require("../utils/messages");
-var jwt = require("jsonwebtoken");
+const jwt = require("jsonwebtoken");
const SERVICE_NAME = "inviteController";
const getTokenFromHeaders = (headers) => {
- const authorizationHeader = headers.authorization;
- if (!authorizationHeader) throw new Error("No auth headers");
+ const authorizationHeader = headers.authorization;
+ if (!authorizationHeader) throw new Error("No auth headers");
- const parts = authorizationHeader.split(" ");
- if (parts.length !== 2 || parts[0] !== "Bearer")
- throw new Error("Invalid auth headers");
+ const parts = authorizationHeader.split(" ");
+ if (parts.length !== 2 || parts[0] !== "Bearer")
+ throw new Error("Invalid auth headers");
- return parts[1];
+ return parts[1];
};
-const inviteController = async (req, res, next) => {
- try {
- // Only admins can invite
- const token = getTokenFromHeaders(req.headers);
- const { role, firstname, teamId } = jwt.decode(token);
- req.body.teamId = teamId;
+/**
+ * Issues an invitation to a new user. Only admins can invite new users. An invitation token is created and sent via email.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.headers - The headers of the request.
+ * @property {string} req.headers.authorization - The authorization header containing the JWT token.
+ * @property {Object} req.body - The body of the request.
+ * @property {string} req.body.email - The email of the user to be invited.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the sending of the invitation, and the invitation token.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
+ */
+const issueInvitation = async (req, res, next) => {
try {
- await inviteRoleValidation.validateAsync({ roles: role });
- await inviteBodyValidation.validateAsync(req.body);
+ // Only admins can invite
+ const token = getTokenFromHeaders(req.headers);
+ const {role, firstname, teamId} = jwt.decode(token);
+ req.body.teamId = teamId;
+ try {
+ await inviteRoleValidation.validateAsync({roles: role});
+ await inviteBodyValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message =
+ error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
+
+ const inviteToken = await req.db.requestInviteToken({...req.body});
+ const {clientHost} = req.settingsService.getSettings();
+ req.emailService
+ .buildAndSendEmail(
+ "employeeActivationTemplate",
+ {
+ name: firstname,
+ link: `${clientHost}/register/${inviteToken.token}`,
+ },
+ req.body.email,
+ "Welcome to Uptime Monitor"
+ )
+ .catch((error) => {
+ logger.error("Error sending invite email", {
+ service: SERVICE_NAME,
+ error: error.message,
+ });
+ });
+
+ return res
+ .status(200)
+ .json({success: true, msg: "Invite sent", data: inviteToken});
} catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined ? (error.method = "inviteController") : null;
+ next(error);
}
-
- const inviteToken = await req.db.requestInviteToken({ ...req.body });
- const { clientHost } = req.settingsService.getSettings();
- req.emailService
- .buildAndSendEmail(
- "employeeActivationTemplate",
- {
- name: firstname,
- link: `${clientHost}/register/${inviteToken.token}`,
- },
- req.body.email,
- "Welcome to Uptime Monitor"
- )
- .catch((error) => {
- logger.error("Error sending invite email", {
- service: SERVICE_NAME,
- error: error.message,
- });
- });
-
- return res
- .status(200)
- .json({ success: true, msg: "Invite sent", data: inviteToken });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "inviteController") : null;
- next(error);
- return;
- }
};
const inviteVerifyController = async (req, res, next) => {
- try {
- await inviteVerifciationBodyValidation.validateAsync(req.body);
- } catch (error) {
- error.status = 422;
- error.service = SERVICE_NAME;
- error.message =
- error.details?.[0]?.message || error.message || "Validation Error";
- next(error);
- return;
- }
+ try {
+ await inviteVerificationBodyValidation.validateAsync(req.body);
+ } catch (error) {
+ error.status = 422;
+ error.service = SERVICE_NAME;
+ error.message =
+ error.details?.[0]?.message || error.message || "Validation Error";
+ next(error);
+ return;
+ }
- try {
- const invite = await req.db.getInviteToken(req.body.token);
- res
- .status(200)
- .json({ status: "success", msg: "Invite verified", data: invite });
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined
- ? (error.method = "inviteVerifyController")
- : null;
- next(error);
- return;
- }
+ try {
+ const invite = await req.db.getInviteToken(req.body.token);
+ res
+ .status(200)
+ .json({status: "success", msg: "Invite verified", data: invite});
+ } catch (error) {
+ error.service === undefined ? (error.service = SERVICE_NAME) : null;
+ error.method === undefined
+ ? (error.method = "inviteVerifyController")
+ : null;
+ next(error);
+ }
};
module.exports = {
- inviteController,
- inviteVerifyController,
+ inviteController: issueInvitation,
+ inviteVerifyController,
};
diff --git a/Server/controllers/maintenanceWindowController.js b/Server/controllers/maintenanceWindowController.js
index 755d4c5bb..775c04728 100644
--- a/Server/controllers/maintenanceWindowController.js
+++ b/Server/controllers/maintenanceWindowController.js
@@ -5,8 +5,9 @@ const {
getMaintenanceWindowsByMonitorIdParamValidation,
} = require("../validation/joi");
+const {successMessages} = require("../utils/messages")
+
const SERVICE_NAME = "maintenanceWindowController";
-const { errorMessages, successMessages } = require("../utils/messages");
const createMaintenanceWindow = async (req, res, next) => {
try {
@@ -67,7 +68,7 @@ const getMaintenanceWindowsByUserId = async (req, res, next) => {
return res.status(201).json({
success: true,
- msg: successMessages.MAINTEANCE_WINDOW_GET_BY_USER,
+ msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER,
data: maintenanceWindows,
});
} catch (error) {
@@ -100,7 +101,7 @@ const getMaintenanceWindowsByMonitorId = async (req, res, next) => {
return res.status(201).json({
success: true,
- msg: successMessages.MAINTEANCE_WINDOW_GET_BY_USER,
+ msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER,
data: maintenanceWindows,
});
} catch (error) {
diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js
index 5cda56634..3f77a412e 100644
--- a/Server/controllers/monitorController.js
+++ b/Server/controllers/monitorController.js
@@ -11,8 +11,6 @@ const {
getMonitorStatsByIdParamValidation,
getMonitorStatsByIdQueryValidation,
getCertificateParamValidation,
- getMonitorAggregateStatsParamValidation,
- getMonitorAggregateStatsQueryValidation,
} = require("../validation/joi");
const sslChecker = require("ssl-checker");
@@ -27,6 +25,7 @@ const logger = require("../utils/logger");
* @async
* @param {Express.Request} req
* @param {Express.Response} res
+ * @param {function} next
* @returns {Promise}
* @throws {Error}
*/
@@ -50,6 +49,7 @@ const getAllMonitors = async (req, res, next) => {
* @async
* @param {Express.Request} req
* @param {Express.Response} res
+ * @param {function} next
* @returns {Promise}
* @throws {Error}
*/
@@ -69,7 +69,7 @@ const getMonitorStatsById = async (req, res, next) => {
const monitorStats = await req.db.getMonitorStatsById(req);
return res.json({
success: true,
- msg: successMessages.MONTIOR_STATS_BY_ID,
+ msg: successMessages.MONITOR_STATS_BY_ID,
data: monitorStats,
});
} catch (error) {
@@ -119,12 +119,15 @@ const getMonitorCertificate = async (req, res, next) => {
};
/**
- * Returns monitor with matching ID
+ * Retrieves a monitor by its ID.
* @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.monitorId - The ID of the monitor to be retrieved.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message, and the retrieved monitor data.
+ * @throws {Error} If there is an error during the process, especially if the monitor is not found (404) or if there is a validation error (422).
*/
const getMonitorById = async (req, res, next) => {
try {
@@ -143,12 +146,11 @@ const getMonitorById = async (req, res, next) => {
if (!monitor) {
const error = new Error(errorMessages.MONITOR_GET_BY_ID);
error.status = 404;
- throw error;
}
return res.json({
success: true,
- msg: successMessages.MONTIOR_GET_BY_ID,
+ msg: successMessages.MONITOR_GET_BY_ID,
data: monitor,
});
} catch (error) {
@@ -159,14 +161,18 @@ const getMonitorById = async (req, res, next) => {
};
/**
- * Returns all monitors and a sumamry for a team with TeamID
+ * Retrieves all monitors and a summary for a team based on the team ID.
* @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.teamId - The ID of the team.
+ * @property {Object} req.query - The query parameters of the request.
+ * @property {string} req.query.type - The type of the request.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message, and the data containing the monitors and summary for the team.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
*/
-
const getMonitorsAndSummaryByTeamId = async (req, res, next) => {
try {
await getMonitorsAndSummaryByTeamIdParamValidation.validateAsync(
@@ -177,8 +183,7 @@ const getMonitorsAndSummaryByTeamId = async (req, res, next) => {
} catch (error) {
error.status = 422;
error.service = SERVICE_NAME;
- error.method === undefined &&
- error.method === "getMonitorsAndSummaryByTeamId";
+ error.method === undefined? error.method = "getMonitorsAndSummaryByTeamId": null;
error.message =
error.details?.[0]?.message || error.message || "Validation Error";
next(error);
@@ -207,12 +212,16 @@ const getMonitorsAndSummaryByTeamId = async (req, res, next) => {
};
/**
- * Returns all monitors belong to team with TeamID
+ * Retrieves all monitors associated with a team by the team's ID.
* @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.teamId - The ID of the team.
+ * @property {Object} req.query - The query parameters of the request.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message, and the data containing the monitors for the team.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
*/
const getMonitorsByTeamId = async (req, res, next) => {
try {
@@ -243,14 +252,16 @@ const getMonitorsByTeamId = async (req, res, next) => {
};
/**
- * Creates a new monitor
+ * Creates a new monitor and adds it to the job queue.
* @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.body - The body of the request.
+ * @property {Array} req.body.notifications - The notifications associated with the monitor.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the creation of the monitor, and the created monitor data.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
*/
-
const createMonitor = async (req, res, next) => {
try {
await createMonitorBodyValidation.validateAsync(req.body);
@@ -269,13 +280,12 @@ const createMonitor = async (req, res, next) => {
const monitor = await req.db.createMonitor(req, res);
if (notifications && notifications.length !== 0) {
- const setNotifications = await Promise.all(
- notifications.map(async (notification) => {
- notification.monitorId = monitor._id;
- await req.db.createNotification(notification);
- })
+ monitor.notifications = await Promise.all(
+ notifications.map(async (notification) => {
+ notification.monitorId = monitor._id;
+ await req.db.createNotification(notification);
+ })
);
- monitor.notifications = setNotifications;
await monitor.save();
}
// Add monitor to job queue
@@ -293,14 +303,16 @@ const createMonitor = async (req, res, next) => {
};
/**
- * Delete a monitor by ID
+ * Deletes a monitor by its ID and also deletes associated checks, alerts, and notifications.
* @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.monitorId - The ID of the monitor to be deleted.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status and a message indicating the deletion of the monitor.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422) or an error in deleting associated records.
*/
-
const deleteMonitor = async (req, res, next) => {
try {
await getMonitorByIdParamValidation.validateAsync(req.params);
@@ -331,7 +343,6 @@ const deleteMonitor = async (req, res, next) => {
}
);
}
-
return res
.status(200)
.json({ success: true, msg: successMessages.MONITOR_DELETE });
@@ -342,7 +353,18 @@ const deleteMonitor = async (req, res, next) => {
}
};
-const deleteAllMonitors = async (req, res) => {
+/**
+ * Deletes all monitors associated with a team.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.headers - The headers of the request.
+ * @property {string} req.headers.authorization - The authorization header containing the JWT token.
+ * @param {Object} res - The Express response object.
+ * @param {function} next
+ * @returns {Object} The response object with a success status and a message indicating the number of deleted monitors.
+ * @throws {Error} If there is an error during the deletion process.
+ */
+const deleteAllMonitors = async (req, res, next) => {
try {
const token = getTokenFromHeaders(req.headers);
const { jwtSecret } = req.settingsService.getSettings();
@@ -367,12 +389,17 @@ const deleteAllMonitors = async (req, res) => {
};
/**
- * Edit a monitor by ID
+ * Edits a monitor by its ID, updates its notifications, and updates its job in the job queue.
* @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.monitorId - The ID of the monitor to be edited.
+ * @property {Object} req.body - The body of the request.
+ * @property {Array} req.body.notifications - The notifications to be associated with the monitor.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the editing of the monitor, and the edited monitor data.
+ * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
*/
const editMonitor = async (req, res, next) => {
try {
@@ -423,6 +450,17 @@ const editMonitor = async (req, res, next) => {
}
};
+/**
+ * Pauses or resumes a monitor based on its current state.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.params - The parameters of the request.
+ * @property {string} req.params.monitorId - The ID of the monitor to be paused or resumed.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the new state of the monitor, and the updated monitor data.
+ * @throws {Error} If there is an error during the process.
+ */
const pauseMonitor = async (req, res, next) => {
try {
await pauseMonitorParamValidation.validateAsync(req.params);
@@ -458,6 +496,17 @@ const pauseMonitor = async (req, res, next) => {
}
};
+/**
+ * Adds demo monitors for a team.
+ * @async
+ * @param {Object} req - The Express request object.
+ * @property {Object} req.headers - The headers of the request.
+ * @property {string} req.headers.authorization - The authorization header containing the JWT token.
+ * @param {Object} res - The Express response object.
+ * @param {function} next - The next middleware function.
+ * @returns {Object} The response object with a success status, a message indicating the addition of demo monitors, and the number of demo monitors added.
+ * @throws {Error} If there is an error during the process.
+ */
const addDemoMonitors = async (req, res, next) => {
try {
const token = getTokenFromHeaders(req.headers);
diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js
index e4f36d001..a7c8bf4ee 100644
--- a/Server/db/FakeDb.js
+++ b/Server/db/FakeDb.js
@@ -20,8 +20,8 @@
// The rest of the code is the same, as all the `db` methods are standardized.
// **************************
-const Monitor = require("../models/Monitor");
-const UserModel = require("../models/User");
+const Monitor = require("./models/Monitor");
+const UserModel = require("./models/User");
const bcrypt = require("bcrypt");
let FAKE_MONITOR_DATA = [];
diff --git a/Server/models/AppSettings.js b/Server/db/models/AppSettings.js
similarity index 100%
rename from Server/models/AppSettings.js
rename to Server/db/models/AppSettings.js
diff --git a/Server/models/Check.js b/Server/db/models/Check.js
similarity index 93%
rename from Server/models/Check.js
rename to Server/db/models/Check.js
index acb20dfb9..7705ba3fd 100644
--- a/Server/models/Check.js
+++ b/Server/db/models/Check.js
@@ -1,6 +1,6 @@
const mongoose = require("mongoose");
-const EmailService = require("../service/emailService");
-const Notification = require("../models/Notification");
+const EmailService = require("../../service/emailService");
+const Notification = require("./Notification");
/**
* Check Schema for MongoDB collection.
diff --git a/Server/models/InviteToken.js b/Server/db/models/InviteToken.js
similarity index 100%
rename from Server/models/InviteToken.js
rename to Server/db/models/InviteToken.js
diff --git a/Server/models/MaintenanceWindow.js b/Server/db/models/MaintenanceWindow.js
similarity index 100%
rename from Server/models/MaintenanceWindow.js
rename to Server/db/models/MaintenanceWindow.js
diff --git a/Server/models/Monitor.js b/Server/db/models/Monitor.js
similarity index 100%
rename from Server/models/Monitor.js
rename to Server/db/models/Monitor.js
diff --git a/Server/models/Notification.js b/Server/db/models/Notification.js
similarity index 100%
rename from Server/models/Notification.js
rename to Server/db/models/Notification.js
diff --git a/Server/models/PageSpeedCheck.js b/Server/db/models/PageSpeedCheck.js
similarity index 100%
rename from Server/models/PageSpeedCheck.js
rename to Server/db/models/PageSpeedCheck.js
diff --git a/Server/models/RecoveryToken.js b/Server/db/models/RecoveryToken.js
similarity index 100%
rename from Server/models/RecoveryToken.js
rename to Server/db/models/RecoveryToken.js
diff --git a/Server/models/Team.js b/Server/db/models/Team.js
similarity index 100%
rename from Server/models/Team.js
rename to Server/db/models/Team.js
diff --git a/Server/models/User.js b/Server/db/models/User.js
similarity index 100%
rename from Server/models/User.js
rename to Server/db/models/User.js
diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js
index 268e62798..aeb993433 100644
--- a/Server/db/mongo/MongoDB.js
+++ b/Server/db/mongo/MongoDB.js
@@ -1,6 +1,6 @@
const mongoose = require("mongoose");
-const UserModel = require("../../models/User");
-const AppSettings = require("../../models/AppSettings");
+const UserModel = require("../models/User");
+const AppSettings = require("../models/AppSettings");
//****************************************
// DB Connection
diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js
index c18491021..193d793c6 100644
--- a/Server/db/mongo/modules/checkModule.js
+++ b/Server/db/mongo/modules/checkModule.js
@@ -1,6 +1,6 @@
-const Check = require("../../../models/Check");
-const Monitor = require("../../../models/Monitor");
-const User = require("../../../models/User");
+const Check = require("../../models/Check");
+const Monitor = require("../../models/Monitor");
+const User = require("../../models/User");
const logger = require("../../../utils/logger");
const SERVICE_NAME = "checkModule";
const dateRangeLookup = {
diff --git a/Server/db/mongo/modules/inviteModule.js b/Server/db/mongo/modules/inviteModule.js
index 9a0d35621..8152941d7 100644
--- a/Server/db/mongo/modules/inviteModule.js
+++ b/Server/db/mongo/modules/inviteModule.js
@@ -1,4 +1,4 @@
-const InviteToken = require("../../../models/InviteToken");
+const InviteToken = require("../../models/InviteToken");
const crypto = require("crypto");
const { errorMessages } = require("../../../utils/messages");
const SERVICE_NAME = "inviteModule";
diff --git a/Server/db/mongo/modules/maintenaceWindowModule.js b/Server/db/mongo/modules/maintenaceWindowModule.js
index bcb11e816..f49c9dca4 100644
--- a/Server/db/mongo/modules/maintenaceWindowModule.js
+++ b/Server/db/mongo/modules/maintenaceWindowModule.js
@@ -1,4 +1,4 @@
-const MaintenanceWindow = require("../../../models/MaintenanceWindow");
+const MaintenanceWindow = require("../../models/MaintenanceWindow");
const SERVICE_NAME = "maintenanceWindowModule";
/**
diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js
index 002bf9c41..bac2e146b 100644
--- a/Server/db/mongo/modules/monitorModule.js
+++ b/Server/db/mongo/modules/monitorModule.js
@@ -1,8 +1,8 @@
-const Monitor = require("../../../models/Monitor");
-const Check = require("../../../models/Check");
-const PageSpeedCheck = require("../../../models/PageSpeedCheck");
+const Monitor = require("../../models/Monitor");
+const Check = require("../../models/Check");
+const PageSpeedCheck = require("../../models/PageSpeedCheck");
const { errorMessages } = require("../../../utils/messages");
-const Notification = require("../../../models/Notification");
+const Notification = require("../../models/Notification");
const { NormalizeData } = require("../../../utils/dataUtils");
const demoMonitors = require("../../../utils/demoMonitors.json");
const SERVICE_NAME = "monitorModule";
@@ -147,7 +147,7 @@ const getMonitorStatsById = async (req) => {
let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query;
const monitor = await Monitor.findById(monitorId);
if (monitor === null || monitor === undefined) {
- throw new Error(errorMessages.DB_FIND_MONTIOR_BY_ID(monitorId));
+ throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
}
// This effectively removes limit, returning all checks
if (limit === undefined) limit = 0;
@@ -283,7 +283,7 @@ const getMonitorById = async (monitorId) => {
try {
const monitor = await Monitor.findById(monitorId);
if (monitor === null || monitor === undefined) {
- throw new Error(errorMessages.DB_FIND_MONTIOR_BY_ID(monitorId));
+ throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
}
// Get notifications
const notifications = await Notification.find({
@@ -481,7 +481,7 @@ const deleteMonitor = async (req, res) => {
try {
const monitor = await Monitor.findByIdAndDelete(monitorId);
if (!monitor) {
- throw new Error(errorMessages.DB_FIND_MONTIOR_BY_ID(monitorId));
+ throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
}
return monitor;
} catch (error) {
diff --git a/Server/db/mongo/modules/notificationModule.js b/Server/db/mongo/modules/notificationModule.js
index 89df2130b..ecc52644d 100644
--- a/Server/db/mongo/modules/notificationModule.js
+++ b/Server/db/mongo/modules/notificationModule.js
@@ -1,4 +1,4 @@
-const Notification = require("../../../models/Notification");
+const Notification = require("../../models/Notification");
const SERVICE_NAME = "notificationModule";
/**
* Creates a new notification.
diff --git a/Server/db/mongo/modules/pageSpeedCheckModule.js b/Server/db/mongo/modules/pageSpeedCheckModule.js
index 8b76555c5..a0c7694c8 100644
--- a/Server/db/mongo/modules/pageSpeedCheckModule.js
+++ b/Server/db/mongo/modules/pageSpeedCheckModule.js
@@ -1,4 +1,4 @@
-const PageSpeedCheck = require("../../../models/PageSpeedCheck");
+const PageSpeedCheck = require("../../models/PageSpeedCheck");
const SERVICE_NAME = "pageSpeedCheckModule";
/**
* Create a PageSpeed check for a monitor
diff --git a/Server/db/mongo/modules/recoveryModule.js b/Server/db/mongo/modules/recoveryModule.js
index 469954cef..4649fa084 100644
--- a/Server/db/mongo/modules/recoveryModule.js
+++ b/Server/db/mongo/modules/recoveryModule.js
@@ -1,5 +1,5 @@
-const UserModel = require("../../../models/User");
-const RecoveryToken = require("../../../models/RecoveryToken");
+const UserModel = require("../../models/User");
+const RecoveryToken = require("../../models/RecoveryToken");
const crypto = require("crypto");
const { errorMessages } = require("../../../utils/messages");
const SERVICE_NAME = "recoveryModule";
diff --git a/Server/db/mongo/modules/settingsModule.js b/Server/db/mongo/modules/settingsModule.js
index b7e6b765d..f7cfc9ce1 100644
--- a/Server/db/mongo/modules/settingsModule.js
+++ b/Server/db/mongo/modules/settingsModule.js
@@ -1,4 +1,4 @@
-const AppSettings = require("../../../models/AppSettings");
+const AppSettings = require("../../models/AppSettings");
const SERVICE_NAME = "SettingsModule";
const getAppSettings = async () => {
diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js
index bfdd54a4c..c72bd7c5d 100644
--- a/Server/db/mongo/modules/userModule.js
+++ b/Server/db/mongo/modules/userModule.js
@@ -1,5 +1,5 @@
-const UserModel = require("../../../models/User");
-const TeamModel = require("../../../models/Team");
+const UserModel = require("../../models/User");
+const TeamModel = require("../../models/Team");
const { errorMessages } = require("../../../utils/messages");
const { GenerateAvatarImage } = require("../../../utils/imageProcessing");
diff --git a/Server/index.js b/Server/index.js
index 05eaeb617..b717482cd 100644
--- a/Server/index.js
+++ b/Server/index.js
@@ -21,7 +21,6 @@ const queueRouter = require("./routes/queueRoute");
const JobQueue = require("./service/jobQueue");
const NetworkService = require("./service/networkService");
const EmailService = require("./service/emailService");
-const PageSpeedService = require("./service/pageSpeedService");
const SettingsService = require("./service/settingsService");
const SERVICE_NAME = "Server";
@@ -73,7 +72,6 @@ const startApp = async () => {
req.db = db;
req.jobQueue = jobQueue;
req.emailService = emailService;
- req.pageSpeedService = pageSpeedService;
req.settingsService = settingsService;
next();
});
@@ -136,7 +134,6 @@ const startApp = async () => {
networkService,
settingsService
);
- const pageSpeedService = new PageSpeedService();
const cleanup = async () => {
if (cleaningUp) {
diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js
index 0007bad39..8e2e88b6f 100644
--- a/Server/middleware/verifyJWT.js
+++ b/Server/middleware/verifyJWT.js
@@ -4,7 +4,7 @@ const SERVICE_NAME = "verifyJWT";
const TOKEN_PREFIX = "Bearer ";
const { errorMessages } = require("../utils/messages");
const { parse } = require("path");
-const User = require("../models/User");
+const User = require("../db/models/User");
/**
* Verifies the JWT token
* @function
diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js
index 362f6a5a2..ce6ec440e 100644
--- a/Server/routes/authRoute.js
+++ b/Server/routes/authRoute.js
@@ -4,47 +4,46 @@ const { verifyOwnership } = require("../middleware/verifyOwnership");
const { isAllowed } = require("../middleware/isAllowed");
const multer = require("multer");
const upload = multer();
-const User = require("../models/User");
+const User = require("../db/models/User");
const {
- registerController,
- loginController,
- logoutController,
- userEditController,
- recoveryRequestController,
- validateRecoveryTokenController,
- resetPasswordController,
- checkSuperadminController,
- getAllUsersController,
- deleteUserController,
+ registerUser,
+ loginUser,
+ editUser,
+ requestRecovery,
+ validateRecovery,
+ resetPassword,
+ checkSuperadminExists,
+ getAllUsers,
+ deleteUser,
} = require("../controllers/authController");
//Auth routes
-router.post("/register", upload.single("profileImage"), registerController);
-router.post("/login", loginController);
+router.post("/register", upload.single("profileImage"), registerUser);
+router.post("/login", loginUser);
router.put(
"/user/:userId",
upload.single("profileImage"),
verifyJWT,
- userEditController
+ editUser
);
-router.get("/users/superadmin", checkSuperadminController);
+router.get("/users/superadmin", checkSuperadminExists);
router.get(
"/users",
verifyJWT,
isAllowed(["admin", "superadmin"]),
- getAllUsersController
+ getAllUsers
);
router.delete(
"/user/:userId",
verifyJWT,
verifyOwnership(User, "userId"),
- deleteUserController
+ deleteUser
);
//Recovery routes
-router.post("/recovery/request", recoveryRequestController);
-router.post("/recovery/validate", validateRecoveryTokenController);
-router.post("/recovery/reset/", resetPasswordController);
+router.post("/recovery/request", requestRecovery);
+router.post("/recovery/validate", validateRecovery);
+router.post("/recovery/reset/", resetPassword);
module.exports = router;
diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js
index fe99e7271..47b48809d 100644
--- a/Server/routes/checkRoute.js
+++ b/Server/routes/checkRoute.js
@@ -2,7 +2,7 @@ const router = require("express").Router();
const checkController = require("../controllers/checkController");
const { verifyOwnership } = require("../middleware/verifyOwnership");
const { isAllowed } = require("../middleware/isAllowed");
-const Monitor = require("../models/Monitor");
+const Monitor = require("../db/models/Monitor");
router.get("/:monitorId", checkController.getChecks);
router.post(
diff --git a/Server/routes/maintenanceWindowRoute.js b/Server/routes/maintenanceWindowRoute.js
index c357df76b..8ed15b609 100644
--- a/Server/routes/maintenanceWindowRoute.js
+++ b/Server/routes/maintenanceWindowRoute.js
@@ -1,7 +1,7 @@
const router = require("express").Router();
const maintenanceWindowController = require("../controllers/maintenanceWindowController");
const { verifyOwnership } = require("../middleware/verifyOwnership");
-const Monitor = require("../models/Monitor");
+const Monitor = require("../db/models/Monitor");
router.get(
"/monitor/:monitorId",
diff --git a/Server/routes/settingsRoute.js b/Server/routes/settingsRoute.js
index 3cbfff2b8..521987aec 100644
--- a/Server/routes/settingsRoute.js
+++ b/Server/routes/settingsRoute.js
@@ -1,7 +1,7 @@
const router = require("express").Router();
const settingsController = require("../controllers/settingsController");
const { isAllowed } = require("../middleware/isAllowed");
-const Monitor = require("../models/Monitor");
+const Monitor = require("../db/models/Monitor");
router.get("/", settingsController.getAppSettings);
router.put(
diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js
index 7566debd8..58d628a2e 100644
--- a/Server/service/jobQueue.js
+++ b/Server/service/jobQueue.js
@@ -5,11 +5,18 @@ const JOBS_PER_WORKER = 5;
const logger = require("../utils/logger");
const { errorMessages, successMessages } = require("../utils/messages");
const SERVICE_NAME = "JobQueue";
-
+/**
+ * JobQueue
+ *
+ * This service is responsible for managing the job queue.
+ * It handles enqueuing, dequeuing, and processing jobs.
+ * It scales the number of workers based on the number of jobs/worker
+ */
class JobQueue {
/**
* Constructs a new JobQueue
* @constructor
+ * @param {SettingsService} settingsService - The settings service
* @throws {Error}
*/
constructor(settingsService) {
@@ -132,7 +139,7 @@ class JobQueue {
return { jobs, load };
} catch (error) {
error.service === undefined ? (error.service = SERVICE_NAME) : null;
- errorObject.method === undefined
+ error.method === undefined
? (error.method = "getWorkerStats")
: null;
throw error;
@@ -278,6 +285,8 @@ class JobQueue {
service: SERVICE_NAME,
jobId: monitor.id,
});
+ const workerStats = await this.getWorkerStats();
+ await this.scaleWorkers(workerStats);
} else {
logger.error(errorMessages.JOB_QUEUE_DELETE_JOB, {
service: SERVICE_NAME,
diff --git a/Server/service/networkService.js b/Server/service/networkService.js
index b4d90c6e1..f09c7ac70 100644
--- a/Server/service/networkService.js
+++ b/Server/service/networkService.js
@@ -4,6 +4,13 @@ const logger = require("../utils/logger");
const http = require("http");
const { errorMessages, successMessages } = require("../utils/messages");
+/**
+ * NetworkService
+ *
+ * This service handles all network requests on the back end
+ * This includes pings, http requests, and pagespeed checks
+ */
+
class NetworkService {
constructor(db, emailService) {
this.db = db;
diff --git a/Server/service/pageSpeedService.js b/Server/service/pageSpeedService.js
deleted file mode 100644
index b482e7860..000000000
--- a/Server/service/pageSpeedService.js
+++ /dev/null
@@ -1,52 +0,0 @@
-const PageSpeedCheck = require("../models/PageSpeedCheck");
-const axios = require("axios");
-const logger = require("../utils/logger");
-
-class PageSpeedService {
- constructor() {
- this.SERVICE_NAME = "PageSpeedService";
- }
-
- /**
- * Runs a PageSpeed check using Google Lighthouse API.
- * @param {string} url - The URL to check.
- * @returns {Promise