diff --git a/Client/.env.production b/.env.production
similarity index 100%
rename from Client/.env.production
rename to .env.production
diff --git a/Client/.eslintrc.cjs b/.eslintrc.cjs
similarity index 100%
rename from Client/.eslintrc.cjs
rename to .eslintrc.cjs
diff --git a/.gitignore b/.gitignore
index 63420e072..ebf01f87a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,28 @@
-.vite
-.vscode
-*.sh
-!uptime.sh
-*.idea
-*.notes
\ No newline at end of file
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+.env
+
+!env.sh
\ No newline at end of file
diff --git a/Client/.gitignore b/Client/.gitignore
deleted file mode 100644
index ebf01f87a..000000000
--- a/Client/.gitignore
+++ /dev/null
@@ -1,28 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-
-.env
-
-!env.sh
\ No newline at end of file
diff --git a/Client/README.md b/Client/README.md
deleted file mode 100644
index f768e33fc..000000000
--- a/Client/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# React + Vite
-
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
-
-Currently, two official plugins are available:
-
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
diff --git a/Client/index.html b/Client/index.html
deleted file mode 100644
index 2d13bd25d..000000000
--- a/Client/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
- Checkmate
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Docker/.gitignore b/Docker/.gitignore
deleted file mode 100644
index 9b501e10d..000000000
--- a/Docker/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-*.sh
-!quickstart.sh
-!build_images.sh
-dev/mongo/data/*
-dev/redis/data/*
-dist/mongo/data/*
-dist/redis/data/*
-prod/mongo/data/*
-prod/redis/data/*
-*.env
-prod/certbot/*
\ No newline at end of file
diff --git a/Docker/dev/build_images.sh b/Docker/dev/build_images.sh
deleted file mode 100755
index f6171089c..000000000
--- a/Docker/dev/build_images.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-
-# Change directory to root Server directory for correct Docker Context
-cd "$(dirname "$0")"
-cd ../..
-
-# Define an array of services and their Dockerfiles
-declare -A services=(
- ["uptime_client"]="./Docker/dev/client.Dockerfile"
- ["uptime_database_mongo"]="./Docker/dev/mongoDB.Dockerfile"
- ["uptime_redis"]="./Docker/dev/redis.Dockerfile"
- ["uptime_server"]="./Docker/dev/server.Dockerfile"
-)
-
-# Loop through each service and build the corresponding image
-for service in "${!services[@]}"; do
- docker build -f "${services[$service]}" -t "$service" .
-
- ## Check if the build succeeded
- if [ $? -ne 0 ]; then
- echo "Error building $service image. Exiting..."
- exit 1
- fi
-done
-
-echo "All images built successfully"
diff --git a/Docker/dev/client.Dockerfile b/Docker/dev/client.Dockerfile
deleted file mode 100644
index cdb1646d6..000000000
--- a/Docker/dev/client.Dockerfile
+++ /dev/null
@@ -1,27 +0,0 @@
-FROM node:20-alpine AS build
-
-ENV NODE_OPTIONS="--max-old-space-size=4096"
-
-WORKDIR /app
-
-RUN apk add --no-cache \
- python3 \
- make g++ \
- gcc \
- libc-dev \
- linux-headers \
- libusb-dev \
- eudev-dev
-
-
-COPY ../../Client/package*.json ./
-
-RUN npm install
-
-COPY ../../Client .
-
-RUN npm run build-dev
-
-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
deleted file mode 100644
index f8b2c6936..000000000
--- a/Docker/dev/docker-compose.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-services:
- client:
- image: uptime_client:latest
- restart: always
- ports:
- - "5173:5173"
-
- depends_on:
- - server
- server:
- image: uptime_server:latest
- restart: always
- ports:
- - "5000:5000"
- env_file:
- - server.env
- depends_on:
- - redis
- - mongodb
- redis:
- image: uptime_redis:latest
- restart: always
- ports:
- - "6379:6379"
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
- mongodb:
- image: uptime_database_mongo:latest
- restart: always
- command: ["mongod", "--quiet"]
- ports:
- - "27017:27017"
- volumes:
- - ./mongo/data:/data/db
diff --git a/Docker/dev/mongoDB.Dockerfile b/Docker/dev/mongoDB.Dockerfile
deleted file mode 100644
index 969a320c2..000000000
--- a/Docker/dev/mongoDB.Dockerfile
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM mongo
-EXPOSE 27017
-CMD ["mongod"]
diff --git a/Docker/dev/redis.Dockerfile b/Docker/dev/redis.Dockerfile
deleted file mode 100644
index af68ec61e..000000000
--- a/Docker/dev/redis.Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM redis
-EXPOSE 6379
\ No newline at end of file
diff --git a/Docker/dev/server.Dockerfile b/Docker/dev/server.Dockerfile
deleted file mode 100644
index 3a9ed5907..000000000
--- a/Docker/dev/server.Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-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/build_images.sh b/Docker/dist/build_images.sh
deleted file mode 100755
index 33af5efdf..000000000
--- a/Docker/dist/build_images.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-
-# Change directory to root Server directory for correct Docker Context
-cd "$(dirname "$0")"
-cd ../..
-
-# Define an array of services and their Dockerfiles
-declare -A services=(
- ["bluewaveuptime/uptime_client"]="./Docker/dist/client.Dockerfile"
- ["bluewaveuptime/uptime_database_mongo"]="./Docker/dist/mongoDB.Dockerfile"
- ["bluewaveuptime/uptime_redis"]="./Docker/dist/redis.Dockerfile"
- ["bluewaveuptime/uptime_server"]="./Docker/dist/server.Dockerfile"
-)
-
-# Loop through each service and build the corresponding image
-for service in "${!services[@]}"; do
- docker build -f "${services[$service]}" -t "$service" .
-
- # Check if the build succeeded
- if [ $? -ne 0 ]; then
- echo "Error building $service image. Exiting..."
- exit 1
- fi
-done
-
-echo "All images built successfully"
\ No newline at end of file
diff --git a/Docker/dist/client.Dockerfile b/Docker/dist/client.Dockerfile
deleted file mode 100644
index ddb5ac73d..000000000
--- a/Docker/dist/client.Dockerfile
+++ /dev/null
@@ -1,33 +0,0 @@
-FROM node:20-alpine AS build
-
-ENV NODE_OPTIONS="--max-old-space-size=4096"
-
-WORKDIR /app
-
-RUN apk add --no-cache \
- python3 \
- make g++ \
- gcc \
- libc-dev \
- linux-headers \
- libusb-dev \
- eudev-dev
-
-
-COPY ../../Client/package*.json ./
-
-RUN npm install
-
-COPY ../../Client .
-
-RUN npm run build
-
-
-FROM nginx:1.27.1-alpine
-
-COPY ./Docker/dist/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
-COPY --from=build /app/dist /usr/share/nginx/html
-COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh
-RUN chmod +x /docker-entrypoint.d/env.sh
-
-CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/Docker/dist/docker-compose.yaml b/Docker/dist/docker-compose.yaml
deleted file mode 100644
index 420941e7e..000000000
--- a/Docker/dist/docker-compose.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-services:
- client:
- image: bluewaveuptime/uptime_client:latest
- restart: always
- environment:
- UPTIME_APP_API_BASE_URL: "http://localhost:5000/api/v1"
- UPTIME_STATUS_PAGE_SUBDOMAIN_PREFIX: "http://uptimegenie.com/"
- ports:
- - "80:80"
- - "443:443"
- depends_on:
- - server
- server:
- image: bluewaveuptime/uptime_server:latest
- restart: always
- ports:
- - "5000:5000"
- depends_on:
- - redis
- - mongodb
- environment:
- - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db
- - REDIS_HOST=redis
- # volumes:
- # - /var/run/docker.sock:/var/run/docker.sock:ro
- redis:
- image: bluewaveuptime/uptime_redis:latest
- restart: always
- ports:
- - "6379:6379"
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
- mongodb:
- image: bluewaveuptime/uptime_database_mongo:latest
- restart: always
- volumes:
- - ./mongo/data:/data/db
- command: ["mongod", "--quiet"]
- ports:
- - "27017:27017"
diff --git a/Docker/dist/mongoDB.Dockerfile b/Docker/dist/mongoDB.Dockerfile
deleted file mode 100644
index 969a320c2..000000000
--- a/Docker/dist/mongoDB.Dockerfile
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM mongo
-EXPOSE 27017
-CMD ["mongod"]
diff --git a/Docker/dist/nginx/conf.d/default.conf b/Docker/dist/nginx/conf.d/default.conf
deleted file mode 100644
index 0592dcd06..000000000
--- a/Docker/dist/nginx/conf.d/default.conf
+++ /dev/null
@@ -1,35 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
-
- server_name checkmate-demo.bluewavelabs.ca;
- server_tokens off;
-
- location /.well-known/acme-challenge/ {
- root /var/www/certbot;
- }
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
- }
-
- location /api/ {
- proxy_pass http://server:5000/api/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- location /api-docs/ {
- proxy_pass http://server:5000/api-docs/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
\ No newline at end of file
diff --git a/Docker/dist/redis.Dockerfile b/Docker/dist/redis.Dockerfile
deleted file mode 100644
index af68ec61e..000000000
--- a/Docker/dist/redis.Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM redis
-EXPOSE 6379
\ No newline at end of file
diff --git a/Docker/dist/server.Dockerfile b/Docker/dist/server.Dockerfile
deleted file mode 100644
index 3a9ed5907..000000000
--- a/Docker/dist/server.Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-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/prod/build_images.sh b/Docker/prod/build_images.sh
deleted file mode 100755
index c879190bc..000000000
--- a/Docker/prod/build_images.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-
-# Change directory to root directory for correct Docker Context
-cd "$(dirname "$0")"
-cd ../..
-
-# Define an array of services and their Dockerfiles
-declare -A services=(
- ["uptime_client"]="./Docker/prod/client.Dockerfile"
- ["uptime_database_mongo"]="./Docker/prod/mongoDB.Dockerfile"
- ["uptime_redis"]="./Docker/prod/redis.Dockerfile"
- ["uptime_server"]="./Docker/prod/server.Dockerfile"
-)
-
-# Loop through each service and build the corresponding image
-for service in "${!services[@]}"; do
- docker build -f "${services[$service]}" -t "$service" .
-
- # Check if the build succeeded
- if [ $? -ne 0 ]; then
- echo "Error building $service image. Exiting..."
- exit 1
- fi
-done
-
-echo "All images built successfully"
\ No newline at end of file
diff --git a/Docker/prod/certbot-compose.yaml b/Docker/prod/certbot-compose.yaml
deleted file mode 100644
index 274f15b0c..000000000
--- a/Docker/prod/certbot-compose.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
-version: '3'
-
-services:
- webserver:
- image: nginx:latest
- ports:
- - 80:80
- - 443:443
- restart: always
- volumes:
- - ./nginx/conf.d/:/etc/nginx/conf.d/:ro
- - ./certbot/www/:/var/www/certbot/:ro
- certbot:
- image: certbot/certbot:latest
- volumes:
- - ./certbot/www/:/var/www/certbot/:rw
- - ./certbot/conf/:/etc/letsencrypt/:rw
- depends_on:
- - webserver
diff --git a/Docker/prod/client.Dockerfile b/Docker/prod/client.Dockerfile
deleted file mode 100644
index 629ec644c..000000000
--- a/Docker/prod/client.Dockerfile
+++ /dev/null
@@ -1,29 +0,0 @@
-FROM node:20-alpine AS build
-
-ENV NODE_OPTIONS="--max-old-space-size=4096"
-
-WORKDIR /app
-
-RUN apk add --no-cache \
- python3 \
- make g++ \
- gcc \
- libc-dev \
- linux-headers \
- libusb-dev \
- eudev-dev
-
-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
-COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh
-RUN chmod +x /docker-entrypoint.d/env.sh
-CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/Docker/prod/docker-compose.yaml b/Docker/prod/docker-compose.yaml
deleted file mode 100644
index 0da2da996..000000000
--- a/Docker/prod/docker-compose.yaml
+++ /dev/null
@@ -1,57 +0,0 @@
-services:
- client:
- image: uptime_client:latest
- restart: always
- environment:
- UPTIME_APP_API_BASE_URL: "https://checkmate-demo.bluewavelabs.ca/api/v1"
- UPTIME_STATUS_PAGE_SUBDOMAIN_PREFIX: "http://uptimegenie.com/"
- ports:
- - "80:80"
- - "443:443"
- depends_on:
- - server
- volumes:
- - ./nginx/conf.d:/etc/nginx/conf.d/:ro
- - ./certbot/www:/var/www/certbot/:ro
- - ./certbot/conf/:/etc/nginx/ssl/:ro
-
- certbot:
- image: certbot/certbot:latest
- restart: always
- volumes:
- - ./certbot/www/:/var/www/certbot/:rw
- - ./certbot/conf/:/etc/letsencrypt/:rw
- server:
- image: uptime_server:latest
- restart: always
- ports:
- - "5000:5000"
- env_file:
- - server.env
- depends_on:
- - redis
- - mongodb
- redis:
- image: uptime_redis:latest
- restart: always
- ports:
- - "6379:6379"
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
- mongodb:
- image: uptime_database_mongo:latest
- restart: always
- command: ["mongod", "--quiet", "--auth"]
- ports:
- - "27017:27017"
- volumes:
- - ./mongo/data:/data/db
- - ./mongo/init/create_users.js:/docker-entrypoint-initdb.d/create_users.js
- env_file:
- - mongo.env
diff --git a/Docker/prod/mongo/init/create_users.js b/Docker/prod/mongo/init/create_users.js
deleted file mode 100644
index 67a21f910..000000000
--- a/Docker/prod/mongo/init/create_users.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var username = process.env.USERNAME_ENV_VAR;
-var password = process.env.PASSWORD_ENV_VAR;
-
-db = db.getSiblingDB("uptime_db");
-
-db.createUser({
- user: username,
- pwd: password,
- roles: [
- {
- role: "readWrite",
- db: "uptime_db",
- },
- ],
-});
-print("User uptime_user created successfully");
diff --git a/Docker/prod/mongoDB.Dockerfile b/Docker/prod/mongoDB.Dockerfile
deleted file mode 100644
index 80c8b9c8f..000000000
--- a/Docker/prod/mongoDB.Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM mongo
-COPY ./Docker/prod/mongo/init/create_users.js /docker-entrypoint-initdb.d/
-EXPOSE 27017
-CMD ["mongod"]
diff --git a/Docker/prod/nginx/conf.d/cerbot b/Docker/prod/nginx/conf.d/cerbot
deleted file mode 100644
index f26cfeb23..000000000
--- a/Docker/prod/nginx/conf.d/cerbot
+++ /dev/null
@@ -1,15 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
-
- server_name checkmate-demo.bluewavelabs.ca www.checkmate-demo.bluewavelabs.ca;
- server_tokens off;
-
- location /.well-known/acme-challenge/ {
- root /var/www/certbot;
- }
-
- location / {
- return 301 https://[domain-name]$request_uri;
- }
-}
diff --git a/Docker/prod/nginx/conf.d/default.conf b/Docker/prod/nginx/conf.d/default.conf
deleted file mode 100644
index 98e1bd97d..000000000
--- a/Docker/prod/nginx/conf.d/default.conf
+++ /dev/null
@@ -1,69 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
-
- server_name checkmate-demo.bluewavelabs.ca;
- server_tokens off;
-
- location /.well-known/acme-challenge/ {
- root /var/www/certbot;
- }
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
- }
-
- location /api/ {
- proxy_pass http://server:5000/api/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- location /api-docs/ {
- proxy_pass http://server:5000/api-docs/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
-
-server {
- listen 443 default_server ssl http2;
- listen [::]:443 ssl http2;
-
- server_name checkmate-demo.bluewavelabs.ca;
-
- ssl_certificate /etc/nginx/ssl/live/checkmate-demo.bluewavelabs.ca/fullchain.pem;
- ssl_certificate_key /etc/nginx/ssl/live/checkmate-demo.bluewavelabs.ca/privkey.pem;
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
- }
-
- location /api/ {
- proxy_pass http://server:5000/api/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- location /api-docs/ {
- proxy_pass http://server:5000/api-docs/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
diff --git a/Docker/prod/redis.Dockerfile b/Docker/prod/redis.Dockerfile
deleted file mode 100644
index af68ec61e..000000000
--- a/Docker/prod/redis.Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM redis
-EXPOSE 6379
\ No newline at end of file
diff --git a/Docker/prod/server.Dockerfile b/Docker/prod/server.Dockerfile
deleted file mode 100644
index 707b774e7..000000000
--- a/Docker/prod/server.Dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-FROM node:20-alpine
-
-ENV NODE_OPTIONS="--max-old-space-size=2048"
-
-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/test/build_images.sh b/Docker/test/build_images.sh
deleted file mode 100755
index c879190bc..000000000
--- a/Docker/test/build_images.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-
-# Change directory to root directory for correct Docker Context
-cd "$(dirname "$0")"
-cd ../..
-
-# Define an array of services and their Dockerfiles
-declare -A services=(
- ["uptime_client"]="./Docker/prod/client.Dockerfile"
- ["uptime_database_mongo"]="./Docker/prod/mongoDB.Dockerfile"
- ["uptime_redis"]="./Docker/prod/redis.Dockerfile"
- ["uptime_server"]="./Docker/prod/server.Dockerfile"
-)
-
-# Loop through each service and build the corresponding image
-for service in "${!services[@]}"; do
- docker build -f "${services[$service]}" -t "$service" .
-
- # Check if the build succeeded
- if [ $? -ne 0 ]; then
- echo "Error building $service image. Exiting..."
- exit 1
- fi
-done
-
-echo "All images built successfully"
\ No newline at end of file
diff --git a/Docker/test/cerbot-compose.yaml b/Docker/test/cerbot-compose.yaml
deleted file mode 100644
index 9220d07a1..000000000
--- a/Docker/test/cerbot-compose.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
-version: "3"
-
-services:
- webserver:
- image: nginx:latest
- ports:
- - 80:80
- - 443:443
- restart: always
- volumes:
- - ./nginx/conf.d/:/etc/nginx/conf.d/:ro
- - ./certbot/www/:/var/www/certbot/:ro
- certbot:
- image: certbot/certbot:latest
- volumes:
- - ./certbot/www/:/var/www/certbot/:rw
- - ./certbot/conf/:/etc/letsencrypt/:rw
- depends_on:
- - webserver
diff --git a/Docker/test/client.Dockerfile b/Docker/test/client.Dockerfile
deleted file mode 100644
index 215d20873..000000000
--- a/Docker/test/client.Dockerfile
+++ /dev/null
@@ -1,30 +0,0 @@
-FROM node:20-alpine AS build
-
-ENV NODE_OPTIONS="--max-old-space-size=4096"
-
-WORKDIR /app
-
-RUN apk add --no-cache \
- python3 \
- make g++ \
- gcc \
- libc-dev \
- linux-headers \
- libusb-dev \
- eudev-dev
-
-
-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
-COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh
-RUN chmod +x /docker-entrypoint.d/env.sh
-CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/Docker/test/docker-compose.yaml b/Docker/test/docker-compose.yaml
deleted file mode 100644
index 2c38e8af3..000000000
--- a/Docker/test/docker-compose.yaml
+++ /dev/null
@@ -1,57 +0,0 @@
-services:
- client:
- image: uptime_client:latest
- restart: always
- environment:
- UPTIME_APP_API_BASE_URL: "https://checkmate-test.bluewavelabs.ca/api/v1"
- UPTIME_STATUS_PAGE_SUBDOMAIN_PREFIX: "http://uptimegenie.com/"
- ports:
- - "80:80"
- - "443:443"
- depends_on:
- - server
- volumes:
- - ./nginx/conf.d:/etc/nginx/conf.d/:ro
- - ./certbot/www:/var/www/certbot/:ro
- - ./certbot/conf/:/etc/nginx/ssl/:ro
-
- certbot:
- image: certbot/certbot:latest
- restart: always
- volumes:
- - ./certbot/www/:/var/www/certbot/:rw
- - ./certbot/conf/:/etc/letsencrypt/:rw
- server:
- image: uptime_server:latest
- restart: always
- ports:
- - "5000:5000"
- env_file:
- - server.env
- depends_on:
- - redis
- - mongodb
- redis:
- image: uptime_redis:latest
- restart: always
- ports:
- - "6379:6379"
- volumes:
- - ./redis/data:/data
- healthcheck:
- test: ["CMD", "redis-cli", "ping"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 5s
- mongodb:
- image: uptime_database_mongo:latest
- restart: always
- command: ["mongod", "--quiet", "--replSet", "rs0", "--bind_ip_all"]
- ports:
- - "27017:27017"
- volumes:
- - ./mongo/data:/data/db
- - ./mongo/init/create_users.js:/docker-entrypoint-initdb.d/create_users.js
- env_file:
- - mongo.env
diff --git a/Docker/test/mongo/init/create_users.js b/Docker/test/mongo/init/create_users.js
deleted file mode 100644
index 67a21f910..000000000
--- a/Docker/test/mongo/init/create_users.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var username = process.env.USERNAME_ENV_VAR;
-var password = process.env.PASSWORD_ENV_VAR;
-
-db = db.getSiblingDB("uptime_db");
-
-db.createUser({
- user: username,
- pwd: password,
- roles: [
- {
- role: "readWrite",
- db: "uptime_db",
- },
- ],
-});
-print("User uptime_user created successfully");
diff --git a/Docker/test/mongoDB.Dockerfile b/Docker/test/mongoDB.Dockerfile
deleted file mode 100644
index 80c8b9c8f..000000000
--- a/Docker/test/mongoDB.Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM mongo
-COPY ./Docker/prod/mongo/init/create_users.js /docker-entrypoint-initdb.d/
-EXPOSE 27017
-CMD ["mongod"]
diff --git a/Docker/test/nginx/conf.d/certbot b/Docker/test/nginx/conf.d/certbot
deleted file mode 100644
index eba24638c..000000000
--- a/Docker/test/nginx/conf.d/certbot
+++ /dev/null
@@ -1,15 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
-
- server_name checkmate-test.bluewavelabs.ca www.checkmate-test.bluewavelabs.ca;
- server_tokens off;
-
- location /.well-known/acme-challenge/ {
- root /var/www/certbot;
- }
-
- location / {
- return 301 https://[domain-name]$request_uri;
- }
-}
diff --git a/Docker/test/nginx/conf.d/default.conf b/Docker/test/nginx/conf.d/default.conf
deleted file mode 100644
index 1533dd35e..000000000
--- a/Docker/test/nginx/conf.d/default.conf
+++ /dev/null
@@ -1,69 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
-
- server_name checkmate-test.bluewavelabs.ca;
- server_tokens off;
-
- location /.well-known/acme-challenge/ {
- root /var/www/certbot;
- }
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
- }
-
- location /api/ {
- proxy_pass http://server:5000/api/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- location /api-docs/ {
- proxy_pass http://server:5000/api-docs/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
-
-server {
- listen 443 default_server ssl http2;
- listen [::]:443 ssl http2;
-
- server_name checkmate-test.bluewavelabs.ca;
-
- ssl_certificate /etc/nginx/ssl/live/checkmate-test.bluewavelabs.ca/fullchain.pem;
- ssl_certificate_key /etc/nginx/ssl/live/checkmate-test.bluewavelabs.ca/privkey.pem;
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
- }
-
- location /api/ {
- proxy_pass http://server:5000/api/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- location /api-docs/ {
- proxy_pass http://server:5000/api-docs/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
diff --git a/Docker/test/redis.Dockerfile b/Docker/test/redis.Dockerfile
deleted file mode 100644
index af68ec61e..000000000
--- a/Docker/test/redis.Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM redis
-EXPOSE 6379
\ No newline at end of file
diff --git a/Docker/test/server.Dockerfile b/Docker/test/server.Dockerfile
deleted file mode 100644
index 5d4ea7364..000000000
--- a/Docker/test/server.Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-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/README.md b/README.md
index e6cadaeda..f768e33fc 100644
--- a/README.md
+++ b/README.md
@@ -1,140 +1,8 @@
+# React + Vite
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
-
-
-
-
-
-
-
-
-
-[](https://www.bestpractices.dev/projects/9901)
+Currently, two official plugins are available:
-
-
-
-An open source uptime and infrastructure monitoring application
-
-
-
-Checkmate is an open source monitoring tool used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.
-
-Checkmate also has an agent, called [Capture](https://github.com/bluewave-labs/capture), to retrieve data from remote servers. While Capture is not required to run Checkmate, it provides additional insigths about your servers' CPU, RAM, disk and temperature status.
-
-Checkmate has ben stress tested with 1000+ active monitors without any particular issues or performance bottlenecks.
-
-We **love** what we are building here, and we contibuously learn a few things about Reactjs, Nodejs, MongoDB and Docker while building Checkmate.
-
-## ๐ฆ Demo
-
-See [Checkmate](https://checkmate-demo.bluewavelabs.ca/) in action. The username is uptimedemo@demo.com and the password is Demouser1! (just a note that we update the demo server from time to time, so if it doesn't work for you, please ping us on Discussions channel).
-
-## ๐ User's guide
-
-Usage instructions can be found [here](https://docs.checkmate.so/). It's still WIP and some of the information there might be outdated. Rest assured, we are doing our best! :)
-
-## ๐ ๏ธ Installation
-
-See installation instructions in [Checkmate documentation portal](https://docs.checkmate.so/quickstart). Alternatively, you can also use [Coolify](https://coolify.io/) or [Elestio](https://elest.io/open-source/checkmate) for a one click Docker deployment. If you would like to monitor your server infrastructure, you'll need [Capture agent](https://github.com/bluewave-labs/capture). Capture repository also contains the installation instructions.
-
-## ๐ Performance
-Thanks to extensive optimizations, Checkmate operates with an exceptionally small memory footprint, requiring minimal memory and CPU resources. Hereโs the memory usage of a Node.js instance running on a server that monitors 323 servers every minute:
-
-
-
-You can see the memory footprint of MongoDB and Redis on the same server (398Mb and 15Mb) for the same amount of servers:
-
-
-
-
-## ๐ Questions & ideas
-
-If you have any questions, suggestions or comments, please use our [Discord channel](https://discord.gg/NAb6H3UTjK). We've also 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
-
-- Completely open source, deployable on your servers
-- Website monitoring
-- Page speed monitoring
-- Infrastructure monitoring (memory, disk usage, CPU performance etc) - requires [Capture](https://github.com/bluewave-labs/capture)
-- Docker monitoring
-- Ping monitoring
-- SSL monitoring
-- Port monitoring
-- Incidents at a glance
-- E-mail notifications
-- Scheduled maintenance
-
-**Short term roadmap:**
-
-- Global (distributed) uptime checking on Solana network (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1593
-- Status pages (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1131
-- Translations (i18n) (**in progress**)
-- Better notification options (Webhooks, Discord, Telegram, Slack) (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1545
-- Command line interface (CLI) (**in progress**) https://github.com/bluewave-labs/Checkmate/issues/1558
-- JSON query monitoring https://github.com/bluewave-labs/Checkmate/issues/1573
-- Tagging/grouping monitors https://github.com/bluewave-labs/Checkmate/issues/1546
-- More configuration options
-- DNS monitoring
-
-## ๐๏ธ Screenshots
-
-
-
-
-
-
-
-
-
-
-## ๐๏ธ Tech stack
-
-- [ReactJs](https://react.dev/)
-- [MUI (React framework)](https://mui.com/)
-- [Node.js](https://nodejs.org/en)
-- [MongoDB](https://mongodb.com)
-- [Recharts](https://recharts.org)
-- Lots of other open source components!
-
-## A few links
-
-- If you would like to support us, please consider giving it a โญ and click on "watch".
-- Have a question or suggestion for the roadmap/featureset? Check our [Discord channel](https://discord.gg/NAb6H3UTjK) or [Discussions](https://github.com/bluewave-labs/checkmate/discussions) forum.
-- Need a ping when there's a new release? Use [Newreleases](https://newreleases.io/), a free service to track releases.
-- Watch a Checkmate [installation and usage video](https://www.youtube.com/watch?v=GfFOc0xHIwY)
-
-## ๐ค Contributing
-
-We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 3700+ stars and attracted 45+ contributors from around the globe. So, donโt hold back โ jump in, contribute and learn with us!
-
-Here's how you can contribute:
-
-0. Star this repo :)
-1. Check [Contributor's guideline](https://github.com/bluewave-labs/bluewave-uptime/blob/master/CONTRIBUTING.md). First timers are encouraged to check `good-first-issue` tag.
-2. Optionally, read [project structure](https://docs.checkmate.so/developers-guide/general-project-structure) and [high level overview](https://bluewavelabs.gitbook.io/checkmate/developers-guide/high-level-overview).
-3. Have a look at our Figma designs [here](https://www.figma.com/design/RPSfaw66HjzSwzntKcgDUV/Uptime-Genie?node-id=0-1&t=WqOFv9jqNTFGItpL-1) if you are going to use one of our designs. We encourage you to copy to your own Figma page, then work on it as it is read-only.
-4. Open an issue if you believe you've encountered a bug.
-5. Check for good-first-issue's if you are a newcomer.
-6. Make a pull request to add new features/make quality-of-life improvements/fix bugs.
-
-
-
-
-
-## ๐ฐ Our sponsors
-
-Thanks to [Gitbook](https://gitbook.io/) for giving us a free tier for their documentation platform, and [Poeditor](https://poeditor.com/) providing us a free account to use their i18n services. If you would like to sponsor Checkmate, please send an email to hello@bluewavelabs.ca
-
-[](https://star-history.com/#bluewave-labs/bluewave-uptime&Date)
-
-Also check other developer and contributor-friendly projects of BlueWave:
-
-- [LangRoute](https://github.com/bluewave-labs/langroute), an LLM proxy and gateway
-- [DataRoom](https://github.com/bluewave-labs/bluewave-dataroom), an secure file sharing application, aka dataroom.
-- [Headcount](https://github.com/bluewave-labs/bluewave-hrm), a complete Human Resource Management platform.
-- [Guidefox](https://github.com/bluewave-labs/guidefox), an application that helps new users learn how to use your product via hints, tours, popups and banners.
-- [VerifyWise](https://github.com/bluewave-labs/verifywise), the first open source AI governance platform.
-
- since 14 Jan, 2025
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
diff --git a/Server/.dockerignore b/Server/.dockerignore
deleted file mode 100644
index d88f9054e..000000000
--- a/Server/.dockerignore
+++ /dev/null
@@ -1 +0,0 @@
-./docker
\ No newline at end of file
diff --git a/Server/.gitignore b/Server/.gitignore
deleted file mode 100644
index 6dc7e9e0d..000000000
--- a/Server/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-node_modules
-.env
-*.log
-*.sh
-.nyc_output
-coverage
-.clinic
\ No newline at end of file
diff --git a/Server/.mocharc.cjs b/Server/.mocharc.cjs
deleted file mode 100644
index e4c8f0dd7..000000000
--- a/Server/.mocharc.cjs
+++ /dev/null
@@ -1,8 +0,0 @@
-module.exports = {
- require: ["esm", "chai/register-expect.js"], // Include Chai's "expect" interface globally
- spec: "tests/**/*.test.js", // Specify test files
- timeout: 5000, // Set test-case timeout in milliseconds
- recursive: true, // Include subdirectories
- reporter: "spec", // Use the "spec" reporter
- exit: true, // Force Mocha to quit after tests complete
-};
diff --git a/Server/.nycrc b/Server/.nycrc
deleted file mode 100644
index 96f20d795..000000000
--- a/Server/.nycrc
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "all": true,
- "include": ["controllers/*.js", "utils/*.js", "service/*.js", "db/mongo/modules/*.js"],
- "exclude": ["**/*.test.js"],
- "reporter": ["html", "text", "lcov"],
- "sourceMap": false,
- "instrument": true
-}
diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js
deleted file mode 100644
index 94952e3c7..000000000
--- a/Server/controllers/authController.js
+++ /dev/null
@@ -1,523 +0,0 @@
-import {
- registrationBodyValidation,
- loginValidation,
- editUserParamValidation,
- editUserBodyValidation,
- recoveryValidation,
- recoveryTokenValidation,
- newPasswordValidation,
-} from "../validation/joi.js";
-import logger from "../utils/logger.js";
-import jwt from "jsonwebtoken";
-import { getTokenFromHeaders, tokenType } from "../utils/utils.js";
-import crypto from "crypto";
-import { handleValidationError, handleError } from "./controllerUtils.js";
-const SERVICE_NAME = "authController";
-
-class AuthController {
- constructor(db, settingsService, emailService, jobQueue, stringService) {
- this.db = db;
- this.settingsService = settingsService;
- this.emailService = emailService;
- this.jobQueue = jobQueue;
- this.stringService = stringService;
- }
-
- /**
- * Creates and returns JWT token with an arbitrary payload
- * @function
- * @param {Object} payload
- * @param {tokenType} typeOfToken - Whether to generate refresh token with long TTL or access token with short TTL.
- * @param {Object} appSettings
- * @returns {String}
- * @throws {Error}
- */
- issueToken = (payload, typeOfToken, appSettings) => {
- try {
- const tokenTTL =
- typeOfToken === tokenType.REFRESH_TOKEN
- ? (appSettings?.refreshTokenTTL ?? "7d")
- : (appSettings?.jwtTTL ?? "2h");
- const tokenSecret =
- typeOfToken === tokenType.REFRESH_TOKEN
- ? appSettings?.refreshTokenSecret
- : appSettings?.jwtSecret;
- const payloadData = typeOfToken === tokenType.REFRESH_TOKEN ? {} : payload;
-
- return jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL });
- } catch (error) {
- throw handleError(error, SERVICE_NAME, "issueToken");
- }
- };
-
- /**
- * 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).
- */
- registerUser = async (req, res, next) => {
- try {
- await registrationBodyValidation.validateAsync(req.body);
- } catch (error) {
- const validationError = handleValidationError(error, SERVICE_NAME);
- next(validationError);
- 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 this.db.checkSuperadmin(req, res);
- if (superAdminExists) {
- await this.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 this.db.updateAppSettings({ jwtSecret });
- }
-
- const newUser = await this.db.insertUser({ ...req.body }, req.file);
- logger.info({
- message: this.stringService.authCreateUser,
- service: SERVICE_NAME,
- details: newUser._id,
- });
-
- const userForToken = { ...newUser._doc };
- delete userForToken.profileImage;
- delete userForToken.avatarImage;
-
- const appSettings = await this.settingsService.getSettings();
-
- const token = this.issueToken(userForToken, tokenType.ACCESS_TOKEN, appSettings);
- const refreshToken = this.issueToken({}, tokenType.REFRESH_TOKEN, appSettings);
-
- this.emailService
- .buildAndSendEmail(
- "welcomeEmailTemplate",
- { name: newUser.firstName },
- newUser.email,
- "Welcome to Uptime Monitor"
- )
- .catch((error) => {
- logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "registerUser",
- stack: error.stack,
- });
- });
-
- res.success({
- msg: this.stringService.authCreateUser,
- data: { user: newUser, token: token, refreshToken: refreshToken },
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "registerController"));
- }
- };
-
- /**
- * Logs in a user by validating the user's credentials and issuing a JWT token.
- * @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.
- * @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.
- */
- loginUser = async (req, res, next) => {
- try {
- await loginValidation.validateAsync(req.body);
- } catch (error) {
- const validationError = handleValidationError(error, SERVICE_NAME);
- next(validationError);
- return;
- }
- try {
- const { email, password } = req.body;
-
- // Check if user exists
- const user = await this.db.getUserByEmail(email);
-
- // Compare password
- const match = await user.comparePassword(password);
- if (match !== true) {
- const error = new Error(this.stringService.authIncorrectPassword);
- error.status = 401;
- next(error);
- 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 = await this.settingsService.getSettings();
- const token = this.issueToken(
- userWithoutPassword,
- tokenType.ACCESS_TOKEN,
- appSettings
- );
- const refreshToken = this.issueToken({}, tokenType.REFRESH_TOKEN, appSettings);
- // reset avatar image
- userWithoutPassword.avatarImage = user.avatarImage;
-
- return res.success({
- msg: this.stringService.authLoginUser,
- data: {
- user: userWithoutPassword,
- token: token,
- refreshToken: refreshToken,
- },
- });
- } catch (error) {
- error.status = 401;
- next(handleError(error, SERVICE_NAME, "loginUser"));
- }
- };
-
- /**
- * Generates new auth token if the refresh token is valid
- * @async
- * @param {Express.Request} req - The Express request object.
- * @property {Object} req.headers - The parameter of the request.
- * @param {Express.Response} res - The Express response object.
- * @param {function} next - The next middleware function.
- * @returns {Object} The response object with a success status, a message indicating new auth token is generated.
- * @throws {Error} If there is an error during the process such as any of the token is not received
- */
- refreshAuthToken = async (req, res, next) => {
-
- try {
- // check for refreshToken
- const refreshToken = req.headers["x-refresh-token"];
-
- if (!refreshToken) {
- // No refresh token provided
- const error = new Error(this.stringService.noRefreshToken);
- error.status = 401;
- error.service = SERVICE_NAME;
- error.method = "refreshAuthToken";
- return next(error);
- }
-
- // Verify refresh token
- const appSettings = await this.settingsService.getSettings();
- const { refreshTokenSecret } = appSettings;
- jwt.verify(refreshToken, refreshTokenSecret, async (refreshErr, refreshDecoded) => {
- if (refreshErr) {
- // Invalid or expired refresh token, trigger logout
- const errorMessage =
- refreshErr.name === "TokenExpiredError"
- ? this.stringService.expiredAuthToken
- : this.stringService.invalidAuthToken;
- const error = new Error(errorMessage);
- error.status = 401;
- error.service = SERVICE_NAME;
- return next(error);
- }
- });
- // Refresh token is valid and unexpired, generate new access token
- const oldAuthToken = getTokenFromHeaders(req.headers);
- const { jwtSecret } = await this.settingsService.getSettings();
- const payloadData = jwt.verify(oldAuthToken, jwtSecret, { ignoreExpiration: true });
- // delete old token related data
- delete payloadData.iat;
- delete payloadData.exp;
- const newAuthToken = this.issueToken(
- payloadData,
- tokenType.ACCESS_TOKEN,
- appSettings
- );
-
- return res.success({
- msg: this.stringService.authTokenRefreshed,
- data: { user: payloadData, token: newAuthToken, refreshToken: refreshToken },
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "refreshAuthToken"));
- }
- };
- /**
- * 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 {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).
- */
- editUser = async (req, res, next) => {
-
- try {
- await editUserParamValidation.validateAsync(req.params);
- await editUserBodyValidation.validateAsync(req.body);
- } catch (error) {
- const validationError = handleValidationError(error, SERVICE_NAME);
- next(validationError);
- 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(this.stringService.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 } = this.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 this.db.getUserByEmail(email);
- // Compare passwords
- const match = await user.comparePassword(req.body.password);
- // If not a match, throw a 403
- // 403 instead of 401 to avoid triggering axios interceptor
- if (!match) {
- const error = new Error(this.stringService.authIncorrectPassword);
- error.status = 403;
- next(error);
- return;
- }
- // If a match, update the password
- req.body.password = req.body.newPassword;
- }
-
- const updatedUser = await this.db.updateUser(req, res);
- res.success({
- msg: this.stringService.authUpdateUser,
- data: updatedUser,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "userEditController"));
- }
- };
-
- /**
- * 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.
- */
- checkSuperadminExists = async (req, res, next) => {
- try {
- const superAdminExists = await this.db.checkSuperadmin(req, res);
-
- return res.success({
- msg: this.stringService.authAdminExists,
- data: superAdminExists,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "checkSuperadminController"));
- }
- };
- /**
- * 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).
- */
- requestRecovery = async (req, res, next) => {
- try {
- await recoveryValidation.validateAsync(req.body);
- } catch (error) {
- const validationError = handleValidationError(error, SERVICE_NAME);
- next(validationError);
- return;
- }
-
- try {
- const { email } = req.body;
- const user = await this.db.getUserByEmail(email);
- const recoveryToken = await this.db.requestRecoveryToken(req, res);
- const name = user.firstName;
- const { clientHost } = this.settingsService.getSettings();
- const url = `${clientHost}/set-new-password/${recoveryToken.token}`;
- const msgId = await this.emailService.buildAndSendEmail(
- "passwordResetTemplate",
- {
- name,
- email,
- url,
- },
- email,
- "Checkmate Password Reset"
- );
-
- return res.success({
- msg: this.stringService.authCreateRecoveryToken,
- data: msgId,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "recoveryRequestController"));
- }
- };
- /**
- * 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).
- */
- validateRecovery = async (req, res, next) => {
- try {
- await recoveryTokenValidation.validateAsync(req.body);
- } catch (error) {
- const validationError = handleValidationError(error, SERVICE_NAME);
- next(validationError);
- return;
- }
-
- try {
- await this.db.validateRecoveryToken(req, res);
-
- return res.success({
- msg: this.stringService.authVerifyRecoveryToken,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController"));
- }
- };
-
- /**
- * 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).
- */
- resetPassword = async (req, res, next) => {
- try {
- await newPasswordValidation.validateAsync(req.body);
- } catch (error) {
- const validationError = handleValidationError(error, SERVICE_NAME);
- next(validationError);
- return;
- }
- try {
- const user = await this.db.resetPassword(req, res);
- const appSettings = await this.settingsService.getSettings();
- const token = this.issueToken(user._doc, tokenType.ACCESS_TOKEN, appSettings);
-
- return res.success({
- msg: this.stringService.authResetPassword,
- data: { user, token },
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "resetPasswordController"));
- }
- };
-
- /**
- * Deletes a user and all associated monitors, checks, and alerts.
- *
- * @param {Object} req - The request object.
- * @param {Object} res - The response object.
- * @param {Function} next - The next middleware function.
- * @returns {Object} The response object with success status and message.
- * @throws {Error} If user validation fails or user is not found in the database.
- */
- 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 this.db.getUserByEmail(email);
- // 1. Find all the monitors associated with the team ID if superadmin
-
- const result = await this.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 this.jobQueue.deleteJob(monitor);
- await this.db.deleteChecks(monitor._id);
- await this.db.deletePageSpeedChecksByMonitorId(monitor._id);
- await this.db.deleteNotificationsByMonitorId(monitor._id);
- })
- ));
-
- // 3. Delete team
- await this.db.deleteTeam(user.teamId);
- // 4. Delete all other team members
- await this.db.deleteAllOtherUsers();
- // 5. Delete each monitor
- await this.db.deleteMonitorsByUserId(user._id);
- }
- // 6. Delete the user by id
- await this.db.deleteUser(user._id);
-
- return res.success({
- msg: this.stringService.authDeleteUser,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteUserController"));
- }
- };
-
- getAllUsers = async (req, res, next) => {
- try {
- const allUsers = await this.db.getAllUsers(req, res);
-
- return res.success({
- msg: this.stringService.authGetAllUsers,
- data: allUsers,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getAllUsersController"));
- }
- };
-}
-
-export default AuthController;
diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js
deleted file mode 100644
index 2addd7359..000000000
--- a/Server/controllers/checkController.js
+++ /dev/null
@@ -1,154 +0,0 @@
-import {
- createCheckParamValidation,
- createCheckBodyValidation,
- getChecksParamValidation,
- getChecksQueryValidation,
- getTeamChecksParamValidation,
- getTeamChecksQueryValidation,
- deleteChecksParamValidation,
- deleteChecksByTeamIdParamValidation,
- updateChecksTTLBodyValidation,
-} from "../validation/joi.js";
-import jwt from "jsonwebtoken";
-import { getTokenFromHeaders } from "../utils/utils.js";
-import { handleValidationError, handleError } from "./controllerUtils.js";
-
-const SERVICE_NAME = "checkController";
-
-class CheckController {
- constructor(db, settingsService, stringService) {
- this.db = db;
- this.settingsService = settingsService;
- this.stringService = stringService;
- }
-
- createCheck = async (req, res, next) => {
- try {
- await createCheckParamValidation.validateAsync(req.params);
- await createCheckBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const checkData = { ...req.body };
- const check = await this.db.createCheck(checkData);
-
- return res.success({
- msg: this.stringService.checkCreate,
- data: check,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "createCheck"));
- }
- };
-
- getChecksByMonitor = async (req, res, next) => {
- try {
- await getChecksParamValidation.validateAsync(req.params);
- await getChecksQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const result = await this.db.getChecksByMonitor(req);
-
- return res.success({
- msg: this.stringService.checkGet,
- data: result,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getChecks"));
- }
- };
-
- getChecksByTeam = async (req, res, next) => {
- try {
- await getTeamChecksParamValidation.validateAsync(req.params);
- await getTeamChecksQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
- try {
- const checkData = await this.db.getChecksByTeam(req);
-
- return res.success({
- msg: this.stringService.checkGet,
- data: checkData,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getTeamChecks"));
- }
- };
-
- deleteChecks = async (req, res, next) => {
- try {
- await deleteChecksParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const deletedCount = await this.db.deleteChecks(req.params.monitorId);
-
- return res.success({
- msg: this.stringService.checkDelete,
- data: { deletedCount },
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteChecks"));
- }
- };
-
- deleteChecksByTeamId = async (req, res, next) => {
- try {
- await deleteChecksByTeamIdParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const deletedCount = await this.db.deleteChecksByTeamId(req.params.teamId);
-
- return res.success({
- msg: this.stringService.checkDelete,
- data: { deletedCount },
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteChecksByTeamId"));
- }
- };
-
- updateChecksTTL = async (req, res, next) => {
- const SECONDS_PER_DAY = 86400;
-
- try {
- await updateChecksTTLBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- // Get user's teamId
- const token = getTokenFromHeaders(req.headers);
- const { jwtSecret } = this.settingsService.getSettings();
- const { teamId } = jwt.verify(token, jwtSecret);
- const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY;
- await this.db.updateChecksTTL(teamId, ttl);
-
- return res.success({
- msg: this.stringService.checkUpdateTTL,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "updateTTL"));
- }
- };
-}
-export default CheckController;
diff --git a/Server/controllers/controllerUtils.js b/Server/controllers/controllerUtils.js
deleted file mode 100644
index ec7f1122f..000000000
--- a/Server/controllers/controllerUtils.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const handleValidationError = (error, serviceName) => {
- error.status = 422;
- error.service = serviceName;
- error.message = error.details?.[0]?.message || error.message || "Validation Error";
- return error;
-};
-
-const handleError = (error, serviceName, method, status = 500) => {
- error.status === undefined ? (error.status = status) : null;
- error.service === undefined ? (error.service = serviceName) : null;
- error.method === undefined ? (error.method = method) : null;
- return error;
-};
-
-const fetchMonitorCertificate = async (sslChecker, monitor) => {
- const monitorUrl = new URL(monitor.url);
- const hostname = monitorUrl.hostname;
- const cert = await sslChecker(hostname);
- // Throw an error if no cert or if cert.validTo is not present
- if (cert?.validTo === null || cert?.validTo === undefined) {
- throw new Error("Certificate not found");
- }
- return cert;
-};
-
-export { handleValidationError, handleError, fetchMonitorCertificate };
diff --git a/Server/controllers/distributedUptimeController.js b/Server/controllers/distributedUptimeController.js
deleted file mode 100644
index 78ccafa75..000000000
--- a/Server/controllers/distributedUptimeController.js
+++ /dev/null
@@ -1,212 +0,0 @@
-import { handleError } from "./controllerUtils.js";
-import Monitor from "../db/models/Monitor.js";
-import DistributedUptimeCheck from "../db/models/DistributedUptimeCheck.js";
-const SERVICE_NAME = "DistributedUptimeQueueController";
-
-class DistributedUptimeController {
- constructor(db, http, statusService) {
- this.db = db;
- this.http = http;
- this.statusService = statusService;
- this.resultsCallback = this.resultsCallback.bind(this);
- this.getDistributedUptimeMonitors = this.getDistributedUptimeMonitors.bind(this);
- this.getDistributedUptimeMonitorDetails =
- this.getDistributedUptimeMonitorDetails.bind(this);
- }
-
- async resultsCallback(req, res, next) {
- try {
- const { id, result } = req.body;
- // Calculate response time
- const {
- first_byte_took,
- body_read_took,
- dns_took,
- conn_took,
- connect_took,
- tls_took,
- status_code,
- error,
- } = result;
-
- // Calculate response time
- const responseTime = first_byte_took / 1_000_000;
-
- // Calculate if server is up or down
- const isErrorStatus = status_code >= 400;
- const hasError = error !== "";
-
- const status = isErrorStatus || hasError ? false : true;
-
- // Build response
- const distributedUptimeResponse = {
- monitorId: id,
- type: "distributed_http",
- payload: result,
- status,
- code: status_code,
- responseTime,
- first_byte_took,
- body_read_took,
- dns_took,
- conn_took,
- connect_took,
- tls_took,
- };
- if (error) {
- const code = status_code || this.NETWORK_ERROR;
- distributedUptimeResponse.code = code;
- distributedUptimeResponse.message =
- this.http.STATUS_CODES[code] || "Network Error";
- } else {
- distributedUptimeResponse.message = this.http.STATUS_CODES[status_code];
- }
-
- await this.statusService.updateStatus(distributedUptimeResponse);
-
- res.status(200).json({ message: "OK" });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "resultsCallback"));
- }
- }
-
- async getDistributedUptimeMonitors(req, res, next) {
- try {
- res.setHeader("Content-Type", "text/event-stream");
- res.setHeader("Cache-Control", "no-cache");
- res.setHeader("Connection", "keep-alive");
- res.setHeader("Access-Control-Allow-Origin", "*");
-
- const BATCH_DELAY = 1000;
- let batchTimeout = null;
- let opInProgress = false;
-
- // Do things here
- const notifyChange = async () => {
- if (opInProgress) {
- // Get data
- const monitors = await this.db.getMonitorsByTeamId(req);
- res.write(`data: ${JSON.stringify({ monitors })}\n\n`);
- opInProgress = false;
- }
- batchTimeout = null;
- };
-
- const handleChange = () => {
- opInProgress = true;
- if (batchTimeout) clearTimeout(batchTimeout);
- batchTimeout = setTimeout(notifyChange, BATCH_DELAY);
- };
-
- const monitorStream = Monitor.watch(
- [{ $match: { operationType: { $in: ["insert", "update", "delete"] } } }],
- { fullDocument: "updateLookup" }
- );
-
- const checksStream = DistributedUptimeCheck.watch(
- [{ $match: { operationType: { $in: ["insert", "update", "delete"] } } }],
- { fullDocument: "updateLookup" }
- );
-
- monitorStream.on("change", handleChange);
- checksStream.on("change", handleChange);
-
- // Send initial data
- const monitors = await this.db.getMonitorsByTeamId(req);
- res.write(`data: ${JSON.stringify({ monitors })}\n\n`);
-
- // Handle client disconnect
- req.on("close", () => {
- if (batchTimeout) {
- clearTimeout(batchTimeout);
- }
- monitorStream.close();
- checksStream.close();
- clearInterval(keepAlive);
- });
-
- // Keep connection alive
- const keepAlive = setInterval(() => {
- res.write(": keepalive\n\n");
- }, 30000);
-
- // Clean up on close
- req.on("close", () => {
- clearInterval(keepAlive);
- });
- } catch (error) {
- console.log(error);
- next(handleError(error, SERVICE_NAME, "getDistributedUptimeMonitors"));
- }
- }
-
- async getDistributedUptimeMonitorDetails(req, res, next) {
- try {
- res.setHeader("Content-Type", "text/event-stream");
- res.setHeader("Cache-Control", "no-cache");
- res.setHeader("Connection", "keep-alive");
- res.setHeader("Access-Control-Allow-Origin", "*");
-
- const BATCH_DELAY = 1000;
- let batchTimeout = null;
- let opInProgress = false;
-
- // Do things here
- const notifyChange = async () => {
- try {
- if (opInProgress) {
- // Get data
- const monitor = await this.db.getDistributedUptimeDetailsById(req);
- res.write(`data: ${JSON.stringify({ monitor })}\n\n`);
- opInProgress = false;
- }
- batchTimeout = null;
- } catch (error) {
- console.error("Error in notifyChange:", error);
- opInProgress = false;
- batchTimeout = null;
- next(handleError(error, SERVICE_NAME, "getDistributedUptimeMonitorDetails"));
- }
- };
-
- const handleChange = () => {
- opInProgress = true;
- if (batchTimeout) clearTimeout(batchTimeout);
- batchTimeout = setTimeout(notifyChange, BATCH_DELAY);
- };
-
- const checksStream = DistributedUptimeCheck.watch(
- [{ $match: { operationType: { $in: ["insert", "update", "delete"] } } }],
- { fullDocument: "updateLookup" }
- );
-
- checksStream.on("change", handleChange);
-
- // Send initial data
- const monitor = await this.db.getDistributedUptimeDetailsById(req);
- res.write(`data: ${JSON.stringify({ monitor })}\n\n`);
-
- // Handle client disconnect
- req.on("close", () => {
- if (batchTimeout) {
- clearTimeout(batchTimeout);
- }
- checksStream.close();
- clearInterval(keepAlive);
- });
-
- // Keep connection alive
- const keepAlive = setInterval(() => {
- res.write(": keepalive\n\n");
- }, 30000);
-
- // Clean up on close
- req.on("close", () => {
- clearInterval(keepAlive);
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getDistributedUptimeMonitorDetails"));
- }
- }
-}
-export default DistributedUptimeController;
diff --git a/Server/controllers/inviteController.js b/Server/controllers/inviteController.js
deleted file mode 100644
index 40d045432..000000000
--- a/Server/controllers/inviteController.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import {
- inviteRoleValidation,
- inviteBodyValidation,
- inviteVerificationBodyValidation,
-} from "../validation/joi.js";
-import logger from "../utils/logger.js";
-import jwt from "jsonwebtoken";
-import { handleError, handleValidationError } from "./controllerUtils.js";
-import { getTokenFromHeaders } from "../utils/utils.js";
-
-const SERVICE_NAME = "inviteController";
-
-class InviteController {
- constructor(db, settingsService, emailService, stringService) {
- this.db = db;
- this.settingsService = settingsService;
- this.emailService = emailService;
- this.stringService = stringService;
- }
-
- /**
- * 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).
- */
- issueInvitation = 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);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- const inviteToken = await this.db.requestInviteToken({ ...req.body });
- const { clientHost } = this.settingsService.getSettings();
- this.emailService
- .buildAndSendEmail(
- "employeeActivationTemplate",
- {
- name: firstname,
- link: `${clientHost}/register/${inviteToken.token}`,
- },
- req.body.email,
- "Welcome to Uptime Monitor"
- )
- .catch((error) => {
- logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "issueInvitation",
- stack: error.stack,
- });
- });
-
- return res.success({
- msg: this.stringService.inviteIssued,
- data: inviteToken,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "inviteController"));
- }
- };
-
- inviteVerifyController = async (req, res, next) => {
- try {
- await inviteVerificationBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const invite = await this.db.getInviteToken(req.body.token);
-
- return res.success({
- msg: this.stringService.inviteVerified,
- data: invite,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "inviteVerifyController"));
- }
- };
-}
-
-export default InviteController;
diff --git a/Server/controllers/maintenanceWindowController.js b/Server/controllers/maintenanceWindowController.js
deleted file mode 100644
index a11f957ed..000000000
--- a/Server/controllers/maintenanceWindowController.js
+++ /dev/null
@@ -1,163 +0,0 @@
-import {
- createMaintenanceWindowBodyValidation,
- editMaintenanceWindowByIdParamValidation,
- editMaintenanceByIdWindowBodyValidation,
- getMaintenanceWindowByIdParamValidation,
- getMaintenanceWindowsByMonitorIdParamValidation,
- getMaintenanceWindowsByTeamIdQueryValidation,
- deleteMaintenanceWindowByIdParamValidation,
-} from "../validation/joi.js";
-import jwt from "jsonwebtoken";
-import { getTokenFromHeaders } from "../utils/utils.js";
-import { handleValidationError, handleError } from "./controllerUtils.js";
-
-const SERVICE_NAME = "maintenanceWindowController";
-
-class MaintenanceWindowController {
- constructor(db, settingsService, stringService) {
- this.db = db;
- this.settingsService = settingsService;
- this.stringService = stringService;
- }
-
- createMaintenanceWindows = async (req, res, next) => {
- try {
- await createMaintenanceWindowBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
- try {
- const token = getTokenFromHeaders(req.headers);
- const { jwtSecret } = this.settingsService.getSettings();
- const { teamId } = jwt.verify(token, jwtSecret);
- const monitorIds = req.body.monitors;
- const dbTransactions = monitorIds.map((monitorId) => {
- return this.db.createMaintenanceWindow({
- teamId,
- monitorId,
- name: req.body.name,
- active: req.body.active ? req.body.active : true,
- repeat: req.body.repeat,
- start: req.body.start,
- end: req.body.end,
- });
- });
- await Promise.all(dbTransactions);
-
- return res.success({
- msg: this.stringService.maintenanceWindowCreate,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "createMaintenanceWindow"));
- }
- };
-
- getMaintenanceWindowById = async (req, res, next) => {
- try {
- await getMaintenanceWindowByIdParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
- try {
- const maintenanceWindow = await this.db.getMaintenanceWindowById(req.params.id);
-
- return res.success({
- msg: this.stringService.maintenanceWindowGetById,
- data: maintenanceWindow,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMaintenanceWindowById"));
- }
- };
-
- getMaintenanceWindowsByTeamId = async (req, res, next) => {
- try {
- await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const token = getTokenFromHeaders(req.headers);
- const { jwtSecret } = this.settingsService.getSettings();
- const { teamId } = jwt.verify(token, jwtSecret);
- const maintenanceWindows = await this.db.getMaintenanceWindowsByTeamId(
- teamId,
- req.query
- );
-
- return res.success({
- msg: this.stringService.maintenanceWindowGetByTeam,
- data: maintenanceWindows,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByUserId"));
- }
- };
-
- getMaintenanceWindowsByMonitorId = async (req, res, next) => {
- try {
- await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId(
- req.params.monitorId
- );
-
- return res.success({
- msg: this.stringService.maintenanceWindowGetByUser,
- data: maintenanceWindows,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByMonitorId"));
- }
- };
-
- deleteMaintenanceWindow = async (req, res, next) => {
- try {
- await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
- try {
- await this.db.deleteMaintenanceWindowById(req.params.id);
- return res.success({
- msg: this.stringService.maintenanceWindowDelete,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow"));
- }
- };
-
- editMaintenanceWindow = async (req, res, next) => {
- try {
- await editMaintenanceWindowByIdParamValidation.validateAsync(req.params);
- await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
- try {
- const editedMaintenanceWindow = await this.db.editMaintenanceWindowById(
- req.params.id,
- req.body
- );
- return res.success({
- msg: this.stringService.maintenanceWindowEdit,
- data: editedMaintenanceWindow,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "editMaintenanceWindow"));
- }
- };
-}
-
-export default MaintenanceWindowController;
diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js
deleted file mode 100644
index 9e87e53e5..000000000
--- a/Server/controllers/monitorController.js
+++ /dev/null
@@ -1,548 +0,0 @@
-import {
- getMonitorByIdParamValidation,
- getMonitorByIdQueryValidation,
- getMonitorsByTeamIdParamValidation,
- getMonitorsByTeamIdQueryValidation,
- createMonitorBodyValidation,
- getMonitorURLByQueryValidation,
- editMonitorBodyValidation,
- pauseMonitorParamValidation,
- getMonitorStatsByIdParamValidation,
- getMonitorStatsByIdQueryValidation,
- getCertificateParamValidation,
- getHardwareDetailsByIdParamValidation,
- getHardwareDetailsByIdQueryValidation,
-} from "../validation/joi.js";
-import sslChecker from "ssl-checker";
-import jwt from "jsonwebtoken";
-import { getTokenFromHeaders } from "../utils/utils.js";
-import logger from "../utils/logger.js";
-import { handleError, handleValidationError } from "./controllerUtils.js";
-import axios from "axios";
-import seedDb from "../db/mongo/utils/seedDb.js";
-const SERVICE_NAME = "monitorController";
-
-class MonitorController {
- constructor(db, settingsService, jobQueue, stringService) {
- this.db = db;
- this.settingsService = settingsService;
- this.jobQueue = jobQueue;
- this.stringService = stringService;
- }
-
- /**
- * Returns all monitors
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @param {function} next
- * @returns {Promise}
- * @throws {Error}
- */
- getAllMonitors = async (req, res, next) => {
- try {
- const monitors = await this.db.getAllMonitors();
- return res.success({
- msg: this.stringService.monitorGetAll,
- data: monitors,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getAllMonitors"));
- }
- };
-
- /**
- * Returns all monitors with uptime stats for 1,7,30, and 90 days
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @param {function} next
- * @returns {Promise}
- * @throws {Error}
- */
- getAllMonitorsWithUptimeStats = async (req, res, next) => {
- try {
- const monitors = await this.db.getAllMonitorsWithUptimeStats();
- return res.success({
- msg: this.stringService.monitorGetAll,
- data: monitors,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getAllMonitorsWithUptimeStats"));
- }
- };
-
- getUptimeDetailsById = async (req, res, next) => {
- try {
- const monitor = await this.db.getUptimeDetailsById(req);
- return res.success({
- msg: this.stringService.monitorGetById,
- data: monitor,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMonitorDetailsById"));
- }
- };
-
- /**
- * Returns monitor stats for monitor with matching ID
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @param {function} next
- * @returns {Promise}
- * @throws {Error}
- */
- getMonitorStatsById = async (req, res, next) => {
- try {
- await getMonitorStatsByIdParamValidation.validateAsync(req.params);
- await getMonitorStatsByIdQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const monitorStats = await this.db.getMonitorStatsById(req);
- return res.success({
- msg: this.stringService.monitorStatsById,
- data: monitorStats,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMonitorStatsById"));
- }
- };
-
- /**
- * Get hardware details for a specific monitor by ID
- * @async
- * @param {Express.Request} req - Express request object containing monitorId in params
- * @param {Express.Response} res - Express response object
- * @param {Express.NextFunction} next - Express next middleware function
- * @returns {Promise}
- * @throws {Error} - Throws error if monitor not found or other database errors
- */
- getHardwareDetailsById = async (req, res, next) => {
- try {
- await getHardwareDetailsByIdParamValidation.validateAsync(req.params);
- await getHardwareDetailsByIdQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
- try {
- const monitor = await this.db.getHardwareDetailsById(req);
- return res.success({
- msg: this.stringService.monitorGetById,
- data: monitor,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getHardwareDetailsById"));
- }
- };
-
- getMonitorCertificate = async (req, res, next, fetchMonitorCertificate) => {
- try {
- await getCertificateParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- }
-
- try {
- const { monitorId } = req.params;
- const monitor = await this.db.getMonitorById(monitorId);
- const certificate = await fetchMonitorCertificate(sslChecker, monitor);
-
- return res.success({
- msg: this.stringService.monitorCertificate,
- data: {
- certificateDate: new Date(certificate.validTo),
- },
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMonitorCertificate"));
- }
- };
-
- /**
- * Retrieves a monitor by its ID.
- * @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 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).
- */
- getMonitorById = async (req, res, next) => {
- try {
- await getMonitorByIdParamValidation.validateAsync(req.params);
- await getMonitorByIdQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const monitor = await this.db.getMonitorById(req.params.monitorId);
- return res.success({
- msg: this.stringService.monitorGetById,
- data: monitor,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMonitorById"));
- }
- };
-
- /**
- * Creates a new monitor and adds it to the job queue.
- * @async
- * @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).
- */
- createMonitor = async (req, res, next) => {
- try {
- await createMonitorBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const notifications = req.body.notifications;
- const monitor = await this.db.createMonitor(req, res);
-
- if (notifications && notifications.length > 0) {
- monitor.notifications = await Promise.all(
- notifications.map(async (notification) => {
- notification.monitorId = monitor._id;
- return await this.db.createNotification(notification);
- })
- );
- }
-
- await monitor.save();
- // Add monitor to job queue
- this.jobQueue.addJob(monitor._id, monitor);
- return res.success({
- msg: this.stringService.monitorCreate,
- data: monitor,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "createMonitor"));
- }
- };
-
- /**
- * Checks if the endpoint can be resolved
- * @async
- * @param {Object} req - The Express request object.
- * @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 resolution result.
- * @throws {Error} If there is an error during the process, especially if there is a validation error (422).
- */
- checkEndpointResolution = async (req, res, next) => {
- try {
- await getMonitorURLByQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const { monitorURL } = req.query;
- const parsedUrl = new URL(monitorURL);
- const response = await axios.get(parsedUrl, {
- timeout: 5000,
- validateStatus: () => true,
- });
- return res.success({
- status: response.status,
- msg: response.statusText,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
- }
- };
-
- /**
- * Deletes a monitor by its ID and also deletes associated checks, alerts, and notifications.
- * @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 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.
- */
- deleteMonitor = async (req, res, next) => {
- try {
- await getMonitorByIdParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const monitor = await this.db.deleteMonitor(req, res, next);
- // Delete associated checks,alerts,and notifications
-
- try {
- const operations = [
- { name: "deleteJob", fn: () => this.jobQueue.deleteJob(monitor) },
- { name: "deleteChecks", fn: () => this.db.deleteChecks(monitor._id) },
- {
- name: "deletePageSpeedChecks",
- fn: () => this.db.deletePageSpeedChecksByMonitorId(monitor._id),
- },
- {
- name: "deleteNotifications",
- fn: () => this.db.deleteNotificationsByMonitorId(monitor._id),
- },
- {
- name: "deleteHardwareChecks",
- fn: () => this.db.deleteHardwareChecksByMonitorId(monitor._id),
- },
- {
- name: "deleteDistributedUptimeChecks",
- fn: () => this.db.deleteDistributedChecksByMonitorId(monitor._id),
- },
-
- // TODO We don't actually want to delete the status page if there are other monitors in it
- // We actually just want to remove the monitor being deleted from the status page.
- // Only delete he status page if there are no other monitors in it.
- {
- name: "deleteStatusPages",
- fn: () => this.db.deleteStatusPagesByMonitorId(monitor._id),
- },
- ];
- const results = await Promise.allSettled(operations.map((op) => op.fn()));
-
- results.forEach((result, index) => {
- if (result.status === "rejected") {
- const operationName = operations[index].name;
- logger.error({
- message: `Failed to ${operationName} for monitor ${monitor._id}`,
- service: SERVICE_NAME,
- method: "deleteMonitor",
- stack: result.reason.stack,
- });
- }
- });
- } catch (error) {
- logger.error({
- message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
- service: SERVICE_NAME,
- method: "deleteMonitor",
- stack: error.stack,
- });
- }
- return res.success({ msg: this.stringService.monitorDelete });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteMonitor"));
- }
- };
-
- /**
- * 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.
- */
- deleteAllMonitors = async (req, res, next) => {
- try {
- const token = getTokenFromHeaders(req.headers);
- const { jwtSecret } = this.settingsService.getSettings();
- const { teamId } = jwt.verify(token, jwtSecret);
- const { monitors, deletedCount } = await this.db.deleteAllMonitors(teamId);
- await Promise.all(
- monitors.map(async (monitor) => {
- try {
- await this.jobQueue.deleteJob(monitor);
- await this.db.deleteChecks(monitor._id);
- await this.db.deletePageSpeedChecksByMonitorId(monitor._id);
- await this.db.deleteNotificationsByMonitorId(monitor._id);
- } catch (error) {
- logger.error({
- message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
- service: SERVICE_NAME,
- method: "deleteAllMonitors",
- stack: error.stack,
- });
- }
- })
- );
- return res.success({ msg: `Deleted ${deletedCount} monitors` });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteAllMonitors"));
- }
- };
-
- /**
- * Edits a monitor by its ID, updates its notifications, and updates its job in the job queue.
- * @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 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).
- */
- editMonitor = async (req, res, next) => {
- try {
- await getMonitorByIdParamValidation.validateAsync(req.params);
- await editMonitorBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const { monitorId } = req.params;
- const monitorBeforeEdit = await this.db.getMonitorById(monitorId);
-
- // Get notifications from the request body
- const notifications = req.body.notifications;
-
- const editedMonitor = await this.db.editMonitor(monitorId, req.body);
-
- await this.db.deleteNotificationsByMonitorId(editedMonitor._id);
-
- await Promise.all(
- notifications &&
- notifications.map(async (notification) => {
- notification.monitorId = editedMonitor._id;
- await this.db.createNotification(notification);
- })
- );
-
- // Delete the old job(editedMonitor has the same ID as the old monitor)
- await this.jobQueue.deleteJob(monitorBeforeEdit);
- // Add the new job back to the queue
- await this.jobQueue.addJob(editedMonitor._id, editedMonitor);
- return res.success({
- msg: this.stringService.monitorEdit,
- data: editedMonitor,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "editMonitor"));
- }
- };
-
- /**
- * 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.
- */
- pauseMonitor = async (req, res, next) => {
- try {
- await pauseMonitorParamValidation.validateAsync(req.params);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- }
-
- try {
- const monitor = await this.db.getMonitorById(req.params.monitorId);
- monitor.isActive === true
- ? await this.jobQueue.deleteJob(monitor)
- : await this.jobQueue.addJob(monitor._id, monitor);
-
- monitor.isActive = !monitor.isActive;
- monitor.status = undefined;
- monitor.save();
- return res.success({
- msg: monitor.isActive
- ? this.stringService.monitorResume
- : this.stringService.monitorPause,
- data: monitor,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "pauseMonitor"));
- }
- };
-
- /**
- * 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.
- */
- addDemoMonitors = async (req, res, next) => {
- try {
- const token = getTokenFromHeaders(req.headers);
- const { jwtSecret } = this.settingsService.getSettings();
- const { _id, teamId } = jwt.verify(token, jwtSecret);
- const demoMonitors = await this.db.addDemoMonitors(_id, teamId);
- await Promise.all(
- demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor))
- );
-
- return res.success({
- msg: this.stringService.monitorDemoAdded,
- data: demoMonitors.length,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "addDemoMonitors"));
- }
- };
-
- getMonitorsByTeamId = async (req, res, next) => {
- try {
- await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
- await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- }
-
- try {
- const monitors = await this.db.getMonitorsByTeamId(req);
- return res.success({
- msg: this.stringService.monitorGetByTeamId,
- data: monitors,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId"));
- }
- };
-
- seedDb = async (req, res, next) => {
- try {
- const token = getTokenFromHeaders(req.headers);
- const { jwtSecret } = this.settingsService.getSettings();
- const { _id, teamId } = jwt.verify(token, jwtSecret);
- await seedDb(_id, teamId);
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "seedDb"));
- }
- };
-}
-
-export default MonitorController;
diff --git a/Server/controllers/notificationController.js b/Server/controllers/notificationController.js
deleted file mode 100644
index bce20ea13..000000000
--- a/Server/controllers/notificationController.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import {
- triggerNotificationBodyValidation,
-} from '../validation/joi.js';
-import { handleError, handleValidationError } from './controllerUtils.js';
-
-const SERVICE_NAME = "NotificationController";
-
-class NotificationController {
- constructor(notificationService, stringService) {
- this.notificationService = notificationService;
- this.stringService = stringService;
- this.triggerNotification = this.triggerNotification.bind(this);
- }
-
- async triggerNotification(req, res, next) {
- try {
- await triggerNotificationBodyValidation.validateAsync(req.body, {
- abortEarly: false,
- stripUnknown: true
- });
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const { monitorId, type, platform, config } = req.body;
-
- const networkResponse = {
- monitor: { _id: monitorId, name: "Test Monitor", url: "http://www.google.com" },
- status: false,
- statusChanged: true,
- prevStatus: true,
- };
-
- if (type === "webhook") {
- const notification = {
- type,
- platform,
- config
- };
-
- await this.notificationService.sendWebhookNotification(
- networkResponse,
- notification
- );
- }
-
- return res.success({
- msg: this.stringService.webhookSendSuccess
- });
-
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "triggerNotification"));
- }
- }
-}
-
-export default NotificationController;
\ No newline at end of file
diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js
deleted file mode 100644
index 330ade532..000000000
--- a/Server/controllers/queueController.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { handleError } from "./controllerUtils.js";
-
-const SERVICE_NAME = "JobQueueController";
-
-class JobQueueController {
- constructor(jobQueue, stringService) {
- this.jobQueue = jobQueue;
- this.stringService = stringService;
- }
-
- getMetrics = async (req, res, next) => {
- try {
- const metrics = await this.jobQueue.getMetrics();
- res.success({
- msg: this.stringService.queueGetMetrics,
- data: metrics,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getMetrics"));
- return;
- }
- };
-
- getJobs = async (req, res, next) => {
- try {
- const jobs = await this.jobQueue.getJobStats();
- return res.success({
- msg: this.stringService.queueGetMetrics,
- data: jobs,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getJobs"));
- return;
- }
- };
-
- addJob = async (req, res, next) => {
- try {
- await this.jobQueue.addJob(Math.random().toString(36).substring(7));
- return res.success({
- msg: this.stringService.queueAddJob,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "addJob"));
- return;
- }
- };
-
- obliterateQueue = async (req, res, next) => {
- try {
- await this.jobQueue.obliterate();
- return res.success({
- msg: this.stringService.queueObliterate,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "obliterateQueue"));
- return;
- }
- };
-}
-export default JobQueueController;
diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js
deleted file mode 100644
index 54643dfbb..000000000
--- a/Server/controllers/settingsController.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { updateAppSettingsBodyValidation } from "../validation/joi.js";
-import { handleValidationError, handleError } from "./controllerUtils.js";
-
-const SERVICE_NAME = "SettingsController";
-
-class SettingsController {
- constructor(db, settingsService, stringService) {
- this.db = db;
- this.settingsService = settingsService;
- this.stringService = stringService;
- }
-
- getAppSettings = async (req, res, next) => {
- try {
- const settings = { ...(await this.settingsService.getSettings()) };
- delete settings.jwtSecret;
- return res.success({
- msg: this.stringService.getAppSettings,
- data: settings,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getAppSettings"));
- }
- };
-
- updateAppSettings = async (req, res, next) => {
- try {
- await updateAppSettingsBodyValidation.validateAsync(req.body);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- await this.db.updateAppSettings(req.body);
- const updatedSettings = { ...(await this.settingsService.reloadSettings()) };
- delete updatedSettings.jwtSecret;
- return res.success({
- msg: this.stringService.updateAppSettings,
- data: updatedSettings,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "updateAppSettings"));
- }
- };
-}
-
-export default SettingsController;
diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js
deleted file mode 100644
index eb3e69484..000000000
--- a/Server/controllers/statusPageController.js
+++ /dev/null
@@ -1,120 +0,0 @@
-import { handleError, handleValidationError } from "./controllerUtils.js";
-import {
- createStatusPageBodyValidation,
- getStatusPageParamValidation,
- getStatusPageQueryValidation,
- imageValidation,
-} from "../validation/joi.js";
-
-const SERVICE_NAME = "statusPageController";
-
-class StatusPageController {
- constructor(db, stringService) {
- this.db = db;
- this.stringService = stringService;
- }
-
- createStatusPage = async (req, res, next) => {
- try {
- await createStatusPageBodyValidation.validateAsync(req.body);
- await imageValidation.validateAsync(req.file);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const statusPage = await this.db.createStatusPage(req.body, req.file);
- return res.success({
- msg: this.stringService.statusPageCreate,
- data: statusPage,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "createStatusPage"));
- }
- };
-
- updateStatusPage = async (req, res, next) => {
- try {
- await createStatusPageBodyValidation.validateAsync(req.body);
- await imageValidation.validateAsync(req.file);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const statusPage = await this.db.updateStatusPage(req.body, req.file);
- if (statusPage === null) {
- const error = new Error(this.stringService.statusPageNotFound);
- error.status = 404;
- throw error;
- }
- return res.success({
- msg: this.stringService.statusPageUpdate,
- data: statusPage,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "updateStatusPage"));
- }
- };
-
- getStatusPage = async (req, res, next) => {
- try {
- const statusPage = await this.db.getStatusPage();
- return res.success({
- msg: this.stringService.statusPageByUrl,
- data: statusPage,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getStatusPage"));
- }
- };
-
- getStatusPageByUrl = async (req, res, next) => {
- try {
- await getStatusPageParamValidation.validateAsync(req.params);
- await getStatusPageQueryValidation.validateAsync(req.query);
- } catch (error) {
- next(handleValidationError(error, SERVICE_NAME));
- return;
- }
-
- try {
- const statusPage = await this.db.getStatusPageByUrl(req.params.url, req.query.type);
- return res.success({
- msg: this.stringService.statusPageByUrl,
- data: statusPage,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getStatusPageByUrl"));
- }
- };
-
- getStatusPagesByTeamId = async (req, res, next) => {
- try {
- const teamId = req.params.teamId;
- const statusPages = await this.db.getStatusPagesByTeamId(teamId);
-
- return res.success({
- msg: this.stringService.statusPageByTeamId,
- data: statusPages,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "getStatusPageByTeamId"));
- }
- };
-
- deleteStatusPage = async (req, res, next) => {
- try {
- await this.db.deleteStatusPage(req.params.url);
- return res.success({
- msg: this.stringService.statusPageDelete,
- });
- } catch (error) {
- next(handleError(error, SERVICE_NAME, "deleteStatusPage"));
- }
- };
-}
-
-export default StatusPageController;
diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js
deleted file mode 100644
index 4e9b32992..000000000
--- a/Server/db/FakeDb.js
+++ /dev/null
@@ -1,138 +0,0 @@
-// **************************
-// The idea here is to provide a layer of abstraction between the database and whoever is using it.
-// Instead of directly calling mongoose methods, we can call the methods on the DB object.
-// If this were Typescript or Java or Golang an interface would be implemented to ensure the methods are available.
-// But we do the best we can with Javascript.
-//
-// If the methods are consistent all we have to do to swap out one DB for another is simply change the import.
-//
-// Example:
-// We start with the fake DB:
-//
-// const db = require("../db/FakeDb");
-// const monitors = await db.getAllMonitors();
-//
-// And when we want to swtich to a real DB, all we have to do is swap the import
-//
-// const db = require("../db/MongoDb");
-// const monitors = await db.getAllMonitors();
-//
-// 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 bcrypt = require("bcrypt");
-
-let FAKE_MONITOR_DATA = [];
-const USERS = [];
-
-const connect = async () => {
- try {
- await console.log("Connected to FakeDB");
- } catch (error) {
- console.error(error);
- }
-};
-
-const insertUser = async (req, res) => {
- try {
- const newUser = new UserModel({ ...req.body });
- const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait
- newUser.password = await bcrypt.hash(newUser.password, salt); // hash is also async, need to eitehr await or use hashSync
- USERS.push(newUser);
- const userToReturn = { ...newUser._doc };
- delete userToReturn.password;
- return userToReturn;
- } catch (error) {
- throw error;
- }
-};
-
-const getUserByEmail = async (req, res) => {
- const email = req.body.email;
- try {
- const idx = USERS.findIndex((user) => {
- return user.email === email;
- });
- if (idx === -1) {
- return null;
- }
- return USERS[idx];
- } catch (error) {
- throw new Error(`User with email ${email} not found`);
- }
-};
-
-const getAllMonitors = async () => {
- return FAKE_MONITOR_DATA;
-};
-
-const getMonitorById = async (monitorId) => {
- const idx = FAKE_MONITOR_DATA.findIndex((monitor) => {
- return monitor.id === monitorId;
- });
- if (idx === -1) {
- throw new Error(`Monitor with id ${monitorId} not found`);
- }
- return FAKE_MONITOR_DATA[idx];
-};
-
-const getMonitorsByUserId = async (userId) => {
- const userMonitors = FAKE_MONITOR_DATA.filter((monitor) => {
- return monitor.userId === userId;
- });
-
- if (userMonitors.length === 0) {
- throw new Error(`Monitors for user ${userId} not found`);
- }
- return userMonitors;
-};
-
-const createMonitor = async (req, res) => {
- const monitor = new Monitor(req.body);
- monitor.createdAt = Date.now();
- monitor.updatedAt = Date.now();
- FAKE_MONITOR_DATA.push(monitor);
- return monitor;
-};
-
-const deleteMonitor = async (req, res) => {
- const monitorId = req.params.monitorId;
- try {
- const monitor = getMonitorById(monitorId);
- FAKE_MONITOR_DATA = FAKE_MONITOR_DATA.filter((monitor) => {
- return monitor.id !== monitorId;
- });
- return monitor;
- } catch (error) {
- throw error;
- }
-};
-
-const editMonitor = async (req, res) => {
- const monitorId = req.params.monitorId;
- const idx = FAKE_MONITOR_DATA.findIndex((monitor) => {
- return monitor._id.toString() === monitorId;
- });
- const oldMonitor = FAKE_MONITOR_DATA[idx];
- const editedMonitor = new Monitor({ ...req.body });
- editedMonitor._id = oldMonitor._id;
- editedMonitor.userId = oldMonitor.userId;
- editedMonitor.updatedAt = Date.now();
- editedMonitor.createdAt = oldMonitor.createdAt;
- FAKE_MONITOR_DATA[idx] = editedMonitor;
- return FAKE_MONITOR_DATA[idx];
-};
-
-module.exports = {
- connect,
- insertUser,
- getUserByEmail,
- getAllMonitors,
- getMonitorById,
- getMonitorsByUserId,
- createMonitor,
- deleteMonitor,
- editMonitor,
-};
diff --git a/Server/db/models/AppSettings.js b/Server/db/models/AppSettings.js
deleted file mode 100644
index c4faa0881..000000000
--- a/Server/db/models/AppSettings.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import mongoose from "mongoose";
-
-const AppSettingsSchema = mongoose.Schema(
- {
- apiBaseUrl: {
- type: String,
- required: true,
- default: "http://localhost:5000/api/v1",
- },
- logLevel: {
- type: String,
- default: "debug",
- enum: ["debug", "none", "error", "warn"],
- },
- clientHost: {
- type: String,
- required: true,
- default: "http://localhost:5173",
- },
- jwtSecret: {
- type: String,
- required: true,
- default: "my_secret",
- },
- refreshTokenSecret: {
- type: String,
- required: true,
- default: "my_refresh_secret",
- },
- dbType: {
- type: String,
- required: true,
- default: "MongoDB",
- },
- dbConnectionString: {
- type: String,
- required: true,
- default: "mongodb://localhost:27017/uptime_db",
- },
- redisHost: {
- type: String,
- required: true,
- default: "127.0.0.1",
- },
- redisPort: {
- type: Number,
- default: "6379",
- },
- jwtTTL: {
- type: String,
- required: true,
- default: "2h",
- },
- refreshTokenTTL: {
- type: String,
- required: true,
- default: "7d",
- },
- pagespeedApiKey: {
- type: String,
- default: "",
- },
- systemEmailHost: {
- type: String,
- default: "smtp.gmail.com",
- },
- systemEmailPort: {
- type: Number,
- default: 465,
- },
- systemEmailAddress: {
- type: String,
- default: "",
- },
- systemEmailPassword: {
- type: String,
- default: "",
- },
- singleton: {
- type: Boolean,
- required: true,
- unique: true,
- default: true,
- },
- },
- {
- timestamps: true,
- }
-);
-
-export default mongoose.model("AppSettings", AppSettingsSchema);
diff --git a/Server/db/models/Check.js b/Server/db/models/Check.js
deleted file mode 100644
index 158749986..000000000
--- a/Server/db/models/Check.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import mongoose from "mongoose";
-
-const BaseCheckSchema = mongoose.Schema({
- /**
- * Reference to the associated Monitor document.
- *
- * @type {mongoose.Schema.Types.ObjectId}
- */
- monitorId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Monitor",
- immutable: true,
- index: true,
- },
-
- teamId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Team",
- immutable: true,
- index: true,
- },
- /**
- * Status of the check (true for up, false for down).
- *
- * @type {Boolean}
- */
- status: {
- type: Boolean,
- index: true,
- },
- /**
- * Response time of the check in milliseconds.
- *
- * @type {Number}
- */
- responseTime: {
- type: Number,
- },
- /**
- * HTTP status code received during the check.
- *
- * @type {Number}
- */
- statusCode: {
- type: Number,
- index: true,
- },
- /**
- * Message or description of the check result.
- *
- * @type {String}
- */
- message: {
- type: String,
- },
- /**
- * Expiry date of the check, auto-calculated to expire after 30 days.
- *
- * @type {Date}
- */
-
- expiry: {
- type: Date,
- default: Date.now,
- expires: 60 * 60 * 24 * 30, // 30 days
- },
-});
-
-/**
- * Check Schema for MongoDB collection.
- *
- * Represents a check associated with a monitor, storing information
- * about the status and response of a particular check event.
- */
-const CheckSchema = mongoose.Schema({ ...BaseCheckSchema.obj }, { timestamps: true });
-CheckSchema.index({ createdAt: 1 });
-CheckSchema.index({ monitorId: 1, createdAt: 1 });
-CheckSchema.index({ monitorId: 1, createdAt: -1 });
-CheckSchema.index({ teamId: 1, createdAt: -1 });
-CheckSchema.index({ teamId: 1 });
-
-export default mongoose.model("Check", CheckSchema);
-export { BaseCheckSchema };
diff --git a/Server/db/models/DistributedUptimeCheck.js b/Server/db/models/DistributedUptimeCheck.js
deleted file mode 100644
index adbeffd09..000000000
--- a/Server/db/models/DistributedUptimeCheck.js
+++ /dev/null
@@ -1,123 +0,0 @@
-import mongoose from "mongoose";
-
-import { BaseCheckSchema } from "./Check.js";
-
-// {
-// "id": "12123",
-// "result": {
-// "task_arrived": "2025-01-13T19:21:37.463466602Z",
-// "dns_start": "2025-01-14T00:21:33.1801319+05:00",
-// "dns_end": "2025-01-14T00:21:33.4582552+05:00",
-// "conn_start": "2025-01-14T00:21:33.1801319+05:00",
-// "conn_end": "2025-01-14T00:21:33.7076318+05:00",
-// "connect_start": "2025-01-14T00:21:33.4582552+05:00",
-// "connect_end": "2025-01-14T00:21:33.541899+05:00",
-// "tls_hand_shake_start": "2025-01-14T00:21:33.541899+05:00",
-// "tls_hand_shake_end": "2025-01-14T00:21:33.7076318+05:00",
-// "body_read_start": "2025-01-14T00:21:34.1894707+05:00",
-// "body_read_end": "2025-01-14T00:21:34.1894707+05:00",
-// "wrote_request": "2025-01-14T00:21:33.7076318+05:00",
-// "got_first_response_byte": "2025-01-14T00:21:34.1327652+05:00",
-// "first_byte_took": 425133400,
-// "body_read_took": 56030000,
-// "dns_took": 278123300,
-// "conn_took": 527499900,
-// "connect_took": 83643800,
-// "tls_took": 165732800,
-// "sni_name": "uprock.com",
-// "status_code": 200,
-// "body_size": 19320,
-// "request_header_size": 95,
-// "response_header_size": 246,
-// "response_headers": "X-Vercel-Id: bom1::iad1::sm87v-1736796096856-aec270c01f23\nDate: Mon, 13 Jan 2025 19:21:37 GMT\nServer: Vercel\nStrict-Transport-Security: max-age=63072000\nVary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url\nX-Matched-Path: /\nX-Powered-By: Next.js\nX-Vercel-Cache: MISS\nAge: 0\nCache-Control: private, no-cache, no-store, max-age=0, must-revalidate\nContent-Type: text/html; charset=utf-8",
-// "error": "",
-// "device_id": "d5f578e143a2cd603dd6bf5f846a86a538bde4a8fbe2ad1fca284ad9f033daf8",
-// "ip_address": "223.123.19.0",
-// "proof": "",
-// "created_at": "2025-01-13T19:21:37.463466912Z",
-// "continent": "AS",
-// "country_code": "PK",
-// "city": "Muzaffargarh",
-// "upt_burnt" : "0.01",
-// "location": {
-// "lat": 71.0968,
-// "lng": 30.0208
-// },
-// "payload": {
-// "callback": "https://webhook.site/2a15b0af-545a-4ac2-b913-153b97592d7a",
-// "x": "y"
-// }
-// }
-// }
-
-const LocationSchema = new mongoose.Schema(
- {
- lat: { type: Number, required: true },
- lng: { type: Number, required: true },
- },
- { _id: false }
-);
-
-const DistributedUptimeCheckSchema = mongoose.Schema(
- {
- ...BaseCheckSchema.obj,
- first_byte_took: {
- type: Number,
- required: false,
- },
- body_read_took: {
- type: Number,
- required: false,
- },
- dns_took: {
- type: Number,
- required: false,
- },
- conn_took: {
- type: Number,
- required: false,
- },
- connect_took: {
- type: Number,
- required: false,
- },
- tls_took: {
- type: Number,
- required: false,
- },
- location: {
- type: LocationSchema,
- required: false,
- },
- continent: {
- type: String,
- required: false,
- },
- countryCode: {
- type: String,
- required: false,
- },
- city: {
- type: String,
- required: false,
- },
- uptBurnt: {
- type: mongoose.Schema.Types.Decimal128,
- required: false,
- },
- },
- { timestamps: true }
-);
-
-DistributedUptimeCheckSchema.pre("save", function (next) {
- if (this.isModified("uptBurnt") && typeof this.uptBurnt === "string") {
- this.uptBurnt = mongoose.Types.Decimal128.fromString(this.uptBurnt);
- }
- next();
-});
-
-DistributedUptimeCheckSchema.index({ createdAt: 1 });
-DistributedUptimeCheckSchema.index({ monitorId: 1, createdAt: 1 });
-DistributedUptimeCheckSchema.index({ monitorId: 1, createdAt: -1 });
-
-export default mongoose.model("DistributedUptimeCheck", DistributedUptimeCheckSchema);
diff --git a/Server/db/models/HardwareCheck.js b/Server/db/models/HardwareCheck.js
deleted file mode 100644
index 76b9dfc2e..000000000
--- a/Server/db/models/HardwareCheck.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import mongoose from "mongoose";
-import { BaseCheckSchema } from "./Check.js";
-const cpuSchema = mongoose.Schema({
- physical_core: { type: Number, default: 0 },
- logical_core: { type: Number, default: 0 },
- frequency: { type: Number, default: 0 },
- temperature: { type: [Number], default: [] },
- free_percent: { type: Number, default: 0 },
- usage_percent: { type: Number, default: 0 },
-});
-
-const memorySchema = mongoose.Schema({
- total_bytes: { type: Number, default: 0 },
- available_bytes: { type: Number, default: 0 },
- used_bytes: { type: Number, default: 0 },
- usage_percent: { type: Number, default: 0 },
-});
-
-const diskSchema = mongoose.Schema({
- read_speed_bytes: { type: Number, default: 0 },
- write_speed_bytes: { type: Number, default: 0 },
- total_bytes: { type: Number, default: 0 },
- free_bytes: { type: Number, default: 0 },
- usage_percent: { type: Number, default: 0 },
-});
-
-const hostSchema = mongoose.Schema({
- os: { type: String, default: "" },
- platform: { type: String, default: "" },
- kernel_version: { type: String, default: "" },
-});
-
-const errorSchema = mongoose.Schema({
- metric: { type: [String], default: [] },
- err: { type: String, default: "" },
-});
-
-const HardwareCheckSchema = mongoose.Schema(
- {
- ...BaseCheckSchema.obj,
- cpu: {
- type: cpuSchema,
- default: () => ({}),
- },
- memory: {
- type: memorySchema,
- default: () => ({}),
- },
- disk: {
- type: [diskSchema],
- default: () => [],
- },
- host: {
- type: hostSchema,
- default: () => ({}),
- },
-
- errors: {
- type: [errorSchema],
- default: () => [],
- },
- },
- { timestamps: true }
-);
-
-HardwareCheckSchema.index({ createdAt: 1 });
-HardwareCheckSchema.index({ monitorId: 1, createdAt: 1 });
-HardwareCheckSchema.index({ monitorId: 1, createdAt: -1 });
-
-export default mongoose.model("HardwareCheck", HardwareCheckSchema);
diff --git a/Server/db/models/InviteToken.js b/Server/db/models/InviteToken.js
deleted file mode 100644
index 0c2402b07..000000000
--- a/Server/db/models/InviteToken.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import mongoose from "mongoose";
-const InviteTokenSchema = mongoose.Schema(
- {
- email: {
- type: String,
- required: true,
- unique: true,
- },
- teamId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Team",
- immutable: true,
- required: true,
- },
- role: {
- type: Array,
- required: true,
- },
- token: {
- type: String,
- required: true,
- },
- expiry: {
- type: Date,
- default: Date.now,
- expires: 3600,
- },
- },
- {
- timestamps: true,
- }
-);
-
-export default mongoose.model("InviteToken", InviteTokenSchema);
diff --git a/Server/db/models/MaintenanceWindow.js b/Server/db/models/MaintenanceWindow.js
deleted file mode 100644
index b68abb413..000000000
--- a/Server/db/models/MaintenanceWindow.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import mongoose from "mongoose";
-/**
- * MaintenanceWindow Schema
- * @module MaintenanceWindow
- * @typedef {Object} MaintenanceWindow
- * @property {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor. This is a reference to the Monitor model and is immutable.
- * @property {Boolean} active - Indicates whether the maintenance window is active.
- * @property {Number} repeat - Indicates how often this maintenance window should repeat.
- * @property {Date} start - The start date and time of the maintenance window.
- * @property {Date} end - The end date and time of the maintenance window.
- * @property {Date} expiry - The expiry date and time of the maintenance window. This is used for MongoDB's TTL index to automatically delete the document at this time. This field is set to the same value as `end` when `oneTime` is `true`.
- *
- * @example
- *
- * let maintenanceWindow = new MaintenanceWindow({
- * monitorId: monitorId,
- * active: active,
- * repeat: repeat,
- * start: start,
- * end: end,
- * });
- *
- * if (repeat === 0) {
- * maintenanceWindow.expiry = end;
- * }
- *
- */
-
-const MaintenanceWindow = mongoose.Schema(
- {
- monitorId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Monitor",
- immutable: true,
- },
- teamId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Team",
- immutable: true,
- },
- active: {
- type: Boolean,
- default: true,
- },
- name: {
- type: String,
- },
- repeat: {
- type: Number,
- },
- start: {
- type: Date,
- },
- end: {
- type: Date,
- },
- expiry: {
- type: Date,
- index: { expires: "0s" },
- },
- },
-
- {
- timestamps: true,
- }
-);
-
-export default mongoose.model("MaintenanceWindow", MaintenanceWindow);
diff --git a/Server/db/models/Monitor.js b/Server/db/models/Monitor.js
deleted file mode 100644
index eb5f67c1b..000000000
--- a/Server/db/models/Monitor.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import mongoose from "mongoose";
-
-const MonitorSchema = mongoose.Schema(
- {
- userId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "User",
- immutable: true,
- required: true,
- },
- teamId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Team",
- immutable: true,
- required: true,
- },
- name: {
- type: String,
- required: true,
- },
- description: {
- type: String,
- },
- status: {
- type: Boolean,
- default: undefined,
- },
- type: {
- type: String,
- required: true,
- enum: [
- "http",
- "ping",
- "pagespeed",
- "hardware",
- "docker",
- "port",
- "distributed_http",
- ],
- },
- jsonPath: {
- type: String,
- },
- expectedValue: {
- type: String,
- },
- matchMethod: {
- type: String,
- enum: [
- "equal",
- "include",
- "regex",
- ],
- },
- url: {
- type: String,
- required: true,
- },
- port: {
- type: Number,
- },
- isActive: {
- type: Boolean,
- default: true,
- },
- interval: {
- // in milliseconds
- type: Number,
- default: 60000,
- },
- uptimePercentage: {
- type: Number,
- default: undefined,
- },
- thresholds: {
- type: {
- usage_cpu: { type: Number },
- usage_memory: { type: Number },
- usage_disk: { type: Number },
- },
- _id: false,
- },
- notifications: [
- {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Notification",
- },
- ],
- secret: {
- type: String,
- },
- },
- {
- timestamps: true,
- }
-);
-
-export default mongoose.model("Monitor", MonitorSchema);
diff --git a/Server/db/models/Notification.js b/Server/db/models/Notification.js
deleted file mode 100644
index 53d9eb4a2..000000000
--- a/Server/db/models/Notification.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import mongoose from "mongoose";
-
-const configSchema = mongoose.Schema({
- webhookUrl: { type: String },
- botToken: { type: String },
- chatId: { type: String }
-}, { _id: false });
-
-const NotificationSchema = mongoose.Schema(
- {
- monitorId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Monitor",
- immutable: true,
- },
- type: {
- type: String,
- enum: ["email", "sms", "webhook"],
- },
- config: {
- type: configSchema,
- default: () => ({})
- },
- address: {
- type: String,
- },
- phone: {
- type: String,
- },
- alertThreshold: {
- type: Number,
- default: 5,
- },
- cpuAlertThreshold: {
- type: Number,
- default: function () {
- return this.alertThreshold;
- },
- },
- memoryAlertThreshold: {
- type: Number,
- default: function () {
- return this.alertThreshold;
- },
- },
- diskAlertThreshold: {
- type: Number,
- default: function () {
- return this.alertThreshold;
- },
- },
- tempAlertThreshold: {
- type: Number,
- default: function () {
- return this.alertThreshold;
- },
- },
- },
- {
- timestamps: true,
- }
-);
-
-NotificationSchema.pre("save", function (next) {
- if (!this.cpuAlertThreshold || this.isModified("alertThreshold")) {
- this.cpuAlertThreshold = this.alertThreshold;
- }
- if (!this.memoryAlertThreshold || this.isModified("alertThreshold")) {
- this.memoryAlertThreshold = this.alertThreshold;
- }
- if (!this.diskAlertThreshold || this.isModified("alertThreshold")) {
- this.diskAlertThreshold = this.alertThreshold;
- }
- if (!this.tempAlertThreshold || this.isModified("alertThreshold")) {
- this.tempAlertThreshold = this.alertThreshold;
- }
- next();
-});
-
-NotificationSchema.pre("findOneAndUpdate", function (next) {
- const update = this.getUpdate();
- if (update.alertThreshold) {
- update.cpuAlertThreshold = update.alertThreshold;
- update.memoryAlertThreshold = update.alertThreshold;
- update.diskAlertThreshold = update.alertThreshold;
- update.tempAlertThreshold = update.alertThreshold;
- }
- next();
-});
-
-export default mongoose.model("Notification", NotificationSchema);
diff --git a/Server/db/models/PageSpeedCheck.js b/Server/db/models/PageSpeedCheck.js
deleted file mode 100644
index c04df0931..000000000
--- a/Server/db/models/PageSpeedCheck.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import mongoose from "mongoose";
-import { BaseCheckSchema } from "./Check.js";
-import logger from "../../utils/logger.js";
-import { time } from "console";
-const AuditSchema = mongoose.Schema({
- id: { type: String, required: true },
- title: { type: String, required: true },
- description: { type: String, required: true },
- score: { type: Number, required: true },
- scoreDisplayMode: { type: String, required: true },
- displayValue: { type: String, required: true },
- numericValue: { type: Number, required: true },
- numericUnit: { type: String, required: true },
-});
-
-const AuditsSchema = mongoose.Schema({
- cls: {
- type: AuditSchema,
- required: true,
- },
- si: {
- type: AuditSchema,
- required: true,
- },
- fcp: {
- type: AuditSchema,
- required: true,
- },
- lcp: {
- type: AuditSchema,
- required: true,
- },
- tbt: {
- type: AuditSchema,
- required: true,
- },
-});
-
-/**
- * Mongoose schema for storing metrics from Google Lighthouse.
- * @typedef {Object} PageSpeedCheck
- * @property {mongoose.Schema.Types.ObjectId} monitorId - Reference to the Monitor model.
- * @property {number} accessibility - Accessibility score.
- * @property {number} bestPractices - Best practices score.
- * @property {number} seo - SEO score.
- * @property {number} performance - Performance score.
- */
-
-const PageSpeedCheck = mongoose.Schema(
- {
- ...BaseCheckSchema.obj,
- accessibility: {
- type: Number,
- required: true,
- },
- bestPractices: {
- type: Number,
- required: true,
- },
- seo: {
- type: Number,
- required: true,
- },
- performance: {
- type: Number,
- required: true,
- },
- audits: {
- type: AuditsSchema,
- required: true,
- },
- },
- { timestamps: true }
-);
-
-/**
- * Mongoose model for storing metrics from Google Lighthouse.
- * @typedef {mongoose.Model} LighthouseMetricsModel
- */
-
-PageSpeedCheck.pre("save", async function (next) {
- try {
- const monitor = await mongoose.model("Monitor").findById(this.monitorId);
- if (monitor && monitor.status !== this.status) {
- if (monitor.status === true && this.status === false) {
- logger.info({ message: "Monitor went down", monitorId: monitor._id });
- }
-
- if (monitor.status === false && this.status === true) {
- logger.info({ message: "Monitor went up", monitorId: monitor._id });
- }
- monitor.status = this.status;
- await monitor.save();
- }
- } catch (error) {
- logger.error({
- message: error.message,
- service: "PageSpeedCheck",
- method: "pre-save",
- stack: error.stack,
- });
- } finally {
- next();
- }
-});
-
-PageSpeedCheck.index({ createdAt: 1 });
-PageSpeedCheck.index({ monitorId: 1, createdAt: 1 });
-PageSpeedCheck.index({ monitorId: 1, createdAt: -1 });
-
-export default mongoose.model("PageSpeedCheck", PageSpeedCheck);
diff --git a/Server/db/models/RecoveryToken.js b/Server/db/models/RecoveryToken.js
deleted file mode 100644
index 2219a4bca..000000000
--- a/Server/db/models/RecoveryToken.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import mongoose from "mongoose";
-
-const RecoveryTokenSchema = mongoose.Schema(
- {
- email: {
- type: String,
- required: true,
- unique: true,
- },
- token: {
- type: String,
- required: true,
- },
- expiry: {
- type: Date,
- default: Date.now,
- expires: 600,
- },
- },
- {
- timestamps: true,
- }
-);
-
-export default mongoose.model("RecoveryToken", RecoveryTokenSchema);
diff --git a/Server/db/models/StatusPage.js b/Server/db/models/StatusPage.js
deleted file mode 100644
index bb1392d05..000000000
--- a/Server/db/models/StatusPage.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import mongoose from "mongoose";
-
-const StatusPageSchema = mongoose.Schema(
- {
- userId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "User",
- immutable: true,
- required: true,
- },
- teamId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Team",
- immutable: true,
- required: true,
- },
- type: {
- type: String,
- required: true,
- default: "uptime",
- enum: ["uptime", "distributed"],
- },
- companyName: {
- type: String,
- required: true,
- default: "",
- },
- url: {
- type: String,
- unique: true,
- required: true,
- default: "",
- },
- timezone: {
- type: String,
- required: false,
- },
- color: {
- type: String,
- required: false,
- default: "#4169E1",
- },
- monitors: [
- {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Monitor",
- required: true,
- },
- ],
- logo: {
- data: Buffer,
- contentType: String,
- },
- isPublished: {
- type: Boolean,
- default: false,
- },
- showCharts: {
- type: Boolean,
- default: true,
- },
- showUptimePercentage: {
- type: Boolean,
- default: true,
- },
- },
- { timestamps: true }
-);
-
-export default mongoose.model("StatusPage", StatusPageSchema);
diff --git a/Server/db/models/Team.js b/Server/db/models/Team.js
deleted file mode 100644
index 95337c201..000000000
--- a/Server/db/models/Team.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import mongoose from "mongoose";
-const TeamSchema = mongoose.Schema(
- {
- email: {
- type: String,
- required: true,
- unique: true,
- },
- },
- {
- timestamps: true,
- }
-);
-export default mongoose.model("Team", TeamSchema);
diff --git a/Server/db/models/User.js b/Server/db/models/User.js
deleted file mode 100644
index 3ddc5cee0..000000000
--- a/Server/db/models/User.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import mongoose from "mongoose";
-import bcrypt from "bcrypt";
-import logger from "../../utils/logger.js";
-
-const UserSchema = mongoose.Schema(
- {
- firstName: {
- type: String,
- required: true,
- },
- lastName: {
- type: String,
- required: true,
- },
- email: {
- type: String,
- required: true,
- unique: true,
- },
- password: {
- type: String,
- required: true,
- },
- avatarImage: {
- type: String,
- },
- profileImage: {
- data: Buffer,
- contentType: String,
- },
- isActive: {
- type: Boolean,
- default: true,
- },
- isVerified: {
- type: Boolean,
- default: false,
- },
- role: {
- type: [String],
- default: "user",
- enum: ["user", "admin", "superadmin", "demo"],
- },
- teamId: {
- type: mongoose.Schema.Types.ObjectId,
- ref: "Team",
- immutable: true,
- },
- checkTTL: {
- type: Number,
- },
- },
- {
- timestamps: true,
- }
-);
-
-UserSchema.pre("save", async function (next) {
- if (!this.isModified("password")) {
- next();
- }
- const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait
- this.password = await bcrypt.hash(this.password, salt); // hash is also async, need to eitehr await or use hashSync
- next();
-});
-
-UserSchema.pre("findOneAndUpdate", async function (next) {
- const update = this.getUpdate();
- if ("password" in update) {
- const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait
- update.password = await bcrypt.hash(update.password, salt); // hash is also async, need to eitehr await or use hashSync
- }
-
- next();
-});
-
-UserSchema.methods.comparePassword = async function (submittedPassword) {
- const res = await bcrypt.compare(submittedPassword, this.password);
- return res;
-};
-
-const User = mongoose.model("User", UserSchema);
-
-User.init().then(() => {
- logger.info({
- message: "User model initialized",
- service: "UserModel",
- method: "init",
- });
-});
-
-export default mongoose.model("User", UserSchema);
diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js
deleted file mode 100644
index 8f035c2dc..000000000
--- a/Server/db/mongo/MongoDB.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import mongoose from "mongoose";
-import UserModel from "../models/User.js";
-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";
-
-//****************************************
-// Distributed Checks
-//****************************************
-import * as distributedCheckModule from "./modules/distributedCheckModule.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";
-
-class MongoDB {
- static SERVICE_NAME = "MongoDB";
-
- constructor() {
- 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, distributedCheckModule);
- Object.assign(this, maintenanceWindowModule);
- Object.assign(this, notificationModule);
- Object.assign(this, settingsModule);
- Object.assign(this, statusPageModule);
- }
-
- connect = async () => {
- try {
- const connectionString =
- process.env.DB_CONNECTION_STRING || "mongodb://localhost:27017/uptime_db";
- await mongoose.connect(connectionString);
- // If there are no AppSettings, create one
- let appSettings = await AppSettings.find();
- if (appSettings.length === 0) {
- appSettings = new AppSettings({});
- await appSettings.save();
- }
- // Sync indexes
- const models = mongoose.modelNames();
- for (const modelName of models) {
- const model = mongoose.model(modelName);
- await model.syncIndexes();
- }
-
- logger.info({
- message: "Connected to MongoDB",
- service: this.SERVICE_NAME,
- method: "connect",
- });
- } catch (error) {
- logger.error({
- message: error.message,
- service: this.SERVICE_NAME,
- method: "connect",
- stack: error.stack,
- });
- throw error;
- }
- };
-
- disconnect = async () => {
- try {
- logger.info({ message: "Disconnecting from MongoDB" });
- await mongoose.disconnect();
- logger.info({ message: "Disconnected from MongoDB" });
- return;
- } catch (error) {
- logger.error({
- message: error.message,
- service: this.SERVICE_NAME,
- method: "disconnect",
- stack: error.stack,
- });
- }
- };
- checkSuperadmin = async (req, res) => {
- const superAdmin = await UserModel.findOne({ role: "superadmin" });
- if (superAdmin !== null) {
- return true;
- }
- return false;
- };
-}
-
-export default MongoDB;
diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js
deleted file mode 100644
index 6450b78a2..000000000
--- a/Server/db/mongo/modules/checkModule.js
+++ /dev/null
@@ -1,301 +0,0 @@
-import Check from "../../models/Check.js";
-import Monitor from "../../models/Monitor.js";
-import User from "../../models/User.js";
-import logger from "../../../utils/logger.js";
-import { ObjectId } from "mongodb";
-
-const SERVICE_NAME = "checkModule";
-const dateRangeLookup = {
- hour: new Date(new Date().setHours(new Date().getHours() - 1)),
- day: new Date(new Date().setDate(new Date().getDate() - 1)),
- week: new Date(new Date().setDate(new Date().getDate() - 7)),
- month: new Date(new Date().setMonth(new Date().getMonth() - 1)),
- 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}
- * @throws {Error}
- */
-
-const createCheck = async (checkData) => {
- try {
- const { monitorId, status } = checkData;
- const n = (await Check.countDocuments({ monitorId })) + 1;
- const check = await new Check({ ...checkData }).save();
- const monitor = await Monitor.findById(monitorId);
-
- if (!monitor) {
- logger.error({
- message: "Monitor not found",
- service: SERVICE_NAME,
- method: "createCheck",
- details: `monitor ID: ${monitorId}`,
- });
- return;
- }
-
- // Update uptime percentage
- if (monitor.uptimePercentage === undefined) {
- monitor.uptimePercentage = status === true ? 1 : 0;
- } else {
- monitor.uptimePercentage =
- (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n;
- }
-
- await monitor.save();
- return check;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "createCheck";
- throw error;
- }
-};
-
-/**
- * Get all checks for a monitor
- * @async
- * @param {string} monitorId
- * @returns {Promise>}
- * @throws {Error}
- */
-const getChecksByMonitor = async (req) => {
- try {
- const { monitorId } = req.params;
- let { sortOrder, dateRange, filter, page, rowsPerPage, status } = req.query;
- status = typeof status !== "undefined" ? false : undefined;
- page = parseInt(page);
- rowsPerPage = parseInt(rowsPerPage);
- // Match
- const matchStage = {
- monitorId: ObjectId.createFromHexString(monitorId),
- ...(typeof status !== "undefined" && { status }),
- ...(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;
- }
- }
-
- //Sort
- sortOrder = sortOrder === "asc" ? 1 : -1;
-
- // Pagination
- let skip = 0;
- if (page && rowsPerPage) {
- skip = page * rowsPerPage;
- }
- const checks = await Check.aggregate([
- { $match: matchStage },
- { $sort: { createdAt: sortOrder } },
- {
- $facet: {
- summary: [{ $count: "checksCount" }],
- checks: [{ $skip: skip }, { $limit: rowsPerPage }],
- },
- },
- {
- $project: {
- checksCount: {
- $ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
- },
- checks: {
- $ifNull: ["$checks", []],
- },
- },
- },
- ]);
-
- return checks[0];
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getChecks";
- throw error;
- }
-};
-
-const getChecksByTeam = async (req) => {
- try {
- let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query;
- page = parseInt(page);
- rowsPerPage = parseInt(rowsPerPage);
- const { teamId } = req.params;
- const matchStage = {
- teamId: ObjectId.createFromHexString(teamId),
- status: false,
- ...(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:
- 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 checks = await Check.aggregate([
- { $match: matchStage },
- { $sort: { createdAt: sortOrder } },
-
- {
- $facet: {
- summary: [{ $count: "checksCount" }],
- checks: [{ $skip: skip }, { $limit: rowsPerPage }],
- },
- },
- {
- $project: {
- checksCount: { $arrayElemAt: ["$summary.checksCount", 0] },
- checks: "$checks",
- },
- },
- ]);
-
- return checks[0];
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getChecksByTeam";
- 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 {
- const teamMonitors = await Monitor.find({ teamId: teamId });
- let totalDeletedCount = 0;
-
- await Promise.all(
- teamMonitors.map(async (monitor) => {
- const result = await Check.deleteMany({ monitorId: monitor._id });
- totalDeletedCount += result.deletedCount;
- monitor.status = true;
- await monitor.save();
- })
- );
-
- return totalDeletedCount;
- } 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,
- getChecksByMonitor,
- getChecksByTeam,
- deleteChecks,
- deleteChecksByTeamId,
- updateChecksTTL,
-};
diff --git a/Server/db/mongo/modules/distributedCheckModule.js b/Server/db/mongo/modules/distributedCheckModule.js
deleted file mode 100644
index 71238a847..000000000
--- a/Server/db/mongo/modules/distributedCheckModule.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js";
-const SERVICE_NAME = "distributedCheckModule";
-
-const createDistributedCheck = async (checkData) => {
- try {
- const check = await new DistributedUptimeCheck({ ...checkData }).save();
- return check;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "createCheck";
- throw error;
- }
-};
-
-const deleteDistributedChecksByMonitorId = async (monitorId) => {
- try {
- const result = await DistributedUptimeCheck.deleteMany({ monitorId });
- return result.deletedCount;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "deleteDistributedChecksByMonitorId";
- throw error;
- }
-};
-
-export { createDistributedCheck, deleteDistributedChecksByMonitorId };
diff --git a/Server/db/mongo/modules/hardwareCheckModule.js b/Server/db/mongo/modules/hardwareCheckModule.js
deleted file mode 100644
index 25f14bae1..000000000
--- a/Server/db/mongo/modules/hardwareCheckModule.js
+++ /dev/null
@@ -1,58 +0,0 @@
-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;
- }
-
- 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) {
- console.log("error creating hardware check", error);
- error.service = SERVICE_NAME;
- error.method = "createHardwareCheck";
- 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, deleteHardwareChecksByMonitorId };
diff --git a/Server/db/mongo/modules/inviteModule.js b/Server/db/mongo/modules/inviteModule.js
deleted file mode 100644
index f5c960697..000000000
--- a/Server/db/mongo/modules/inviteModule.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import InviteToken from "../../models/InviteToken.js";
-import crypto from "crypto";
-import ServiceRegistry from "../../../service/serviceRegistry.js";
-import StringService from "../../../service/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} 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} 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);
- }
- 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} 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);
- }
- return invite;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getInviteTokenAndDelete";
- throw error;
- }
-};
-
-export { requestInviteToken, getInviteToken, getInviteTokenAndDelete };
diff --git a/Server/db/mongo/modules/maintenanceWindowModule.js b/Server/db/mongo/modules/maintenanceWindowModule.js
deleted file mode 100644
index 072f83c3d..000000000
--- a/Server/db/mongo/modules/maintenanceWindowModule.js
+++ /dev/null
@@ -1,228 +0,0 @@
-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} The saved MaintenanceWindow document.
- * @throws {Error} If there is an error saving the document.
- * @example
- * const maintenanceWindowData = {
- * monitorId: 'yourMonitorId',
- * active: true,
- * oneTime: true,
- * start: new Date(),
- * end: new Date(),
- * };
- * createMaintenanceWindow(maintenanceWindowData)
- * .then(maintenanceWindow => console.log(maintenanceWindow))
- * .catch(error => console.error(error));
- */
-const createMaintenanceWindow = async (maintenanceWindowData) => {
- try {
- const maintenanceWindow = new 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;
- }
- const result = await maintenanceWindow.save();
- return result;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "createMaintenanceWindow";
- throw error;
- }
-};
-
-const getMaintenanceWindowById = async (maintenanceWindowId) => {
- try {
- const maintenanceWindow = await MaintenanceWindow.findById({
- _id: maintenanceWindowId,
- });
- 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>} An array of MaintenanceWindow documents.
- * @throws {Error} If there is an error retrieving the documents.
- * @example
- * getMaintenanceWindowByUserId(teamId)
- * .then(maintenanceWindows => console.log(maintenanceWindows))
- * .catch(error => console.error(error));
- */
-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;
- }
-
- // Sorting
- let sort = {};
- if (field !== undefined && order !== undefined) {
- sort[field] = order === "asc" ? 1 : -1;
- }
-
- const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery)
- .skip(skip)
- .limit(rowsPerPage)
- .sort(sort);
-
- return { maintenanceWindows, maintenanceWindowCount };
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMaintenanceWindowByUserId";
- 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>} An array of MaintenanceWindow documents.
- * @throws {Error} If there is an error retrieving the documents.
- * @example
- * getMaintenanceWindowsByMonitorId('monitorId')
- * .then(maintenanceWindows => console.log(maintenanceWindows))
- * .catch(error => console.error(error));
- */
-const getMaintenanceWindowsByMonitorId = async (monitorId) => {
- try {
- const maintenanceWindows = await MaintenanceWindow.find({
- monitorId: monitorId,
- });
- 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} The deleted MaintenanceWindow document.
- * @throws {Error} If there is an error deleting the document.
- * @example
- * deleteMaintenanceWindowById('maintenanceWindowId')
- * .then(maintenanceWindow => console.log(maintenanceWindow))
- * .catch(error => console.error(error));
- */
-const deleteMaintenanceWindowById = async (maintenanceWindowId) => {
- try {
- const maintenanceWindow =
- await MaintenanceWindow.findByIdAndDelete(maintenanceWindowId);
- 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} 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
- * deleteMaintenanceWindowByMonitorId('monitorId')
- * .then(result => console.log(result))
- * .catch(error => console.error(error));
- */
-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} 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
- * deleteMaintenanceWindowByUserId('userId')
- * .then(result => console.log(result))
- * .catch(error => console.error(error));
- */
-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 (maintenanceWindowId, maintenanceWindowData) => {
- try {
- const editedMaintenanceWindow = await MaintenanceWindow.findByIdAndUpdate(
- maintenanceWindowId,
- maintenanceWindowData,
- { new: true }
- );
- return editedMaintenanceWindow;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "editMaintenanceWindowById";
- throw error;
- }
-};
-
-export {
- createMaintenanceWindow,
- getMaintenanceWindowById,
- getMaintenanceWindowsByTeamId,
- getMaintenanceWindowsByMonitorId,
- deleteMaintenanceWindowById,
- deleteMaintenanceWindowByMonitorId,
- deleteMaintenanceWindowByUserId,
- editMaintenanceWindowById,
-};
diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js
deleted file mode 100644
index 14d883c23..000000000
--- a/Server/db/mongo/modules/monitorModule.js
+++ /dev/null
@@ -1,937 +0,0 @@
-import Monitor from "../../models/Monitor.js";
-import Check from "../../models/Check.js";
-import PageSpeedCheck from "../../models/PageSpeedCheck.js";
-import HardwareCheck from "../../models/HardwareCheck.js";
-import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js";
-import Notification from "../../models/Notification.js";
-import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js";
-import ServiceRegistry from "../../../service/serviceRegistry.js";
-import StringService from "../../../service/stringService.js";
-import fs from "fs";
-import path from "path";
-import { fileURLToPath } from "url";
-import {
- buildUptimeDetailsPipeline,
- buildHardwareDetailsPipeline,
- buildDistributedUptimeDetailsPipeline,
-} from "./monitorModuleQueries.js";
-import { ObjectId } from "mongodb";
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-
-const demoMonitorsPath = path.resolve(__dirname, "../../../utils/demoMonitors.json");
-const demoMonitors = JSON.parse(fs.readFileSync(demoMonitorsPath, "utf8"));
-
-const SERVICE_NAME = "monitorModule";
-
-const CHECK_MODEL_LOOKUP = {
- http: Check,
- ping: Check,
- docker: Check,
- port: Check,
- pagespeed: PageSpeedCheck,
- hardware: HardwareCheck,
-};
-
-/**
- * Get all monitors
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise>}
- * @throws {Error}
- */
-const getAllMonitors = async (req, res) => {
- try {
- const monitors = await Monitor.find();
- return monitors;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getAllMonitors";
- throw error;
- }
-};
-
-/**
- * Get all monitors with uptime stats for 1,7,30, and 90 days
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise>}
- * @throws {Error}
- */
-const getAllMonitorsWithUptimeStats = async () => {
- const timeRanges = {
- 1: new Date(Date.now() - 24 * 60 * 60 * 1000),
- 7: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
- 30: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
- 90: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000),
- };
-
- try {
- const monitors = await Monitor.find();
- const monitorsWithStats = await Promise.all(
- monitors.map(async (monitor) => {
- const model = CHECK_MODEL_LOOKUP[monitor.type];
-
- const uptimeStats = await Promise.all(
- Object.entries(timeRanges).map(async ([days, startDate]) => {
- const checks = await model.find({
- monitorId: monitor._id,
- createdAt: { $gte: startDate },
- });
- return [days, getUptimePercentage(checks)];
- })
- );
-
- return {
- ...monitor.toObject(),
- ...Object.fromEntries(uptimeStats),
- };
- })
- );
-
- return monitorsWithStats;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getAllMonitorsWithUptimeStats";
- throw error;
- }
-};
-
-/**
- * Function to calculate uptime duration based on the most recent check.
- * @param {Array} checks Array of check objects.
- * @returns {number} Uptime duration in ms.
- */
-const calculateUptimeDuration = (checks) => {
- if (!checks || checks.length === 0) {
- return 0;
- }
- const latestCheck = new Date(checks[0].createdAt);
- let latestDownCheck = 0;
-
- for (let i = checks.length - 1; i >= 0; i--) {
- if (checks[i].status === false) {
- latestDownCheck = new Date(checks[i].createdAt);
- break;
- }
- }
-
- // If no down check is found, uptime is from the last check to now
- if (latestDownCheck === 0) {
- return Date.now() - new Date(checks[checks.length - 1].createdAt);
- }
-
- // Otherwise the uptime is from the last check to the last down check
- return latestCheck - latestDownCheck;
-};
-
-/**
- * Helper function to get duration since last check
- * @param {Array} checks Array of check objects.
- * @returns {number} Timestamp of the most recent check.
- */
-const getLastChecked = (checks) => {
- if (!checks || checks.length === 0) {
- return 0; // Handle case when no checks are available
- }
- // Data is sorted newest->oldest, so last check is the most recent
- return new Date() - new Date(checks[0].createdAt);
-};
-
-/**
- * Helper function to get latestResponseTime
- * @param {Array} checks Array of check objects.
- * @returns {number} Timestamp of the most recent check.
- */
-const getLatestResponseTime = (checks) => {
- if (!checks || checks.length === 0) {
- return 0;
- }
-
- return checks[0]?.responseTime ?? 0;
-};
-
-/**
- * Helper function to get average response time
- * @param {Array} checks Array of check objects.
- * @returns {number} Timestamp of the most recent check.
- */
-const getAverageResponseTime = (checks) => {
- if (!checks || checks.length === 0) {
- return 0;
- }
-
- const validChecks = checks.filter((check) => typeof check.responseTime === "number");
- if (validChecks.length === 0) {
- return 0;
- }
- const aggResponseTime = validChecks.reduce((sum, check) => {
- return sum + check.responseTime;
- }, 0);
- return aggResponseTime / validChecks.length;
-};
-
-/**
- * Helper function to get percentage 24h uptime
- * @param {Array} checks Array of check objects.
- * @returns {number} Timestamp of the most recent check.
- */
-
-const getUptimePercentage = (checks) => {
- if (!checks || checks.length === 0) {
- return 0;
- }
- const upCount = checks.reduce((count, check) => {
- return check.status === true ? count + 1 : count;
- }, 0);
- return (upCount / checks.length) * 100;
-};
-
-/**
- * Helper function to get all incidents
- * @param {Array} checks Array of check objects.
- * @returns {number} Timestamp of the most recent check.
- */
-
-const getIncidents = (checks) => {
- if (!checks || checks.length === 0) {
- return 0; // Handle case when no checks are available
- }
- return checks.reduce((acc, check) => {
- return check.status === false ? (acc += 1) : acc;
- }, 0);
-};
-
-/**
- * Get date range parameters
- * @param {string} dateRange - 'day' | 'week' | 'month' | 'all'
- * @returns {Object} Start and end dates
- */
-const getDateRange = (dateRange) => {
- const startDates = {
- day: new Date(new Date().setDate(new Date().getDate() - 1)),
- week: new Date(new Date().setDate(new Date().getDate() - 7)),
- month: new Date(new Date().setMonth(new Date().getMonth() - 1)),
- all: new Date(0),
- };
- return {
- start: startDates[dateRange],
- end: new Date(),
- };
-};
-
-/**
- * Get checks for a monitor
- * @param {string} monitorId - Monitor ID
- * @param {Object} model - Check model to use
- * @param {Object} dateRange - Date range parameters
- * @param {number} sortOrder - Sort order (1 for ascending, -1 for descending)
- * @returns {Promise} All checks and date-ranged checks
- */
-const getMonitorChecks = async (monitorId, model, dateRange, sortOrder) => {
- const indexSpec = {
- monitorId: 1,
- createdAt: sortOrder, // This will be 1 or -1
- };
-
- const [checksAll, checksForDateRange] = await Promise.all([
- model.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(),
- model
- .find({
- monitorId,
- createdAt: { $gte: dateRange.start, $lte: dateRange.end },
- })
- .hint(indexSpec)
- .lean(),
- ]);
-
- return { checksAll, checksForDateRange };
-};
-
-/**
- * Process checks for display
- * @param {Array} checks - Checks to process
- * @param {number} numToDisplay - Number of checks to display
- * @param {boolean} normalize - Whether to normalize the data
- * @returns {Array} Processed checks
- */
-const processChecksForDisplay = (normalizeData, checks, numToDisplay, normalize) => {
- let processedChecks = checks;
- if (numToDisplay && checks.length > numToDisplay) {
- const n = Math.ceil(checks.length / numToDisplay);
- processedChecks = checks.filter((_, index) => index % n === 0);
- }
- return normalize ? normalizeData(processedChecks, 1, 100) : processedChecks;
-};
-
-/**
- * Get time-grouped checks based on date range
- * @param {Array} checks Array of check objects
- * @param {string} dateRange 'day' | 'week' | 'month'
- * @returns {Object} Grouped checks by time period
- */
-const groupChecksByTime = (checks, dateRange) => {
- return checks.reduce((acc, check) => {
- // Validate the date
- const checkDate = new Date(check.createdAt);
- if (Number.isNaN(checkDate.getTime()) || checkDate.getTime() === 0) {
- return acc;
- }
-
- const time =
- dateRange === "day"
- ? checkDate.setMinutes(0, 0, 0)
- : checkDate.toISOString().split("T")[0];
-
- if (!acc[time]) {
- acc[time] = { time, checks: [] };
- }
- acc[time].checks.push(check);
- return acc;
- }, {});
-};
-
-/**
- * Calculate aggregate stats for a group of checks
- * @param {Object} group Group of checks
- * @returns {Object} Stats for the group
- */
-const calculateGroupStats = (group) => {
- const totalChecks = group.checks.length;
-
- const checksWithResponseTime = group.checks.filter(
- (check) => typeof check.responseTime === "number" && !Number.isNaN(check.responseTime)
- );
-
- return {
- time: group.time,
- uptimePercentage: getUptimePercentage(group.checks),
- totalChecks,
- totalIncidents: group.checks.filter((check) => !check.status).length,
- avgResponseTime:
- checksWithResponseTime.length > 0
- ? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) /
- checksWithResponseTime.length
- : 0,
- };
-};
-
-/**
- * Get uptime details by monitor ID
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const getUptimeDetailsById = async (req) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- try {
- const { monitorId } = req.params;
- const monitor = await Monitor.findById(monitorId);
- if (monitor === null || monitor === undefined) {
- throw new Error(stringService.dbFindMonitorById(monitorId));
- }
-
- const { dateRange, normalize } = req.query;
- const dates = getDateRange(dateRange);
- const formatLookup = {
- day: "%Y-%m-%dT%H:00:00Z",
- week: "%Y-%m-%dT%H:00:00Z",
- month: "%Y-%m-%dT00:00:00Z",
- };
-
- const dateString = formatLookup[dateRange];
-
- const results = await Check.aggregate(
- buildUptimeDetailsPipeline(monitor, dates, dateString)
- );
-
- const monitorData = results[0];
- const normalizedGroupChecks = NormalizeDataUptimeDetails(
- monitorData.groupedChecks,
- 10,
- 100
- );
-
- const monitorStats = {
- ...monitor.toObject(),
- ...monitorData,
- groupedChecks: normalizedGroupChecks,
- };
-
- return monitorStats;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getUptimeDetailsById";
- throw error;
- }
-};
-
-const getDistributedUptimeDetailsById = async (req) => {
- try {
- const { monitorId } = req?.params ?? {};
- if (typeof monitorId === "undefined") {
- throw new Error();
- }
- const monitor = await Monitor.findById(monitorId);
- if (monitor === null || monitor === undefined) {
- throw new Error(this.stringService.dbFindMonitorById(monitorId));
- }
-
- const { dateRange, normalize } = req.query;
- const dates = getDateRange(dateRange);
- const formatLookup = {
- day: "%Y-%m-%dT%H:%M:00Z",
- week: "%Y-%m-%dT%H:00:00Z",
- month: "%Y-%m-%dT00:00:00Z",
- };
-
- const dateString = formatLookup[dateRange];
- const results = await DistributedUptimeCheck.aggregate(
- buildDistributedUptimeDetailsPipeline(monitor, dates, dateString)
- );
-
- const monitorData = results[0];
- const normalizedGroupChecks = NormalizeDataUptimeDetails(
- monitorData.groupedChecks,
- 10,
- 100
- );
-
- const monitorStats = {
- ...monitor.toObject(),
- ...monitorData,
- groupedChecks: normalizedGroupChecks,
- };
-
- return monitorStats;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getDistributedUptimeDetailsById";
- throw error;
- }
-};
-
-/**
- * Get stats by monitor ID
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const getMonitorStatsById = async (req) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- try {
- const { monitorId } = req.params;
-
- // Get monitor, if we can't find it, abort with error
- const monitor = await Monitor.findById(monitorId);
- if (monitor === null || monitor === undefined) {
- throw new Error(stringService.getDbFindMonitorById(monitorId));
- }
-
- // Get query params
- let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query;
- const sort = sortOrder === "asc" ? 1 : -1;
-
- // Get Checks for monitor in date range requested
- const model = CHECK_MODEL_LOOKUP[monitor.type];
- const dates = getDateRange(dateRange);
- const { checksAll, checksForDateRange } = await getMonitorChecks(
- monitorId,
- model,
- dates,
- sort
- );
-
- // Build monitor stats
- const monitorStats = {
- ...monitor.toObject(),
- uptimeDuration: calculateUptimeDuration(checksAll),
- lastChecked: getLastChecked(checksAll),
- latestResponseTime: getLatestResponseTime(checksAll),
- periodIncidents: getIncidents(checksForDateRange),
- periodTotalChecks: checksForDateRange.length,
- checks: processChecksForDisplay(
- NormalizeData,
- checksForDateRange,
- numToDisplay,
- normalize
- ),
- };
-
- if (
- monitor.type === "http" ||
- monitor.type === "ping" ||
- monitor.type === "docker" ||
- monitor.type === "port"
- ) {
- // HTTP/PING Specific stats
- monitorStats.periodAvgResponseTime = getAverageResponseTime(checksForDateRange);
- monitorStats.periodUptime = getUptimePercentage(checksForDateRange);
- const groupedChecks = groupChecksByTime(checksForDateRange, dateRange);
- monitorStats.aggregateData = Object.values(groupedChecks).map(calculateGroupStats);
- }
-
- return monitorStats;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMonitorStatsById";
- throw error;
- }
-};
-
-const getHardwareDetailsById = async (req) => {
- try {
- const { monitorId } = req.params;
- const { dateRange } = req.query;
- const monitor = await Monitor.findById(monitorId);
- const dates = getDateRange(dateRange);
- const formatLookup = {
- day: "%Y-%m-%dT%H:00:00Z",
- week: "%Y-%m-%dT%H:00:00Z",
- month: "%Y-%m-%dT00:00:00Z",
- };
- const dateString = formatLookup[dateRange];
- const hardwareStats = await HardwareCheck.aggregate(
- buildHardwareDetailsPipeline(monitor, dates, dateString)
- );
-
- const monitorStats = {
- ...monitor.toObject(),
- stats: hardwareStats[0],
- };
- return monitorStats;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getHardwareDetailsById";
- throw error;
- }
-};
-
-/**
- * Get a monitor by ID
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const getMonitorById = async (monitorId) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- try {
- const monitor = await Monitor.findById(monitorId);
- if (monitor === null || monitor === undefined) {
- const error = new Error(stringService.getDbFindMonitorById(monitorId));
- error.status = 404;
- throw error;
- }
- // Get notifications
- const notifications = await Notification.find({
- monitorId: monitorId,
- });
-
- // Update monitor with notifications and save
- monitor.notifications = notifications;
- await monitor.save();
-
- return monitor;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMonitorById";
- throw error;
- }
-};
-
-const getMonitorsByTeamId = async (req) => {
- 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 the match stage
- const matchStage = { teamId: ObjectId.createFromHexString(req.params.teamId) };
- if (type !== undefined) {
- matchStage.type = Array.isArray(type) ? { $in: type } : type;
- }
-
- const skip = page && rowsPerPage ? page * rowsPerPage : 0;
- const sort = { [field]: order === "asc" ? 1 : -1 };
- const results = await Monitor.aggregate([
- { $match: matchStage },
- {
- $facet: {
- summary: [
- {
- $group: {
- _id: null,
- totalMonitors: { $sum: 1 },
- upMonitors: {
- $sum: {
- $cond: [{ $eq: ["$status", true] }, 1, 0],
- },
- },
- downMonitors: {
- $sum: {
- $cond: [{ $eq: ["$status", false] }, 1, 0],
- },
- },
- pausedMonitors: {
- $sum: {
- $cond: [{ $eq: ["$isActive", false] }, 1, 0],
- },
- },
- },
- },
- {
- $project: {
- _id: 0,
- },
- },
- ],
- monitors: [
- { $sort: sort },
- {
- $project: {
- _id: 1,
- name: 1,
- },
- },
- ],
- filteredMonitors: [
- ...(filter !== undefined
- ? [
- {
- $match: {
- $or: [
- { name: { $regex: filter, $options: "i" } },
- { url: { $regex: filter, $options: "i" } },
- ],
- },
- },
- ]
- : []),
- { $sort: sort },
- { $skip: skip },
- ...(rowsPerPage ? [{ $limit: rowsPerPage }] : []),
- ...(limit
- ? [
- {
- $lookup: {
- from: "checks",
- let: { monitorId: "$_id" },
- pipeline: [
- {
- $match: {
- $expr: { $eq: ["$monitorId", "$$monitorId"] },
- },
- },
- { $sort: { createdAt: -1 } },
- ...(limit ? [{ $limit: limit }] : []),
- ],
- as: "standardchecks",
- },
- },
- ]
- : []),
- ...(limit
- ? [
- {
- $lookup: {
- from: "pagespeedchecks",
- let: { monitorId: "$_id" },
- pipeline: [
- {
- $match: {
- $expr: { $eq: ["$monitorId", "$$monitorId"] },
- },
- },
- { $sort: { createdAt: -1 } },
- ...(limit ? [{ $limit: limit }] : []),
- ],
- as: "pagespeedchecks",
- },
- },
- ]
- : []),
- ...(limit
- ? [
- {
- $lookup: {
- from: "hardwarechecks",
- let: { monitorId: "$_id" },
- pipeline: [
- {
- $match: {
- $expr: { $eq: ["$monitorId", "$$monitorId"] },
- },
- },
- { $sort: { createdAt: -1 } },
- ...(limit ? [{ $limit: limit }] : []),
- ],
- as: "hardwarechecks",
- },
- },
- ]
- : []),
- ...(limit
- ? [
- {
- $lookup: {
- from: "distributeduptimechecks",
- let: { monitorId: "$_id" },
- pipeline: [
- {
- $match: {
- $expr: { $eq: ["$monitorId", "$$monitorId"] },
- },
- },
- { $sort: { createdAt: -1 } },
- ...(limit ? [{ $limit: limit }] : []),
- ],
- as: "distributeduptimechecks",
- },
- },
- ]
- : []),
-
- {
- $addFields: {
- checks: {
- $switch: {
- branches: [
- {
- case: { $in: ["$type", ["http", "ping", "docker", "port"]] },
- then: "$standardchecks",
- },
- {
- case: { $eq: ["$type", "pagespeed"] },
- then: "$pagespeedchecks",
- },
- {
- case: { $eq: ["$type", "hardware"] },
- then: "$hardwarechecks",
- },
- {
- case: { $eq: ["$type", "distributed_http"] },
- then: "$distributeduptimechecks",
- },
- ],
- default: [],
- },
- },
- },
- },
- {
- $project: {
- standardchecks: 0,
- pagespeedchecks: 0,
- hardwarechecks: 0,
- },
- },
- ],
- },
- },
- {
- $project: {
- summary: { $arrayElemAt: ["$summary", 0] },
- filteredMonitors: 1,
- monitors: 1,
- },
- },
- ]);
-
- let { monitors, filteredMonitors, summary } = results[0];
- filteredMonitors = filteredMonitors.map((monitor) => {
- if (!monitor.checks) {
- return monitor;
- }
- monitor.checks = NormalizeData(monitor.checks, 10, 100);
- return monitor;
- });
- return { monitors, filteredMonitors, summary };
-};
-
-/**
- * Create a monitor
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const createMonitor = async (req, res) => {
- try {
- const monitor = new Monitor({ ...req.body });
- // Remove notifications fom monitor as they aren't needed here
- monitor.notifications = undefined;
- await monitor.save();
- return monitor;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "createMonitor";
- throw error;
- }
-};
-
-/**
- * Delete a monitor by ID
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const deleteMonitor = async (req, res) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
-
- const monitorId = req.params.monitorId;
- try {
- const monitor = await Monitor.findByIdAndDelete(monitorId);
- if (!monitor) {
- throw new Error(stringService.getDbFindMonitorById(monitorId));
- }
- return monitor;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "deleteMonitor";
- throw error;
- }
-};
-
-/**
- * DELETE ALL MONITORS (TEMP)
- */
-
-const deleteAllMonitors = async (teamId) => {
- try {
- const monitors = await Monitor.find({ teamId });
- const { deletedCount } = await Monitor.deleteMany({ teamId });
-
- return { monitors, deletedCount };
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "deleteAllMonitors";
- throw error;
- }
-};
-
-/**
- * Delete all monitors associated with a user ID
- * @async
- * @param {string} userId - The ID of the user whose monitors are to be deleted.
- * @returns {Promise} A promise that resolves when the operation is complete.
- */
-const deleteMonitorsByUserId = async (userId) => {
- try {
- const result = await Monitor.deleteMany({ userId: userId });
- return result;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "deleteMonitorsByUserId";
- throw error;
- }
-};
-
-/**
- * Edit a monitor by ID
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const editMonitor = async (candidateId, candidateMonitor) => {
- candidateMonitor.notifications = undefined;
-
- try {
- const editedMonitor = await Monitor.findByIdAndUpdate(candidateId, candidateMonitor, {
- new: true,
- });
- return editedMonitor;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "editMonitor";
- throw error;
- }
-};
-
-const addDemoMonitors = async (userId, teamId) => {
- try {
- const demoMonitorsToInsert = demoMonitors.map((monitor) => {
- return {
- userId,
- teamId,
- name: monitor.name,
- description: monitor.name,
- type: "http",
- url: monitor.url,
- interval: 60000,
- };
- });
- const insertedMonitors = await Monitor.insertMany(demoMonitorsToInsert);
- return insertedMonitors;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "addDemoMonitors";
- throw error;
- }
-};
-
-export {
- getAllMonitors,
- getAllMonitorsWithUptimeStats,
- getMonitorStatsById,
- getMonitorById,
- getMonitorsByTeamId,
- getUptimeDetailsById,
- getDistributedUptimeDetailsById,
- createMonitor,
- deleteMonitor,
- deleteAllMonitors,
- deleteMonitorsByUserId,
- editMonitor,
- addDemoMonitors,
- getHardwareDetailsById,
-};
-
-// Helper functions
-export {
- calculateUptimeDuration,
- getLastChecked,
- getLatestResponseTime,
- getAverageResponseTime,
- getUptimePercentage,
- getIncidents,
- getDateRange,
- getMonitorChecks,
- processChecksForDisplay,
- groupChecksByTime,
- calculateGroupStats,
-};
-
-// limit 25
-// page 1
-// rowsPerPage 25
-// filter undefined
-// field name
-// order asc
-// skip 25
-// sort { name: 1 }
-// filteredMonitors []
-
-// limit 25
-// page NaN
-// rowsPerPage 25
-// filter undefined
-// field name
-// order asc
-// skip 0
-// sort { name: 1 }
diff --git a/Server/db/mongo/modules/monitorModuleQueries.js b/Server/db/mongo/modules/monitorModuleQueries.js
deleted file mode 100644
index 489017f51..000000000
--- a/Server/db/mongo/modules/monitorModuleQueries.js
+++ /dev/null
@@ -1,721 +0,0 @@
-const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
- return [
- {
- $match: {
- monitorId: monitor._id,
- },
- },
- {
- $sort: {
- createdAt: 1,
- },
- },
- {
- $facet: {
- aggregateData: [
- {
- $group: {
- _id: null,
- avgResponseTime: {
- $avg: "$responseTime",
- },
- lastCheck: {
- $last: "$$ROOT",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- ],
- uptimeStreak: [
- {
- $sort: {
- createdAt: -1,
- },
- },
- {
- $group: {
- _id: null,
- checks: { $push: "$$ROOT" },
- },
- },
- {
- $project: {
- streak: {
- $reduce: {
- input: "$checks",
- initialValue: { checks: [], foundFalse: false },
- in: {
- $cond: [
- {
- $and: [
- { $not: "$$value.foundFalse" }, // stop reducing if a false check has been found
- { $eq: ["$$this.status", true] }, // continue reducing if current check true
- ],
- },
- // true case
- {
- checks: { $concatArrays: ["$$value.checks", ["$$this"]] },
- foundFalse: false, // Add the check to the streak
- },
- // false case
- {
- checks: "$$value.checks",
- foundFalse: true, // Mark that we found a false
- },
- ],
- },
- },
- },
- },
- },
- ],
- // For the response time chart, should return checks for date window
- // Grouped by: {day: hour}, {week: day}, {month: day}
- groupedChecks: [
- {
- $match: {
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: {
- $dateToString: {
- format: dateString,
- date: "$createdAt",
- },
- },
- avgResponseTime: {
- $avg: "$responseTime",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- {
- $sort: {
- _id: 1,
- },
- },
- ],
- // Average response time for the date window
- groupAvgResponseTime: [
- {
- $match: {
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: null,
- avgResponseTime: {
- $avg: "$responseTime",
- },
- },
- },
- ],
- // All UpChecks for the date window
- upChecks: [
- {
- $match: {
- status: true,
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: null,
- avgResponseTime: {
- $avg: "$responseTime",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- ],
- // Up checks grouped by: {day: hour}, {week: day}, {month: day}
- groupedUpChecks: [
- {
- $match: {
- status: true,
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: {
- $dateToString: {
- format: dateString,
- date: "$createdAt",
- },
- },
- totalChecks: {
- $sum: 1,
- },
- avgResponseTime: {
- $avg: "$responseTime",
- },
- },
- },
- {
- $sort: { _id: 1 },
- },
- ],
- // All down checks for the date window
- downChecks: [
- {
- $match: {
- status: false,
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: null,
- avgResponseTime: {
- $avg: "$responseTime",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- ],
- // Down checks grouped by: {day: hour}, {week: day}, {month: day} for the date window
- groupedDownChecks: [
- {
- $match: {
- status: false,
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: {
- $dateToString: {
- format: dateString,
- date: "$createdAt",
- },
- },
- totalChecks: {
- $sum: 1,
- },
- avgResponseTime: {
- $avg: "$responseTime",
- },
- },
- },
- {
- $sort: { _id: 1 },
- },
- ],
- },
- },
- {
- $project: {
- uptimeStreak: {
- $let: {
- vars: {
- checks: { $ifNull: [{ $first: "$uptimeStreak.streak.checks" }, []] },
- },
- in: {
- $cond: [
- { $eq: [{ $size: "$$checks" }, 0] },
- 0,
- {
- $subtract: [new Date(), { $last: "$$checks.createdAt" }],
- },
- ],
- },
- },
- },
- avgResponseTime: {
- $arrayElemAt: ["$aggregateData.avgResponseTime", 0],
- },
- totalChecks: {
- $arrayElemAt: ["$aggregateData.totalChecks", 0],
- },
- latestResponseTime: {
- $arrayElemAt: ["$aggregateData.lastCheck.responseTime", 0],
- },
- timeSinceLastCheck: {
- $let: {
- vars: {
- lastCheck: {
- $arrayElemAt: ["$aggregateData.lastCheck", 0],
- },
- },
- in: {
- $cond: [
- {
- $ifNull: ["$$lastCheck", false],
- },
- {
- $subtract: [new Date(), "$$lastCheck.createdAt"],
- },
- 0,
- ],
- },
- },
- },
- groupedChecks: "$groupedChecks",
- groupedAvgResponseTime: {
- $arrayElemAt: ["$groupAvgResponseTime", 0],
- },
- upChecks: {
- $arrayElemAt: ["$upChecks", 0],
- },
- groupedUpChecks: "$groupedUpChecks",
- downChecks: {
- $arrayElemAt: ["$downChecks", 0],
- },
- groupedDownChecks: "$groupedDownChecks",
- },
- },
- ];
-};
-
-const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
- return [
- {
- $match: {
- monitorId: monitor._id,
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $sort: {
- createdAt: 1,
- },
- },
- {
- $facet: {
- aggregateData: [
- {
- $group: {
- _id: null,
- latestCheck: {
- $last: "$$ROOT",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- ],
- upChecks: [
- {
- $match: {
- status: true,
- },
- },
- {
- $group: {
- _id: null,
- totalChecks: {
- $sum: 1,
- },
- },
- },
- ],
- checks: [
- {
- $limit: 1,
- },
- {
- $project: {
- diskCount: {
- $size: "$disk",
- },
- },
- },
- {
- $lookup: {
- from: "hardwarechecks",
- let: {
- diskCount: "$diskCount",
- },
- pipeline: [
- {
- $match: {
- $expr: {
- $and: [
- { $eq: ["$monitorId", monitor._id] },
- { $gte: ["$createdAt", dates.start] },
- { $lte: ["$createdAt", dates.end] },
- ],
- },
- },
- },
- {
- $group: {
- _id: {
- $dateToString: {
- format: dateString,
- date: "$createdAt",
- },
- },
- avgCpuUsage: {
- $avg: "$cpu.usage_percent",
- },
- avgMemoryUsage: {
- $avg: "$memory.usage_percent",
- },
- avgTemperatures: {
- $push: {
- $ifNull: ["$cpu.temperature", [0]],
- },
- },
- disks: {
- $push: "$disk",
- },
- },
- },
- {
- $project: {
- _id: 1,
- avgCpuUsage: 1,
- avgMemoryUsage: 1,
- avgTemperature: {
- $map: {
- input: {
- $range: [
- 0,
- {
- $size: {
- // Handle null temperatures array
- $ifNull: [
- { $arrayElemAt: ["$avgTemperatures", 0] },
- [0], // Default to single-element array if null
- ],
- },
- },
- ],
- },
- as: "index",
- in: {
- $avg: {
- $map: {
- input: "$avgTemperatures",
- as: "tempArray",
- in: {
- $ifNull: [
- { $arrayElemAt: ["$$tempArray", "$$index"] },
- 0, // Default to 0 if element is null
- ],
- },
- },
- },
- },
- },
- },
- disks: {
- $map: {
- input: {
- $range: [0, "$$diskCount"],
- },
- as: "diskIndex",
- in: {
- name: {
- $concat: [
- "disk",
- {
- $toString: "$$diskIndex",
- },
- ],
- },
- readSpeed: {
- $avg: {
- $map: {
- input: "$disks",
- as: "diskArray",
- in: {
- $arrayElemAt: [
- "$$diskArray.read_speed_bytes",
- "$$diskIndex",
- ],
- },
- },
- },
- },
- writeSpeed: {
- $avg: {
- $map: {
- input: "$disks",
- as: "diskArray",
- in: {
- $arrayElemAt: [
- "$$diskArray.write_speed_bytes",
- "$$diskIndex",
- ],
- },
- },
- },
- },
- totalBytes: {
- $avg: {
- $map: {
- input: "$disks",
- as: "diskArray",
- in: {
- $arrayElemAt: [
- "$$diskArray.total_bytes",
- "$$diskIndex",
- ],
- },
- },
- },
- },
- freeBytes: {
- $avg: {
- $map: {
- input: "$disks",
- as: "diskArray",
- in: {
- $arrayElemAt: ["$$diskArray.free_bytes", "$$diskIndex"],
- },
- },
- },
- },
- usagePercent: {
- $avg: {
- $map: {
- input: "$disks",
- as: "diskArray",
- in: {
- $arrayElemAt: [
- "$$diskArray.usage_percent",
- "$$diskIndex",
- ],
- },
- },
- },
- },
- },
- },
- },
- },
- },
- ],
- as: "hourlyStats",
- },
- },
- {
- $unwind: "$hourlyStats",
- },
- {
- $replaceRoot: {
- newRoot: "$hourlyStats",
- },
- },
- ],
- },
- },
- {
- $project: {
- aggregateData: {
- $arrayElemAt: ["$aggregateData", 0],
- },
- upChecks: {
- $arrayElemAt: ["$upChecks", 0],
- },
- checks: {
- $sortArray: {
- input: "$checks",
- sortBy: { _id: 1 },
- },
- },
- },
- },
- ];
-};
-
-const buildDistributedUptimeDetailsPipeline = (monitor, dates, dateString) => {
- return [
- {
- $match: {
- monitorId: monitor._id,
- },
- },
- {
- $sort: {
- createdAt: 1,
- },
- },
- {
- $facet: {
- aggregateData: [
- {
- $group: {
- _id: null,
- avgResponseTime: {
- $avg: "$responseTime",
- },
- lastCheck: {
- $last: "$$ROOT",
- },
- totalChecks: {
- $sum: 1,
- },
- uptBurnt: {
- $sum: "$uptBurnt",
- },
- },
- },
- ],
- // For the response time chart, should return checks for date window
- // Grouped by: {day: hour}, {week: day}, {month: day}
- groupedMapChecks: [
- {
- $match: {
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: {
- date: {
- $dateToString: {
- format: dateString,
- date: "$createdAt",
- },
- },
- city: "$city",
- lat: "$location.lat",
- lng: "$location.lng",
- },
- city: { $first: "$city" }, // Add this line to include city in output
- lat: { $first: "$location.lat" },
- lng: { $first: "$location.lng" },
- avgResponseTime: {
- $avg: "$responseTime",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- {
- $sort: {
- "_id.date": 1,
- },
- },
- ],
- groupedChecks: [
- {
- $match: {
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: {
- $dateToString: {
- format: dateString,
- date: "$createdAt",
- },
- },
- avgResponseTime: {
- $avg: "$responseTime",
- },
- totalChecks: {
- $sum: 1,
- },
- },
- },
- {
- $sort: {
- _id: 1,
- },
- },
- ],
- // Average response time for the date window
- groupAvgResponseTime: [
- {
- $match: {
- createdAt: { $gte: dates.start, $lte: dates.end },
- },
- },
- {
- $group: {
- _id: null,
- avgResponseTime: {
- $avg: "$responseTime",
- },
- },
- },
- ],
- latestChecks: [
- {
- $sort: { createdAt: -1 }, // Sort by newest first
- },
- {
- $limit: 5, // Get only the first 5 documents
- },
- {
- $project: {
- responseTime: 1,
- city: 1,
- countryCode: 1,
- uptBurnt: { $toString: "$uptBurnt" },
- },
- },
- ],
- },
- },
- {
- $project: {
- totalUptBurnt: {
- $toString: {
- $arrayElemAt: ["$aggregateData.uptBurnt", 0],
- },
- },
- avgResponseTime: {
- $arrayElemAt: ["$aggregateData.avgResponseTime", 0],
- },
- totalChecks: {
- $arrayElemAt: ["$aggregateData.totalChecks", 0],
- },
- latestResponseTime: {
- $arrayElemAt: ["$aggregateData.lastCheck.responseTime", 0],
- },
- timeSinceLastCheck: {
- $let: {
- vars: {
- lastCheck: {
- $arrayElemAt: ["$aggregateData.lastCheck", 0],
- },
- },
- in: {
- $cond: [
- {
- $ifNull: ["$$lastCheck", false],
- },
- {
- $subtract: [new Date(), "$$lastCheck.createdAt"],
- },
- 0,
- ],
- },
- },
- },
- groupedMapChecks: "$groupedMapChecks",
- groupedChecks: "$groupedChecks",
- groupedAvgResponseTime: {
- $arrayElemAt: ["$groupAvgResponseTime", 0],
- },
- latestChecks: "$latestChecks",
- },
- },
- ];
-};
-
-export {
- buildUptimeDetailsPipeline,
- buildHardwareDetailsPipeline,
- buildDistributedUptimeDetailsPipeline,
-};
diff --git a/Server/db/mongo/modules/notificationModule.js b/Server/db/mongo/modules/notificationModule.js
deleted file mode 100644
index aab2d03bd..000000000
--- a/Server/db/mongo/modules/notificationModule.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import Notification from "../../models/Notification.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} 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;
- }
-};
-
-/**
- * Retrieves notifications by monitor ID.
- * @param {mongoose.Types.ObjectId} monitorId - The ID of the monitor.
- * @returns {Promise>} 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;
- }
-};
-
-export {
- createNotification,
- getNotificationsByMonitorId,
- deleteNotificationsByMonitorId,
-};
diff --git a/Server/db/mongo/modules/pageSpeedCheckModule.js b/Server/db/mongo/modules/pageSpeedCheckModule.js
deleted file mode 100644
index 427893a96..000000000
--- a/Server/db/mongo/modules/pageSpeedCheckModule.js
+++ /dev/null
@@ -1,47 +0,0 @@
-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}
- * @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;
- }
-};
-
-/**
- * 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;
- }
-};
-
-export { createPageSpeedCheck, deletePageSpeedChecksByMonitorId };
diff --git a/Server/db/mongo/modules/recoveryModule.js b/Server/db/mongo/modules/recoveryModule.js
deleted file mode 100644
index 3b39e847c..000000000
--- a/Server/db/mongo/modules/recoveryModule.js
+++ /dev/null
@@ -1,88 +0,0 @@
-import UserModel from "../../models/User.js";
-import RecoveryToken from "../../models/RecoveryToken.js";
-import crypto from "crypto";
-import serviceRegistry from "../../../service/serviceRegistry.js";
-import StringService from "../../../service/stringService.js";
-
-const SERVICE_NAME = "recoveryModule";
-
-/**
- * Request a recovery token
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @throws {Error}
- */
-const requestRecoveryToken = async (req, res) => {
- try {
- // Delete any existing tokens
- await RecoveryToken.deleteMany({ email: req.body.email });
- let recoveryToken = new RecoveryToken({
- email: req.body.email,
- token: crypto.randomBytes(32).toString("hex"),
- });
- await recoveryToken.save();
- return recoveryToken;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "requestRecoveryToken";
- throw error;
- }
-};
-
-const validateRecoveryToken = async (req, res) => {
- const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
- try {
- const candidateToken = req.body.recoveryToken;
- const recoveryToken = await RecoveryToken.findOne({
- token: candidateToken,
- });
- if (recoveryToken !== null) {
- return recoveryToken;
- } else {
- throw new Error(stringService.dbTokenNotFound);
- }
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "validateRecoveryToken";
- throw error;
- }
-};
-
-const resetPassword = async (req, res) => {
- const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
- try {
- const newPassword = req.body.password;
-
- // Validate token again
- const recoveryToken = await validateRecoveryToken(req, res);
- const user = await UserModel.findOne({ email: recoveryToken.email });
-
- if (user === null) {
- throw new Error(stringService.dbUserNotFound);
- }
-
- const match = await user.comparePassword(newPassword);
- if (match === true) {
- throw new Error(stringService.dbResetPasswordBadMatch);
- }
-
- 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 };
diff --git a/Server/db/mongo/modules/settingsModule.js b/Server/db/mongo/modules/settingsModule.js
deleted file mode 100644
index 3b5f68e19..000000000
--- a/Server/db/mongo/modules/settingsModule.js
+++ /dev/null
@@ -1,30 +0,0 @@
-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;
- }
-};
-
-const updateAppSettings = async (newSettings) => {
- try {
- const settings = await AppSettings.findOneAndUpdate(
- {},
- { $set: newSettings },
- { new: true }
- );
- return settings;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "updateAppSettings";
- throw error;
- }
-};
-
-export { getAppSettings, updateAppSettings };
diff --git a/Server/db/mongo/modules/statusPageModule.js b/Server/db/mongo/modules/statusPageModule.js
deleted file mode 100644
index f970bbb3f..000000000
--- a/Server/db/mongo/modules/statusPageModule.js
+++ /dev/null
@@ -1,211 +0,0 @@
-import StatusPage from "../../models/StatusPage.js";
-import { NormalizeData } from "../../../utils/dataUtils.js";
-import ServiceRegistry from "../../../service/serviceRegistry.js";
-import StringService from "../../../service/stringService.js";
-
-const SERVICE_NAME = "statusPageModule";
-
-const createStatusPage = async (statusPageData, image) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
-
- try {
- const statusPage = new StatusPage({ ...statusPageData });
- 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;
- }
-};
-
-const updateStatusPage = async (statusPageData, image) => {
- try {
- if (image) {
- statusPageData.logo = {
- data: image.buffer,
- contentType: image.mimetype,
- };
- }
- 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) => {
- try {
- if (type === "distributed") {
- const statusPage = await StatusPage.aggregate([{ $match: { url } }]);
- return statusPage[0];
- } else {
- return getStatusPage(url);
- }
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getStatusPageByUrl";
- throw error;
- }
-};
-
-const getStatusPagesByTeamId = async (teamId) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
-
- 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 statusPageQuery = await StatusPage.aggregate([
- { $match: { url: url } },
- {
- $set: {
- originalMonitors: "$monitors",
- },
- },
- {
- $lookup: {
- from: "monitors",
- localField: "monitors",
- foreignField: "_id",
- as: "monitors",
- },
- },
- {
- $unwind: "$monitors",
- },
- {
- $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"],
- },
- },
- },
- {
- $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,
- url: 1,
- },
- monitors: {
- $sortArray: {
- input: "$monitors",
- sortBy: { orderIndex: 1 },
- },
- },
- },
- },
- ]);
- if (!statusPageQuery.length) {
- const error = new Error(stringService.statusPageNotFound);
- error.status = 404;
- throw error;
- }
-
- const { statusPage, monitors } = statusPageQuery[0];
-
- 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,
-};
diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js
deleted file mode 100644
index 4069be8c6..000000000
--- a/Server/db/mongo/modules/userModule.js
+++ /dev/null
@@ -1,232 +0,0 @@
-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/serviceRegistry.js";
-import StringService from "../../../service/stringService.js";
-const SERVICE_NAME = "userModule";
-
-/**
- * Insert a User
- * @async
- * @param {Express.Request} req
- * @param {Express.Response} res
- * @returns {Promise}
- * @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,
- };
-
- // 2. Get the avatar sized image
- const avatar = await generateAvatarImage(imageFile);
- userData.avatarImage = avatar;
- }
-
- // 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();
- }
-
- 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;
- }
- 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}
- * @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);
- }
- 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}
- * @throws {Error}
- */
-
-const updateUser = async (
- req,
- res,
- parseBoolean = ParseBoolean,
- generateAvatarImage = GenerateAvatarImage
-) => {
- const candidateUserId = req.params.userId;
- try {
- const candidateUser = { ...req.body };
- // ******************************************
- // Handle profile image
- // ******************************************
-
- if (parseBoolean(candidateUser.deleteProfileImage) === true) {
- candidateUser.profileImage = null;
- candidateUser.avatarImage = null;
- } else if (req.file) {
- // 1. Save the full size image
- candidateUser.profileImage = {
- data: req.file.buffer,
- contentType: req.file.mimetype,
- };
-
- // 2. Get the avatar sized image
- const avatar = await generateAvatarImage(req.file);
- candidateUser.avatarImage = avatar;
- }
-
- // ******************************************
- // End handling profile image
- // ******************************************
-
- const updatedUser = await UserModel.findByIdAndUpdate(
- candidateUserId,
- 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}
- * @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);
- }
- 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 (req, res) => {
- 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;
- }
-};
-
-export {
- insertUser,
- getUserByEmail,
- updateUser,
- deleteUser,
- deleteTeam,
- deleteAllOtherUsers,
- getAllUsers,
- logoutUser,
-};
diff --git a/Server/db/mongo/utils/seedDb.js b/Server/db/mongo/utils/seedDb.js
deleted file mode 100644
index cd4abb8d5..000000000
--- a/Server/db/mongo/utils/seedDb.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import Monitor from "../../models/Monitor.js";
-import Check from "../../models/Check.js";
-
-const generateRandomUrl = () => {
- const domains = ["example.com", "test.org", "demo.net", "sample.io", "mock.dev"];
- const paths = ["api", "status", "health", "ping", "check"];
- return `https://${domains[Math.floor(Math.random() * domains.length)]}/${paths[Math.floor(Math.random() * paths.length)]}`;
-};
-
-const generateChecks = (monitorId, teamId, count) => {
- const checks = [];
- const endTime = new Date(Date.now() - 10 * 60 * 1000); // 10 minutes ago
- const startTime = new Date(endTime - count * 60 * 1000); // count minutes before endTime
-
- for (let i = 0; i < count; i++) {
- const timestamp = new Date(startTime.getTime() + i * 60 * 1000);
- const status = Math.random() > 0.05; // 95% chance of being up
-
- checks.push({
- monitorId,
- teamId,
- status,
- responseTime: Math.floor(Math.random() * 1000), // Random response time between 0-1000ms
- createdAt: timestamp,
- updatedAt: timestamp,
- });
- }
-
- return checks;
-};
-
-const seedDb = async (userId, teamId) => {
- try {
- console.log("Deleting all monitors and checks");
- await Monitor.deleteMany({});
- await Check.deleteMany({});
- console.log("Adding monitors");
- for (let i = 0; i < 300; i++) {
- const monitor = await Monitor.create({
- name: `Monitor ${i}`,
- url: generateRandomUrl(),
- type: "http",
- userId,
- teamId,
- interval: 60000,
- active: false,
- });
- console.log(`Adding monitor and checks for monitor ${i}`);
- const checks = generateChecks(monitor._id, teamId, 10000);
- await Check.insertMany(checks);
- }
- } catch (error) {
- console.error(error);
- }
-};
-
-export default seedDb;
diff --git a/Server/eslint.config.js b/Server/eslint.config.js
deleted file mode 100644
index 13f1fc038..000000000
--- a/Server/eslint.config.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import globals from "globals";
-import pluginJs from "@eslint/js";
-import mochaPlugin from "eslint-plugin-mocha";
-
-/*
-Please do not forget to look at the latest eslint configurations and rules.
-ESlint v9 configuration is different than v8.
-"https://eslint.org/docs/latest/use/configure/"
-*/
-
-/** @type {import('eslint').Linter.Config[]} */
-export default [
- {
- languageOptions: {
- globals: {
- ...globals.node, // Add Node.js globals
- ...globals.chai, // Add Chai globals
- },
- ecmaVersion: 2023,
- sourceType: "module",
- },
- },
- pluginJs.configs.recommended, // Core JS rules
- mochaPlugin.configs.flat.recommended, // Mocha rules
- {
- rules: {
- "mocha/max-top-level-suites": "warn", // Warn if there are too many top-level suites instead of failing
- },
- },
-];
diff --git a/Server/index.js b/Server/index.js
deleted file mode 100644
index b28fd5a82..000000000
--- a/Server/index.js
+++ /dev/null
@@ -1,320 +0,0 @@
-import path from "path";
-import fs from "fs";
-import swaggerUi from "swagger-ui-express";
-
-import express from "express";
-import helmet from "helmet";
-import cors from "cors";
-import logger from "./utils/logger.js";
-import { verifyJWT } from "./middleware/verifyJWT.js";
-import { handleErrors } from "./middleware/handleErrors.js";
-import { responseHandler } from "./middleware/responseHandler.js";
-import { fileURLToPath } from "url";
-
-import AuthRoutes from "./routes/authRoute.js";
-import AuthController from "./controllers/authController.js";
-
-import InviteRoutes from "./routes/inviteRoute.js";
-import InviteController from "./controllers/inviteController.js";
-
-import MonitorRoutes from "./routes/monitorRoute.js";
-import MonitorController from "./controllers/monitorController.js";
-
-import CheckRoutes from "./routes/checkRoute.js";
-import CheckController from "./controllers/checkController.js";
-
-import MaintenanceWindowRoutes from "./routes/maintenanceWindowRoute.js";
-import MaintenanceWindowController from "./controllers/maintenanceWindowController.js";
-
-import SettingsRoutes from "./routes/settingsRoute.js";
-import SettingsController from "./controllers/settingsController.js";
-
-import StatusPageRoutes from "./routes/statusPageRoute.js";
-import StatusPageController from "./controllers/statusPageController.js";
-
-import QueueRoutes from "./routes/queueRoute.js";
-import QueueController from "./controllers/queueController.js";
-
-import DistributedUptimeRoutes from "./routes/distributedUptimeRoute.js";
-import DistributedUptimeController from "./controllers/distributedUptimeController.js";
-
-import NotificationRoutes from "./routes/notificationRoute.js";
-
-import NotificationController from "./controllers/notificationController.js";
-
-//JobQueue service and dependencies
-import JobQueue from "./service/jobQueue.js";
-import { Queue, Worker } from "bullmq";
-
-//Network service and dependencies
-import NetworkService from "./service/networkService.js";
-import axios from "axios";
-import ping from "ping";
-import http from "http";
-import Docker from "dockerode";
-import net from "net";
-// Email service and dependencies
-import EmailService from "./service/emailService.js";
-import nodemailer from "nodemailer";
-import pkg from "handlebars";
-const { compile } = pkg;
-import mjml2html from "mjml";
-
-// Settings Service and dependencies
-import SettingsService from "./service/settingsService.js";
-import AppSettings from "./db/models/AppSettings.js";
-
-// Status Service and dependencies
-import StatusService from "./service/statusService.js";
-
-// Notification Service and dependencies
-import NotificationService from "./service/notificationService.js";
-
-// Service Registry
-import ServiceRegistry from "./service/serviceRegistry.js";
-
-import MongoDB from "./db/mongo/MongoDB.js";
-
-import IORedis from "ioredis";
-
-import TranslationService from "./service/translationService.js";
-import languageMiddleware from "./middleware/languageMiddleware.js";
-import StringService from "./service/stringService.js";
-
-const SERVICE_NAME = "Server";
-const SHUTDOWN_TIMEOUT = 1000;
-let isShuttingDown = false;
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-const openApiSpec = JSON.parse(
- fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8")
-);
-
-let server;
-
-const PORT = 5000;
-
-const shutdown = async () => {
- if (isShuttingDown) {
- return;
- }
- isShuttingDown = true;
- logger.info({ message: "Attempting graceful shutdown" });
- setTimeout(async () => {
- logger.error({
- message: "Could not shut down in time, forcing shutdown",
- service: SERVICE_NAME,
- method: "shutdown",
- });
- // flush Redis
- const settings =
- ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings() || {};
-
- const { redisHost = "127.0.0.1", redisPort = 6379 } = settings;
- const redis = new IORedis({
- host: redisHost,
- port: redisPort,
- });
- logger.info({ message: "Flushing Redis" });
- await redis.flushall();
- logger.info({ message: "Redis flushed" });
- process.exit(1);
- }, SHUTDOWN_TIMEOUT);
- try {
- server.close();
- await ServiceRegistry.get(JobQueue.SERVICE_NAME).obliterate();
- await ServiceRegistry.get(MongoDB.SERVICE_NAME).disconnect();
- logger.info({ message: "Graceful shutdown complete" });
- process.exit(0);
- } catch (error) {
- logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "shutdown",
- stack: error.stack,
- });
- }
-};
-// Need to wrap server setup in a function to handle async nature of JobQueue
-const startApp = async () => {
- const app = express();
-
- // Create and Register Primary services
- const translationService = new TranslationService(logger);
- const stringService = new StringService(translationService);
- ServiceRegistry.register(StringService.SERVICE_NAME, stringService);
-
- // Create DB
- const db = new MongoDB();
- await db.connect();
-
- // Create services
- const networkService = new NetworkService(
- axios,
- ping,
- logger,
- http,
- Docker,
- net,
- stringService
- );
- const settingsService = new SettingsService(AppSettings);
- await settingsService.loadSettings();
- const emailService = new EmailService(
- settingsService,
- fs,
- path,
- compile,
- mjml2html,
- nodemailer,
- logger
- );
- const statusService = new StatusService(db, logger);
- const notificationService = new NotificationService(emailService, db, logger, networkService, stringService);
-
- const jobQueue = new JobQueue(
- db,
- statusService,
- networkService,
- notificationService,
- settingsService,
- stringService,
- logger,
- Queue,
- Worker
- );
-
- // Register services
- ServiceRegistry.register(JobQueue.SERVICE_NAME, jobQueue);
- ServiceRegistry.register(MongoDB.SERVICE_NAME, db);
- ServiceRegistry.register(SettingsService.SERVICE_NAME, settingsService);
- ServiceRegistry.register(EmailService.SERVICE_NAME, emailService);
- ServiceRegistry.register(NetworkService.SERVICE_NAME, networkService);
- ServiceRegistry.register(StatusService.SERVICE_NAME, statusService);
- ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService);
- ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService);
-
- await translationService.initialize();
-
- server = app.listen(PORT, () => {
- logger.info({ message: `server started on port:${PORT}` });
- });
-
- process.on("SIGUSR2", shutdown);
- process.on("SIGINT", shutdown);
- process.on("SIGTERM", shutdown);
-
- //Create controllers
- const authController = new AuthController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(EmailService.SERVICE_NAME),
- ServiceRegistry.get(JobQueue.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const monitorController = new MonitorController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(JobQueue.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const settingsController = new SettingsController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const checkController = new CheckController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const inviteController = new InviteController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(EmailService.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const maintenanceWindowController = new MaintenanceWindowController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(SettingsService.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const queueController = new QueueController(
- ServiceRegistry.get(JobQueue.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const statusPageController = new StatusPageController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const notificationController = new NotificationController(
- ServiceRegistry.get(NotificationService.SERVICE_NAME),
- ServiceRegistry.get(StringService.SERVICE_NAME)
- );
-
- const distributedUptimeController = new DistributedUptimeController(
- ServiceRegistry.get(MongoDB.SERVICE_NAME),
- http,
- ServiceRegistry.get(StatusService.SERVICE_NAME)
- );
-
- //Create routes
- const authRoutes = new AuthRoutes(authController);
- const monitorRoutes = new MonitorRoutes(monitorController);
- const settingsRoutes = new SettingsRoutes(settingsController);
- const checkRoutes = new CheckRoutes(checkController);
- const inviteRoutes = new InviteRoutes(inviteController);
- const maintenanceWindowRoutes = new MaintenanceWindowRoutes(
- maintenanceWindowController
- );
- const queueRoutes = new QueueRoutes(queueController);
- const statusPageRoutes = new StatusPageRoutes(statusPageController);
- const distributedUptimeRoutes = new DistributedUptimeRoutes(
- distributedUptimeController
- );
-
- const notificationRoutes = new NotificationRoutes(notificationController);
-
- // Init job queue
- await jobQueue.initJobQueue();
- // Middleware
- app.use(cors());
- app.use(express.json());
- app.use(helmet());
- app.use(languageMiddleware(stringService, translationService));
- // Swagger UI
- app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec));
-
- //routes
- app.use(responseHandler);
-
- app.use("/api/v1/auth", authRoutes.getRouter());
- app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter());
- app.use("/api/v1/invite", inviteRoutes.getRouter());
- app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter());
- app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter());
- app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter());
- app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter());
- app.use("/api/v1/distributed-uptime", distributedUptimeRoutes.getRouter());
- app.use("/api/v1/status-page", statusPageRoutes.getRouter());
- app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());
- app.use(handleErrors);
-};
-
-startApp().catch((error) => {
- logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "startApp",
- stack: error.stack,
- });
- process.exit(1);
-});
diff --git a/Server/locales/en.json b/Server/locales/en.json
deleted file mode 100644
index 8657deb73..000000000
--- a/Server/locales/en.json
+++ /dev/null
@@ -1,154 +0,0 @@
-{
- "dontHaveAccount": "Don't have account",
- "email": "E-mail",
- "forgotPassword": "Forgot Password",
- "password": "password",
- "signUp": "Sign up",
- "submit": "Submit",
- "title": "Title",
- "continue": "Continue",
- "enterEmail": "Enter your email",
- "authLoginTitle": "Log In",
- "authLoginEnterPassword": "Enter your password",
- "commonPassword": "Password",
- "commonBack": "Back",
- "authForgotPasswordTitle": "Forgot password?",
- "authForgotPasswordResetPassword": "Reset password",
- "createPassword": "Create your password",
- "createAPassword": "Create a password",
- "authRegisterAlreadyHaveAccount": "Already have an account?",
- "commonAppName": "BlueWave Uptime",
- "authLoginEnterEmail": "Enter your email",
- "authRegisterTitle": "Create an account",
- "authRegisterStepOneTitle": "Create your account",
- "authRegisterStepOneDescription": "Enter your details to get started",
- "authRegisterStepTwoTitle": "Set up your profile",
- "authRegisterStepTwoDescription": "Tell us more about yourself",
- "authRegisterStepThreeTitle": "Almost done!",
- "authRegisterStepThreeDescription": "Review your information",
- "authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
- "authForgotPasswordSendInstructions": "Send instructions",
- "authForgotPasswordBackTo": "Back to",
- "authCheckEmailTitle": "Check your email",
- "authCheckEmailDescription": "We sent a password reset link to {{email}}",
- "authCheckEmailResendEmail": "Resend email",
- "authCheckEmailBackTo": "Back to",
- "goBackTo": "Go back to",
- "authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
- "authCheckEmailClickToResend": "Click to resend",
- "authSetNewPasswordTitle": "Set new password",
- "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
- "authSetNewPasswordNewPassword": "New password",
- "authSetNewPasswordConfirmPassword": "Confirm password",
- "confirmPassword": "Confirm your password",
- "authSetNewPasswordResetPassword": "Reset password",
- "authSetNewPasswordBackTo": "Back to",
- "authPasswordMustBeAtLeast": "Must be at least",
- "authPasswordCharactersLong": "8 characters long",
- "authPasswordMustContainAtLeast": "Must contain at least",
- "authPasswordSpecialCharacter": "one special character",
- "authPasswordOneNumber": "one number",
- "authPasswordUpperCharacter": "one upper character",
- "authPasswordLowerCharacter": "one lower character",
- "authPasswordConfirmAndPassword": "Confirm password and password",
- "authPasswordMustMatch": "must match",
- "friendlyError": "Something went wrong...",
- "unknownError": "An unknown error occurred",
- "unauthorized": "Unauthorized access",
- "authAdminExists": "Admin already exists",
- "authInviteNotFound": "Invite not found",
- "unknownService": "Unknown service",
- "noAuthToken": "No auth token provided",
- "invalidAuthToken": "Invalid auth token",
- "expiredAuthToken": "Token expired",
- "noRefreshToken": "No refresh token provided",
- "invalidRefreshToken": "Invalid refresh token",
- "expiredRefreshToken": "Refresh token expired",
- "requestNewAccessToken": "Request new access token",
- "invalidPayload": "Invalid payload",
- "verifyOwnerNotFound": "Document not found",
- "verifyOwnerUnauthorized": "Unauthorized access",
- "insufficientPermissions": "Insufficient permissions",
- "dbUserExists": "User already exists",
- "dbUserNotFound": "User not found",
- "dbTokenNotFound": "Token not found",
- "dbResetPasswordBadMatch": "New password must be different from old password",
- "dbFindMonitorById": "Monitor with id ${monitorId} not found",
- "dbDeleteChecks": "No checks found for monitor with id ${monitorId}",
- "authIncorrectPassword": "Incorrect password",
- "authUnauthorized": "Unauthorized access",
- "monitorGetById": "Monitor not found",
- "monitorGetByUserId": "No monitors found for user",
- "jobQueueWorkerClose": "Error closing worker",
- "jobQueueDeleteJob": "Job not found in queue",
- "jobQueueObliterate": "Error obliterating queue",
- "pingCannotResolve": "No response",
- "statusPageNotFound": "Status page not found",
- "statusPageUrlNotUnique": "Status page url must be unique",
- "dockerFail": "Failed to fetch Docker container information",
- "dockerNotFound": "Docker container not found",
- "portFail": "Failed to connect to port",
- "alertCreate": "Alert created successfully",
- "alertGetByUser": "Got alerts successfully",
- "alertGetByMonitor": "Got alerts by Monitor successfully",
- "alertGetById": "Got alert by Id successfully",
- "alertEdit": "Alert edited successfully",
- "alertDelete": "Alert deleted successfully",
- "authCreateUser": "User created successfully",
- "authLoginUser": "User logged in successfully",
- "authLogoutUser": "User logged out successfully",
- "authUpdateUser": "User updated successfully",
- "authCreateRecoveryToken": "Recovery token created successfully",
- "authVerifyRecoveryToken": "Recovery token verified successfully",
- "authResetPassword": "Password reset successfully",
- "authAdminCheck": "Admin check completed successfully",
- "authDeleteUser": "User deleted successfully",
- "authTokenRefreshed": "Auth token is refreshed",
- "authGetAllUsers": "Got all users successfully",
- "inviteIssued": "Invite sent successfully",
- "inviteVerified": "Invite verified successfully",
- "checkCreate": "Check created successfully",
- "checkGet": "Got checks successfully",
- "checkDelete": "Checks deleted successfully",
- "checkUpdateTtl": "Checks TTL updated successfully",
- "monitorGetAll": "Got all monitors successfully",
- "monitorStatsById": "Got monitor stats by Id successfully",
- "monitorGetByIdSuccess": "Got monitor by Id successfully",
- "monitorGetByTeamId": "Got monitors by Team Id successfully",
- "monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully",
- "monitorCreate": "Monitor created successfully",
- "monitorDelete": "Monitor deleted successfully",
- "monitorEdit": "Monitor edited successfully",
- "monitorCertificate": "Got monitor certificate successfully",
- "monitorDemoAdded": "Successfully added demo monitors",
- "queueGetMetrics": "Got metrics successfully",
- "queueAddJob": "Job added successfully",
- "queueObliterate": "Queue obliterated",
- "jobQueueDeleteJobSuccess": "Job removed successfully",
- "jobQueuePauseJob": "Job paused successfully",
- "jobQueueResumeJob": "Job resumed successfully",
- "maintenanceWindowGetById": "Got Maintenance Window by Id successfully",
- "maintenanceWindowCreate": "Maintenance Window created successfully",
- "maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully",
- "maintenanceWindowDelete": "Maintenance Window deleted successfully",
- "maintenanceWindowEdit": "Maintenance Window edited successfully",
- "pingSuccess": "Success",
- "getAppSettings": "Got app settings successfully",
- "updateAppSettings": "Updated app settings successfully",
- "statusPageByUrl": "Got status page by url successfully",
- "statusPageCreate": "Status page created successfully",
- "newTermsAdded": "New terms added to POEditor",
- "dockerSuccess": "Docker container status fetched successfully",
- "portSuccess": "Port connected successfully",
- "monitorPause": "Monitor paused successfully",
- "monitorResume": "Monitor resumed successfully",
- "statusPageDelete": "Status page deleted successfully",
- "statusPageUpdate": "Status page updated successfully",
- "statusPageByTeamId": "Got status pages by team id successfully",
- "httpNetworkError": "Network error",
- "httpNotJson": "Response data is not json",
- "httpJsonPathError": "Failed to parse json data",
- "httpEmptyResult": "Result is empty",
- "httpMatchSuccess": "Response data match successfully",
- "httpMatchFail": "Failed to match response data"
-}
\ No newline at end of file
diff --git a/Server/locales/en.json.bak b/Server/locales/en.json.bak
deleted file mode 100644
index eaef58eef..000000000
--- a/Server/locales/en.json.bak
+++ /dev/null
@@ -1,147 +0,0 @@
-{
- "dontHaveAccount": "Don't have account",
- "email": "E-mail",
- "forgotPassword": "Forgot Password",
- "password": "password",
- "signUp": "Sign up",
- "submit": "Submit",
- "title": "Title",
- "continue": "Continue",
- "enterEmail": "Enter your email",
- "authLoginTitle": "Log In",
- "authLoginEnterPassword": "Enter your password",
- "commonPassword": "Password",
- "commonBack": "Back",
- "authForgotPasswordTitle": "Forgot password?",
- "authForgotPasswordResetPassword": "Reset password",
- "createPassword": "Create your password",
- "createAPassword": "Create a password",
- "authRegisterAlreadyHaveAccount": "Already have an account?",
- "commonAppName": "BlueWave Uptime",
- "authLoginEnterEmail": "Enter your email",
- "authRegisterTitle": "Create an account",
- "authRegisterStepOneTitle": "Create your account",
- "authRegisterStepOneDescription": "Enter your details to get started",
- "authRegisterStepTwoTitle": "Set up your profile",
- "authRegisterStepTwoDescription": "Tell us more about yourself",
- "authRegisterStepThreeTitle": "Almost done!",
- "authRegisterStepThreeDescription": "Review your information",
- "authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
- "authForgotPasswordSendInstructions": "Send instructions",
- "authForgotPasswordBackTo": "Back to",
- "authCheckEmailTitle": "Check your email",
- "authCheckEmailDescription": "We sent a password reset link to {{email}}",
- "authCheckEmailResendEmail": "Resend email",
- "authCheckEmailBackTo": "Back to",
- "goBackTo": "Go back to",
- "authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
- "authCheckEmailClickToResend": "Click to resend",
- "authSetNewPasswordTitle": "Set new password",
- "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
- "authSetNewPasswordNewPassword": "New password",
- "authSetNewPasswordConfirmPassword": "Confirm password",
- "confirmPassword": "Confirm your password",
- "authSetNewPasswordResetPassword": "Reset password",
- "authSetNewPasswordBackTo": "Back to",
- "authPasswordMustBeAtLeast": "Must be at least",
- "authPasswordCharactersLong": "8 characters long",
- "authPasswordMustContainAtLeast": "Must contain at least",
- "authPasswordSpecialCharacter": "one special character",
- "authPasswordOneNumber": "one number",
- "authPasswordUpperCharacter": "one upper character",
- "authPasswordLowerCharacter": "one lower character",
- "authPasswordConfirmAndPassword": "Confirm password and password",
- "authPasswordMustMatch": "must match",
- "friendlyError": "Something went wrong...",
- "unknownError": "An unknown error occurred",
- "unauthorized": "Unauthorized access",
- "authAdminExists": "Admin already exists",
- "authInviteNotFound": "Invite not found",
- "unknownService": "Unknown service",
- "noAuthToken": "No auth token provided",
- "invalidAuthToken": "Invalid auth token",
- "expiredAuthToken": "Token expired",
- "noRefreshToken": "No refresh token provided",
- "invalidRefreshToken": "Invalid refresh token",
- "expiredRefreshToken": "Refresh token expired",
- "requestNewAccessToken": "Request new access token",
- "invalidPayload": "Invalid payload",
- "verifyOwnerNotFound": "Document not found",
- "verifyOwnerUnauthorized": "Unauthorized access",
- "insufficientPermissions": "Insufficient permissions",
- "dbUserExists": "User already exists",
- "dbUserNotFound": "User not found",
- "dbTokenNotFound": "Token not found",
- "dbResetPasswordBadMatch": "New password must be different from old password",
- "dbFindMonitorById": "Monitor with id ${monitorId} not found",
- "dbDeleteChecks": "No checks found for monitor with id ${monitorId}",
- "authIncorrectPassword": "Incorrect password",
- "authUnauthorized": "Unauthorized access",
- "monitorGetById": "Monitor not found",
- "monitorGetByUserId": "No monitors found for user",
- "jobQueueWorkerClose": "Error closing worker",
- "jobQueueDeleteJob": "Job not found in queue",
- "jobQueueObliterate": "Error obliterating queue",
- "pingCannotResolve": "No response",
- "statusPageNotFound": "Status page not found",
- "statusPageUrlNotUnique": "Status page url must be unique",
- "dockerFail": "Failed to fetch Docker container information",
- "dockerNotFound": "Docker container not found",
- "portFail": "Failed to connect to port",
- "alertCreate": "Alert created successfully",
- "alertGetByUser": "Got alerts successfully",
- "alertGetByMonitor": "Got alerts by Monitor successfully",
- "alertGetById": "Got alert by Id successfully",
- "alertEdit": "Alert edited successfully",
- "alertDelete": "Alert deleted successfully",
- "authCreateUser": "User created successfully",
- "authLoginUser": "User logged in successfully",
- "authLogoutUser": "User logged out successfully",
- "authUpdateUser": "User updated successfully",
- "authCreateRecoveryToken": "Recovery token created successfully",
- "authVerifyRecoveryToken": "Recovery token verified successfully",
- "authResetPassword": "Password reset successfully",
- "authAdminCheck": "Admin check completed successfully",
- "authDeleteUser": "User deleted successfully",
- "authTokenRefreshed": "Auth token is refreshed",
- "authGetAllUsers": "Got all users successfully",
- "inviteIssued": "Invite sent successfully",
- "inviteVerified": "Invite verified successfully",
- "checkCreate": "Check created successfully",
- "checkGet": "Got checks successfully",
- "checkDelete": "Checks deleted successfully",
- "checkUpdateTtl": "Checks TTL updated successfully",
- "monitorGetAll": "Got all monitors successfully",
- "monitorStatsById": "Got monitor stats by Id successfully",
- "monitorGetByIdSuccess": "Got monitor by Id successfully",
- "monitorGetByTeamId": "Got monitors by Team Id successfully",
- "monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully",
- "monitorCreate": "Monitor created successfully",
- "monitorDelete": "Monitor deleted successfully",
- "monitorEdit": "Monitor edited successfully",
- "monitorCertificate": "Got monitor certificate successfully",
- "monitorDemoAdded": "Successfully added demo monitors",
- "queueGetMetrics": "Got metrics successfully",
- "queueAddJob": "Job added successfully",
- "queueObliterate": "Queue obliterated",
- "jobQueueDeleteJobSuccess": "Job removed successfully",
- "jobQueuePauseJob": "Job paused successfully",
- "jobQueueResumeJob": "Job resumed successfully",
- "maintenanceWindowGetById": "Got Maintenance Window by Id successfully",
- "maintenanceWindowCreate": "Maintenance Window created successfully",
- "maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully",
- "maintenanceWindowDelete": "Maintenance Window deleted successfully",
- "maintenanceWindowEdit": "Maintenance Window edited successfully",
- "pingSuccess": "Success",
- "getAppSettings": "Got app settings successfully",
- "updateAppSettings": "Updated app settings successfully",
- "statusPageByUrl": "Got status page by url successfully",
- "statusPageCreate": "Status page created successfully",
- "newTermsAdded": "New terms added to POEditor",
- "dockerSuccess": "Docker container status fetched successfully",
- "portSuccess": "Port connected successfully",
- "monitorPause": "Monitor paused successfully",
- "monitorResume": "Monitor resumed successfully",
- "statusPageDelete": "Status page deleted successfully",
- "statusPageUpdate": "Status page updated successfully"
-}
diff --git a/Server/middleware/handleErrors.js b/Server/middleware/handleErrors.js
deleted file mode 100644
index b64cda897..000000000
--- a/Server/middleware/handleErrors.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import logger from "../utils/logger.js";
-import ServiceRegistry from "../service/serviceRegistry.js";
-import StringService from "../service/stringService.js";
-
-const handleErrors = (error, req, res, next) => {
- const status = error.status || 500;
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- const message = error.message || stringService.friendlyError;
- const service = error.service || stringService.unknownService;
- logger.error({
- message: message,
- service: service,
- method: error.method,
- stack: error.stack,
- });
- res.error({
- status,
- msg: message,
- });
-};
-
-export { handleErrors };
diff --git a/Server/middleware/isAllowed.js b/Server/middleware/isAllowed.js
deleted file mode 100644
index 03a6b9d16..000000000
--- a/Server/middleware/isAllowed.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import jwt from "jsonwebtoken";
-const TOKEN_PREFIX = "Bearer ";
-const SERVICE_NAME = "allowedRoles";
-import ServiceRegistry from "../service/serviceRegistry.js";
-import StringService from "../service/stringService.js";
-import SettingsService from "../service/settingsService.js";
-
-
-const isAllowed = (allowedRoles) => {
- return (req, res, next) => {
- const token = req.headers["authorization"];
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- // If no token is pressent, return an error
- if (!token) {
- const error = new Error(stringService.noAuthToken);
- error.status = 401;
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
-
- // If the token is improperly formatted, return an error
- if (!token.startsWith(TOKEN_PREFIX)) {
- const error = new Error(stringService.invalidAuthToken);
- error.status = 400;
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
- // Parse the token
- try {
- const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
- const { jwtSecret } = ServiceRegistry.get(
- SettingsService.SERVICE_NAME
- ).getSettings();
- var decoded = jwt.verify(parsedToken, jwtSecret);
- const userRoles = decoded.role;
-
- // Check if the user has the required role
- if (userRoles.some((role) => allowedRoles.includes(role))) {
- next();
- return;
- } else {
- const error = new Error(stringService.insufficientPermissions);
- error.status = 401;
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
- } catch (error) {
- error.status = 401;
- error.method = "isAllowed";
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
- };
-};
-
-export { isAllowed };
diff --git a/Server/middleware/languageMiddleware.js b/Server/middleware/languageMiddleware.js
deleted file mode 100644
index 11c2f2aec..000000000
--- a/Server/middleware/languageMiddleware.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const languageMiddleware = (stringService, translationService) => (req, res, next) => {
- const acceptLanguage = req.headers['accept-language'] || 'en';
- const language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase();
-
- translationService.setLanguage(language);
- stringService.setLanguage(language);
-
- next();
-};
-
-export default languageMiddleware;
\ No newline at end of file
diff --git a/Server/middleware/responseHandler.js b/Server/middleware/responseHandler.js
deleted file mode 100644
index 061422f50..000000000
--- a/Server/middleware/responseHandler.js
+++ /dev/null
@@ -1,20 +0,0 @@
-const responseHandler = (req, res, next) => {
- res.success = ({ status = 200, msg = "OK", data = null }) => {
- return res.status(status).json({
- success: true,
- msg: msg,
- data: data,
- });
- };
-
- res.error = ({ status = 500, msg = "Internal server error", data = null }) => {
- return res.status(status).json({
- success: false,
- msg,
- data,
- });
- };
- next();
-};
-
-export { responseHandler };
diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js
deleted file mode 100644
index 9ee42e809..000000000
--- a/Server/middleware/verifyJWT.js
+++ /dev/null
@@ -1,97 +0,0 @@
-import jwt from "jsonwebtoken";
-import ServiceRegistry from "../service/serviceRegistry.js";
-import SettingsService from "../service/settingsService.js";
-import StringService from "../service/stringService.js";
-const SERVICE_NAME = "verifyJWT";
-const TOKEN_PREFIX = "Bearer ";
-
-/**
- * Verifies the JWT token
- * @function
- * @param {express.Request} req
- * @param {express.Response} res
- * @param {express.NextFunction} next
- * @returns {express.Response}
- */
-const verifyJWT = (req, res, next) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- const token = req.headers["authorization"];
- // Make sure a token is provided
- if (!token) {
- const error = new Error(stringService.noAuthToken);
- error.status = 401;
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
- // Make sure it is properly formatted
- if (!token.startsWith(TOKEN_PREFIX)) {
- const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token
- error.status = 400;
- error.service = SERVICE_NAME;
- error.method = "verifyJWT";
- next(error);
- return;
- }
-
- const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
- // Verify the token's authenticity
- const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings();
- jwt.verify(parsedToken, jwtSecret, (err, decoded) => {
- if (err) {
- if (err.name === "TokenExpiredError") {
- // token has expired
- handleExpiredJwtToken(req, res, next);
- } else {
- // Invalid token (signature or token altered or other issue)
- const errorMessage = stringService.invalidAuthToken;
- return res.status(401).json({ success: false, msg: errorMessage });
- }
- } else {
- // Token is valid and not expired, carry on with request, Add the decoded payload to the request
- req.user = decoded;
- next();
- }
- });
-};
-
-function handleExpiredJwtToken(req, res, next) {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- // check for refreshToken
- const refreshToken = req.headers["x-refresh-token"];
-
- if (!refreshToken) {
- // No refresh token provided
- const error = new Error(stringService.noRefreshToken);
- error.status = 401;
- error.service = SERVICE_NAME;
- error.method = "handleExpiredJwtToken";
- return next(error);
- }
-
- // Verify refresh token
- const { refreshTokenSecret } = ServiceRegistry.get(
- SettingsService.SERVICE_NAME
- ).getSettings();
- jwt.verify(refreshToken, refreshTokenSecret, (refreshErr, refreshDecoded) => {
- if (refreshErr) {
- // Invalid or expired refresh token, trigger logout
- const errorMessage =
- refreshErr.name === "TokenExpiredError"
- ? stringService.expiredRefreshToken
- : stringService.invalidRefreshToken;
- const error = new Error(errorMessage);
- error.status = 401;
- error.service = SERVICE_NAME;
- return next(error);
- }
-
- // Refresh token is valid and unexpired, request for new access token
- res.status(403).json({
- success: false,
- msg: stringService.requestNewAccessToken,
- });
- });
-}
-
-export { verifyJWT };
diff --git a/Server/middleware/verifyOwnership.js b/Server/middleware/verifyOwnership.js
deleted file mode 100644
index ca2476f54..000000000
--- a/Server/middleware/verifyOwnership.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import logger from "../utils/logger.js";
-import ServiceRegistry from "../service/serviceRegistry.js";
-import StringService from "../service/stringService.js";
-const SERVICE_NAME = "verifyOwnership";
-
-const verifyOwnership = (Model, paramName) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- return async (req, res, next) => {
- const userId = req.user._id;
- const documentId = req.params[paramName];
- try {
- const doc = await Model.findById(documentId);
- //If the document is not found, return a 404 error
- if (!doc) {
- logger.error({
- message: stringService.verifyOwnerNotFound,
- service: SERVICE_NAME,
- method: "verifyOwnership",
- });
- const error = new Error(stringService.verifyOwnerNotFound);
- error.status = 404;
- throw error;
- }
-
- // Special case for User model, as it will not have a `userId` field as other docs will
- if (Model.modelName === "User") {
- if (userId.toString() !== doc._id.toString()) {
- const error = new Error(stringService.verifyOwnerUnauthorized);
- error.status = 403;
- throw error;
- }
- next();
- return;
- }
-
- // If the userID does not match the document's userID, return a 403 error
- if (userId.toString() !== doc.userId.toString()) {
- const error = new Error(stringService.verifyOwnerUnauthorized);
- error.status = 403;
- throw error;
- }
- next();
- return;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "verifyOwnership";
- next(error);
- return;
- }
- };
-};
-
-export { verifyOwnership };
diff --git a/Server/middleware/verifySuperAdmin.js b/Server/middleware/verifySuperAdmin.js
deleted file mode 100644
index 98c336fad..000000000
--- a/Server/middleware/verifySuperAdmin.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const jwt = require("jsonwebtoken");
-const logger = require("../utils/logger");
-const SERVICE_NAME = "verifyAdmin";
-const TOKEN_PREFIX = "Bearer ";
-import ServiceRegistry from "../service/serviceRegistry.js";
-import SettingsService from "../service/settingsService.js";
-import StringService from "../service/stringService.js";
-/**
- * Verifies the JWT token
- * @function
- * @param {express.Request} req
- * @param {express.Response} res
- * @param {express.NextFunction} next
- * @returns {express.Response}
- */
-const verifySuperAdmin = (req, res, next) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- const token = req.headers["authorization"];
- // Make sure a token is provided
- if (!token) {
- const error = new Error(stringService.noAuthToken);
- error.status = 401;
- error.service = SERVICE_NAME;
- next(error);
- return;
- }
- // Make sure it is properly formatted
- if (!token.startsWith(TOKEN_PREFIX)) {
- const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token
- error.status = 400;
- error.service = SERVICE_NAME;
- error.method = "verifySuperAdmin";
- next(error);
- return;
- }
-
- const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
- // verify admin role is present
- const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings();
-
- jwt.verify(parsedToken, jwtSecret, (err, decoded) => {
- if (err) {
- logger.error({
- message: err.message,
- service: SERVICE_NAME,
- method: "verifySuperAdmin",
- stack: err.stack,
- details: stringService.invalidAuthToken,
- });
- return res
- .status(401)
- .json({ success: false, msg: stringService.invalidAuthToken });
- }
-
- if (decoded.role.includes("superadmin") === false) {
- logger.error({
- message: stringService.invalidAuthToken,
- service: SERVICE_NAME,
- method: "verifySuperAdmin",
- stack: err.stack,
- });
- return res.status(401).json({ success: false, msg: stringService.unauthorized });
- }
- next();
- });
-};
-
-module.exports = { verifySuperAdmin };
diff --git a/Server/nodemon.json b/Server/nodemon.json
deleted file mode 100644
index b43c80fc1..000000000
--- a/Server/nodemon.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "ignore": [
- "locales/*",
- "*.log",
- "node_modules/*"
- ],
- "watch": [
- "*.js",
- "*.json"
- ],
- "ext": "js,json"
-}
\ No newline at end of file
diff --git a/Server/openapi.json b/Server/openapi.json
deleted file mode 100644
index 7b7bb3cb3..000000000
--- a/Server/openapi.json
+++ /dev/null
@@ -1,2562 +0,0 @@
-{
- "openapi": "3.1.0",
- "info": {
- "title": "Checkmate",
- "summary": "Checkmate OpenAPI Specifications",
- "description": "Checkmate is an open source monitoring tool used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.",
- "contact": {
- "name": "API Support",
- "url": "mailto:support@bluewavelabs.ca",
- "email": "support@bluewavelabs.ca"
- },
- "license": {
- "name": "AGPLv3",
- "url": "https://github.com/bluewave-labs/checkmate/tree/HEAD/LICENSE"
- },
- "version": "1.0"
- },
- "servers": [
- {
- "url": "http://localhost:{PORT}/{API_PATH}",
- "description": "Local Development Server",
- "variables": {
- "PORT": {
- "description": "API Port",
- "enum": ["5000"],
- "default": "5000"
- },
- "API_PATH": {
- "description": "API Base Path",
- "enum": ["api/v1"],
- "default": "api/v1"
- }
- }
- },
- {
- "url": "http://localhost/{API_PATH}",
- "description": "Distribution Local Development Server",
- "variables": {
- "API_PATH": {
- "description": "API Base Path",
- "enum": ["api/v1"],
- "default": "api/v1"
- }
- }
- },
- {
- "url": "https://checkmate-demo.bluewavelabs.ca/{API_PATH}",
- "description": "Checkmate Demo Server",
- "variables": {
- "PORT": {
- "description": "API Port",
- "enum": ["5000"],
- "default": "5000"
- },
- "API_PATH": {
- "description": "API Base Path",
- "enum": ["api/v1"],
- "default": "api/v1"
- }
- }
- }
- ],
- "tags": [
- {
- "name": "auth",
- "description": "Authentication"
- },
- {
- "name": "invite",
- "description": "Invite"
- },
- {
- "name": "monitors",
- "description": "Monitors"
- },
- {
- "name": "checks",
- "description": "Checks"
- },
- {
- "name": "maintenance-window",
- "description": "Maintenance window"
- },
- {
- "name": "queue",
- "description": "Queue"
- },
- {
- "name": "status-page",
- "description": "Status Page"
- }
- ],
- "paths": {
- "/auth/register": {
- "post": {
- "tags": ["auth"],
- "description": "Register a new user",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "firstName",
- "lastName",
- "email",
- "password",
- "role",
- "teamId"
- ],
- "properties": {
- "firstName": {
- "type": "string"
- },
- "lastName": {
- "type": "string"
- },
- "email": {
- "type": "string",
- "format": "email"
- },
- "password": {
- "type": "string",
- "format": "password"
- },
- "profileImage": {
- "type": "file",
- "format": "file"
- },
- "role": {
- "type": "array",
- "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]],
- "default": ["superadmin"]
- },
- "teamId": {
- "type": "string",
- "format": "uuid"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/login": {
- "post": {
- "tags": ["auth"],
- "description": "Login with credentials",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["email", "password"],
- "properties": {
- "email": {
- "type": "string",
- "format": "email"
- },
- "password": {
- "type": "string",
- "format": "password"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/refresh": {
- "post": {
- "tags": ["auth"],
- "description": "Generates a new auth token if the refresh token is valid.",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {}
- }
- }
- },
- "required": false
- },
- "parameters": [
- {
- "name": "x-refresh-token",
- "in": "header",
- "description": "Refresh token required to generate a new auth token.",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "authorization",
- "in": "header",
- "description": "Old access token, used to extract payload).",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "New access token generated.",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "401": {
- "description": "Unauthorized or invalid refresh token.",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/user/{userId}": {
- "put": {
- "tags": ["auth"],
- "description": "Change user information",
- "parameters": [
- {
- "name": "userId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UserUpdateRequest"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["auth"],
- "description": "Delete user",
- "parameters": [
- {
- "name": "userId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/auth/users/superadmin": {
- "get": {
- "tags": ["auth"],
- "description": "Checks to see if an admin account exists",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/auth/users": {
- "get": {
- "tags": ["auth"],
- "description": "Get all users",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/auth/recovery/request": {
- "post": {
- "tags": ["auth"],
- "description": "Request a recovery token",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["email"],
- "properties": {
- "email": {
- "type": "string",
- "format": "email"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/recovery/validate": {
- "post": {
- "tags": ["auth"],
- "description": "Validate recovery token",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["recoveryToken"],
- "properties": {
- "recoveryToken": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/recovery/reset": {
- "post": {
- "tags": ["auth"],
- "description": "Password reset",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["recoveryToken", "password"],
- "properties": {
- "recoveryToken": {
- "type": "string"
- },
- "password": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/invite": {
- "post": {
- "tags": ["invite"],
- "description": "Request an invitation",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["email", "role"],
- "properties": {
- "email": {
- "type": "string"
- },
- "role": {
- "type": "array"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/invite/verify": {
- "post": {
- "tags": ["invite"],
- "description": "Request an invitation",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["token"],
- "properties": {
- "token": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors": {
- "get": {
- "tags": ["monitors"],
- "description": "Get all monitors",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "post": {
- "tags": ["monitors"],
- "description": "Create a new monitor",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateMonitorBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["monitors"],
- "description": "Delete all monitors",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/uptime": {
- "get": {
- "tags": ["monitors"],
- "description": "Get all monitors with uptime stats for 1, 7, 30, and 90 days",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/resolution/url": {
- "get": {
- "tags": ["monitors"],
- "description": "Check DNS resolution for a given URL",
- "parameters": [
- {
- "name": "monitorURL",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "example": "https://example.com"
- },
- "description": "The URL to check DNS resolution for"
- }
- ],
- "responses": {
- "200": {
- "description": "URL resolved successfully",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "400": {
- "description": "DNS resolution failed",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/{monitorId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitor by id",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "put": {
- "tags": ["monitors"],
- "description": "Update monitor by id",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UpdateMonitorBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["monitors"],
- "description": "Delete monitor by id",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/stats/{monitorId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitor stats",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/certificate/{monitorId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitor certificate",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/team/summary/{teamId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitors and summary by teamId",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "type",
- "in": "query",
- "required": false,
- "schema": {
- "type": "array",
- "enum": ["http", "ping", "pagespeed"]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/team/{teamId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitors by teamId",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "status",
- "description": "Status of monitor, true for up, false for down",
- "in": "query",
- "required": false,
- "schema": {
- "type": "boolean"
- }
- },
- {
- "name": "checkOrder",
- "description": "Order of checks",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "enum": ["asc", "desc"]
- }
- },
- {
- "name": "limit",
- "description": "Number of checks to return with monitor",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer"
- }
- },
- {
- "name": "type",
- "description": "Type of monitor",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "enum": ["http", "ping", "pagespeed"]
- }
- },
- {
- "name": "page",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer"
- }
- },
- {
- "name": "rowsPerPage",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer"
- }
- },
- {
- "name": "filter",
- "description": "Value to filter by",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "field",
- "description": "Field to filter on",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "order",
- "description": "Sort order of results",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "enum": ["http", "ping", "pagespeed"]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/pause/{monitorId}": {
- "post": {
- "tags": ["monitors"],
- "description": "Pause monitor",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/demo": {
- "post": {
- "tags": ["monitors"],
- "description": "Create a demo monitor",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateMonitorBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/checks/{monitorId}": {
- "get": {
- "tags": ["checks"],
- "description": "Get all checks for a monitor",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "post": {
- "tags": ["checks"],
- "description": "Create a new check",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateCheckBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["checks"],
- "description": "Delete all checks for a monitor",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/checks/team/{teamId}": {
- "get": {
- "tags": ["checks"],
- "description": "Get all checks for a team",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["checks"],
- "description": "Delete all checks for a team",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/checks/team/ttl": {
- "put": {
- "tags": ["checks"],
- "description": "Update check TTL",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UpdateCheckTTLBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/maintenance-window/monitor/{monitorId}": {
- "get": {
- "tags": ["maintenance-window"],
- "description": "Get maintenance window for monitor",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "post": {
- "tags": ["maintenance-window"],
- "description": "Create maintenance window for monitor",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateMaintenanceWindowBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/maintenance-window/user/{userId}": {
- "get": {
- "tags": ["maintenance-window"],
- "description": "Get maintenance window for user",
- "parameters": [
- {
- "name": "userId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/queue/jobs": {
- "get": {
- "tags": ["queue"],
- "description": "Get all jobs in queue",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "post": {
- "tags": ["queue"],
- "description": "Create a new job. Useful for testing scaling workers",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/queue/metrics": {
- "get": {
- "tags": ["queue"],
- "description": "Get queue metrics",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/queue/obliterate": {
- "post": {
- "tags": ["queue"],
- "description": "Obliterate job queue",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/status-page/{url}": {
- "get": {
- "tags": ["status-page"],
- "description": "Get a status page by URL",
- "parameters": [
- {
- "name": "url",
- "in": "path",
- "required": true,
- "schema": { "type": "string" }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/SuccessResponse" }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "404": {
- "description": "Not Found",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/ErrorResponse" }
- }
- }
- }
- }
- },
- "post": {
- "tags": ["status-page"],
- "description": "Create a status page",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateStatusPageBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "400": {
- "description": "Duplicate URL",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- }
- },
- "components": {
- "securitySchemes": {
- "bearerAuth": {
- "type": "http",
- "scheme": "bearer",
- "bearerFormat": "JWT"
- }
- },
- "schemas": {
- "ErrorResponse": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "default": false
- },
- "msg": {
- "type": "string"
- }
- }
- },
- "SuccessResponse": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "default": true
- },
- "msg": {
- "type": "string"
- },
- "data": {
- "type": "object"
- }
- }
- },
- "UserUpdateRequest": {
- "type": "object",
- "required": ["firstName", "lastName", "email", "password", "role", "teamId"],
- "properties": {
- "firstName": {
- "type": "string"
- },
- "lastName": {
- "type": "string"
- },
- "password": {
- "type": "string",
- "format": "password"
- },
- "newPassword": {
- "type": "string",
- "format": "password"
- },
- "profileImage": {
- "type": "file",
- "format": "file"
- },
- "role": {
- "type": "array",
- "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]],
- "default": ["superadmin"]
- },
- "deleteProfileImage": {
- "type": "boolean"
- }
- }
- },
- "CreateMonitorBody": {
- "type": "object",
- "required": ["userId", "teamId", "name", "description", "type", "url"],
- "properties": {
- "_id": {
- "type": "string"
- },
- "userId": {
- "type": "string"
- },
- "teamId": {
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "type": {
- "type": "string",
- "enum": ["http", "ping", "pagespeed"]
- },
- "url": {
- "type": "string"
- },
- "isActive": {
- "type": "boolean"
- },
- "interval": {
- "type": "integer"
- },
- "notifications": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "UpdateMonitorBody": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "interval": {
- "type": "integer"
- },
- "notifications": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "CreateCheckBody": {
- "type": "object",
- "required": ["monitorId", "status", "responseTime", "statusCode", "message"],
- "properties": {
- "monitorId": {
- "type": "string"
- },
- "status": {
- "type": "boolean"
- },
- "responseTime": {
- "type": "integer"
- },
- "statusCode": {
- "type": "integer"
- },
- "message": {
- "type": "string"
- }
- }
- },
- "UpdateCheckTTLBody": {
- "type": "object",
- "required": ["ttl"],
- "properties": {
- "ttl": {
- "type": "integer"
- }
- }
- },
- "CreateMaintenanceWindowBody": {
- "type": "object",
- "required": ["userId", "active", "oneTime", "start", "end"],
- "properties": {
- "userId": {
- "type": "string"
- },
- "active": {
- "type": "boolean"
- },
- "oneTime": {
- "type": "boolean"
- },
- "start": {
- "type": "string",
- "format": "date-time"
- },
- "end": {
- "type": "string",
- "format": "date-time"
- },
- "expiry": {
- "type": "string",
- "format": "date-time"
- }
- }
- },
- "CreateStatusPageBody": {
- "type": "object",
- "required": ["companyName", "url", "timezone", "color", "theme", "monitors"],
- "properties": {
- "companyName": { "type": "string" },
- "url": { "type": "string" },
- "timezone": { "type": "string" },
- "color": { "type": "string" },
- "theme": { "type": "string" },
- "monitors": {
- "type": "array",
- "items": { "type": "string" }
- }
- }
- }
- }
- }
-}
diff --git a/Server/package-lock.json b/Server/package-lock.json
deleted file mode 100644
index 8b04b093d..000000000
--- a/Server/package-lock.json
+++ /dev/null
@@ -1,8334 +0,0 @@
-{
- "name": "server",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "server",
- "version": "1.0.0",
- "license": "ISC",
- "dependencies": {
- "axios": "^1.7.2",
- "bcrypt": "5.1.1",
- "bullmq": "5.41.2",
- "cors": "^2.8.5",
- "dockerode": "4.0.4",
- "dotenv": "^16.4.5",
- "express": "^4.19.2",
- "handlebars": "^4.7.8",
- "helmet": "^8.0.0",
- "ioredis": "^5.4.2",
- "jmespath": "^0.16.0",
- "joi": "^17.13.1",
- "jsonwebtoken": "9.0.2",
- "mailersend": "^2.2.0",
- "mjml": "^5.0.0-alpha.4",
- "mongoose": "^8.3.3",
- "multer": "1.4.5-lts.1",
- "nodemailer": "^6.9.14",
- "ping": "0.4.4",
- "sharp": "0.33.5",
- "ssl-checker": "2.0.10",
- "swagger-ui-express": "5.0.1",
- "winston": "^3.13.0"
- },
- "devDependencies": {
- "@eslint/js": "^9.17.0",
- "c8": "10.1.3",
- "chai": "5.2.0",
- "eslint": "^9.17.0",
- "eslint-plugin-mocha": "^10.5.0",
- "esm": "3.2.25",
- "globals": "^15.14.0",
- "mocha": "11.1.0",
- "nodemon": "3.1.9",
- "prettier": "^3.3.3",
- "sinon": "19.0.2"
- }
- },
- "node_modules/@babel/code-frame": {
- "version": "7.26.2",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
- "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.25.9",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/runtime": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
- "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
- "license": "MIT",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@balena/dockerignore": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
- "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==",
- "license": "Apache-2.0"
- },
- "node_modules/@bcoe/v8-coverage": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.1.tgz",
- "integrity": "sha512-W+a0/JpU28AqH4IKtwUPcEUnUyXMDLALcn5/JLczGGT9fHE2sIby/xP/oQnx3nxkForzgzPy201RAKcB4xPAFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@colors/colors": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
- "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.1.90"
- }
- },
- "node_modules/@dabh/diagnostics": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
- "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
- "license": "MIT",
- "dependencies": {
- "colorspace": "1.1.x",
- "enabled": "2.0.x",
- "kuler": "^2.0.0"
- }
- },
- "node_modules/@emnapi/runtime": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
- "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
- "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
- "dev": true,
- "dependencies": {
- "eslint-visitor-keys": "^3.4.3"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
- }
- },
- "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
- "dev": true,
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
- }
- },
- "node_modules/@eslint/config-array": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
- "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
- "dev": true,
- "dependencies": {
- "@eslint/object-schema": "^2.1.5",
- "debug": "^4.3.1",
- "minimatch": "^3.1.2"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/config-array/node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
- "dev": true,
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/@eslint/config-array/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "node_modules/@eslint/core": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
- "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
- "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
- "dev": true,
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
- "dev": true,
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "node_modules/@eslint/js": {
- "version": "9.20.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz",
- "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/object-schema": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
- "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/plugin-kit": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
- "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.10.0",
- "levn": "^0.4.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@grpc/grpc-js": {
- "version": "1.12.5",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz",
- "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/proto-loader": "^0.7.13",
- "@js-sdsl/ordered-map": "^4.4.2"
- },
- "engines": {
- "node": ">=12.10.0"
- }
- },
- "node_modules/@grpc/proto-loader": {
- "version": "0.7.13",
- "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
- "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
- "license": "Apache-2.0",
- "dependencies": {
- "lodash.camelcase": "^4.3.0",
- "long": "^5.0.0",
- "protobufjs": "^7.2.5",
- "yargs": "^17.7.2"
- },
- "bin": {
- "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@hapi/hoek": {
- "version": "9.3.0",
- "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
- "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@hapi/topo": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
- "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@hapi/hoek": "^9.0.0"
- }
- },
- "node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node": {
- "version": "0.16.6",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
- "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
- "dev": true,
- "dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.3.0"
- },
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
- "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
- "dev": true,
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
- "engines": {
- "node": ">=12.22"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/retry": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
- "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
- "dev": true,
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@img/sharp-darwin-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
- "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-darwin-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
- "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
- "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
- "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
- "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
- "cpu": [
- "arm"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
- "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
- "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
- "cpu": [
- "s390x"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
- "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
- "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
- "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-linux-arm": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
- "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
- "cpu": [
- "arm"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.0.5"
- }
- },
- "node_modules/@img/sharp-linux-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
- "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linux-s390x": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
- "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
- "cpu": [
- "s390x"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linux-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
- "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
- "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
- "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-wasm32": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
- "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
- "cpu": [
- "wasm32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/runtime": "^1.2.0"
- },
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-ia32": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
- "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
- "cpu": [
- "ia32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
- "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@ioredis/commands": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
- "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==",
- "license": "MIT"
- },
- "node_modules/@isaacs/cliui": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^5.1.2",
- "string-width-cjs": "npm:string-width@^4.2.0",
- "strip-ansi": "^7.0.1",
- "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
- "wrap-ansi": "^8.1.0",
- "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@js-sdsl/ordered-map": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
- "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/js-sdsl"
- }
- },
- "node_modules/@mapbox/node-pre-gyp": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
- "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "detect-libc": "^2.0.0",
- "https-proxy-agent": "^5.0.0",
- "make-dir": "^3.1.0",
- "node-fetch": "^2.6.7",
- "nopt": "^5.0.0",
- "npmlog": "^5.0.1",
- "rimraf": "^3.0.2",
- "semver": "^7.3.5",
- "tar": "^6.1.11"
- },
- "bin": {
- "node-pre-gyp": "bin/node-pre-gyp"
- }
- },
- "node_modules/@mongodb-js/saslprep": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz",
- "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==",
- "license": "MIT",
- "dependencies": {
- "sparse-bitfield": "^3.0.3"
- }
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
- "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
- "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
- "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
- "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
- "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
- "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@protobufjs/aspromise": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
- "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/base64": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
- "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/codegen": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
- "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/eventemitter": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
- "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/fetch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
- "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.1",
- "@protobufjs/inquire": "^1.1.0"
- }
- },
- "node_modules/@protobufjs/float": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
- "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/inquire": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
- "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/path": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
- "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/pool": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
- "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/utf8": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
- "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@scarf/scarf": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
- "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
- "hasInstallScript": true,
- "license": "Apache-2.0"
- },
- "node_modules/@sideway/address": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
- "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@hapi/hoek": "^9.0.0"
- }
- },
- "node_modules/@sideway/formula": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
- "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@sideway/pinpoint": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
- "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@sinonjs/commons": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
- "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "type-detect": "4.0.8"
- }
- },
- "node_modules/@sinonjs/fake-timers": {
- "version": "13.0.5",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz",
- "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1"
- }
- },
- "node_modules/@sinonjs/samsam": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz",
- "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "lodash.get": "^4.4.2",
- "type-detect": "^4.1.0"
- }
- },
- "node_modules/@sinonjs/samsam/node_modules/type-detect": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
- "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@sinonjs/text-encoding": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz",
- "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==",
- "dev": true,
- "license": "(Unlicense OR Apache-2.0)"
- },
- "node_modules/@trysound/sax": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
- "license": "ISC",
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
- "dev": true
- },
- "node_modules/@types/istanbul-lib-coverage": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
- "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true
- },
- "node_modules/@types/node": {
- "version": "22.10.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.4.tgz",
- "integrity": "sha512-99l6wv4HEzBQhvaU/UGoeBoCK61SCROQaCCGyQSgX2tEQ3rKkNZ2S7CEWnS/4s1LV+8ODdK21UeyR1fHP2mXug==",
- "license": "MIT",
- "dependencies": {
- "undici-types": "~6.20.0"
- }
- },
- "node_modules/@types/triple-beam": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
- "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
- "license": "MIT"
- },
- "node_modules/@types/webidl-conversions": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
- "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
- "license": "MIT"
- },
- "node_modules/@types/whatwg-url": {
- "version": "11.0.5",
- "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
- "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
- "license": "MIT",
- "dependencies": {
- "@types/webidl-conversions": "*"
- }
- },
- "node_modules/abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
- "license": "ISC"
- },
- "node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "license": "MIT",
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/acorn": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
- "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
- "peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
- }
- },
- "node_modules/agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "license": "MIT",
- "dependencies": {
- "debug": "4"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/agent-base/node_modules/debug": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
- "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/agent-base/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ansi-colors": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
- "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "license": "ISC",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/append-field": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
- "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
- "license": "MIT"
- },
- "node_modules/aproba": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
- "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
- "license": "ISC"
- },
- "node_modules/are-we-there-yet": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
- "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
- "deprecated": "This package is no longer supported.",
- "license": "ISC",
- "dependencies": {
- "delegates": "^1.0.0",
- "readable-stream": "^3.6.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "license": "Python-2.0"
- },
- "node_modules/array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
- "license": "MIT"
- },
- "node_modules/asn1": {
- "version": "0.2.6",
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
- "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": "~2.1.0"
- }
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/async": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
- "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
- "license": "MIT"
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "license": "MIT"
- },
- "node_modules/axios": {
- "version": "1.7.9",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
- "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
- "license": "MIT",
- "dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "license": "MIT"
- },
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/bcrypt": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
- "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "@mapbox/node-pre-gyp": "^1.0.11",
- "node-addon-api": "^5.0.0"
- },
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/bcrypt-pbkdf": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
- "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "tweetnacl": "^0.14.3"
- }
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/bl": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
- "license": "MIT",
- "dependencies": {
- "buffer": "^5.5.0",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
- }
- },
- "node_modules/body-parser": {
- "version": "1.20.3",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
- "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
- "license": "MIT",
- "dependencies": {
- "bytes": "3.1.2",
- "content-type": "~1.0.5",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.13.0",
- "raw-body": "2.5.2",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
- },
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/body-parser/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/body-parser/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT"
- },
- "node_modules/boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "license": "ISC"
- },
- "node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "license": "MIT",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/browserslist": {
- "version": "4.24.3",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz",
- "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "caniuse-lite": "^1.0.30001688",
- "electron-to-chromium": "^1.5.73",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.1"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/bson": {
- "version": "6.10.1",
- "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz",
- "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=16.20.1"
- }
- },
- "node_modules/buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
- "node_modules/buffer-equal-constant-time": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
- "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "license": "MIT"
- },
- "node_modules/buildcheck": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
- "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
- "optional": true,
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/bullmq": {
- "version": "5.41.2",
- "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.41.2.tgz",
- "integrity": "sha512-wqsUIHW2Td86mTKTepqQpKLLUtP4gmX89bUO1YL2fAorxwj3da1GYtroGZMCg/zgB/+zMRsbylL6DHyMUWX7fA==",
- "license": "MIT",
- "dependencies": {
- "cron-parser": "^4.9.0",
- "ioredis": "^5.4.1",
- "msgpackr": "^1.11.2",
- "node-abort-controller": "^3.1.1",
- "semver": "^7.5.4",
- "tslib": "^2.0.0",
- "uuid": "^9.0.0"
- }
- },
- "node_modules/busboy": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
- "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
- "dependencies": {
- "streamsearch": "^1.1.0"
- },
- "engines": {
- "node": ">=10.16.0"
- }
- },
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/c8": {
- "version": "10.1.3",
- "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz",
- "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@bcoe/v8-coverage": "^1.0.1",
- "@istanbuljs/schema": "^0.1.3",
- "find-up": "^5.0.0",
- "foreground-child": "^3.1.1",
- "istanbul-lib-coverage": "^3.2.0",
- "istanbul-lib-report": "^3.0.1",
- "istanbul-reports": "^3.1.6",
- "test-exclude": "^7.0.1",
- "v8-to-istanbul": "^9.0.0",
- "yargs": "^17.7.2",
- "yargs-parser": "^21.1.1"
- },
- "bin": {
- "c8": "bin/c8.js"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "monocart-coverage-reports": "^2"
- },
- "peerDependenciesMeta": {
- "monocart-coverage-reports": {
- "optional": true
- }
- }
- },
- "node_modules/call-bind-apply-helpers": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
- "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/call-bound": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
- "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/caniuse-api": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
- "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.0.0",
- "caniuse-lite": "^1.0.0",
- "lodash.memoize": "^4.1.2",
- "lodash.uniq": "^4.5.0"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001690",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
- "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "CC-BY-4.0"
- },
- "node_modules/chai": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
- "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 16"
- }
- },
- "node_modules/cheerio": {
- "version": "1.0.0-rc.12",
- "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
- "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
- "license": "MIT",
- "dependencies": {
- "cheerio-select": "^2.1.0",
- "dom-serializer": "^2.0.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.0.1",
- "htmlparser2": "^8.0.1",
- "parse5": "^7.0.0",
- "parse5-htmlparser2-tree-adapter": "^7.0.0"
- },
- "engines": {
- "node": ">= 6"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
- }
- },
- "node_modules/cheerio-select": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
- "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "boolbase": "^1.0.0",
- "css-select": "^5.1.0",
- "css-what": "^6.1.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.0.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/chownr": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
- "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
- "license": "ISC",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/cliui/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/cluster-key-slot": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
- "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/color": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
- "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1",
- "color-string": "^1.9.0"
- },
- "engines": {
- "node": ">=12.5.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT"
- },
- "node_modules/color-string": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
- "license": "MIT",
- "dependencies": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
- },
- "node_modules/color-support": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
- "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
- "license": "ISC",
- "bin": {
- "color-support": "bin.js"
- }
- },
- "node_modules/colord": {
- "version": "2.9.3",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
- "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
- "license": "MIT"
- },
- "node_modules/colorspace": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
- "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
- "license": "MIT",
- "dependencies": {
- "color": "^3.1.3",
- "text-hex": "1.0.x"
- }
- },
- "node_modules/colorspace/node_modules/color": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
- "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.3",
- "color-string": "^1.6.0"
- }
- },
- "node_modules/colorspace/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "license": "MIT",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/colorspace/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "license": "MIT"
- },
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "license": "MIT",
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/commander": {
- "version": "12.1.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
- "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "license": "MIT"
- },
- "node_modules/concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "engines": [
- "node >= 0.8"
- ],
- "license": "MIT",
- "dependencies": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- }
- },
- "node_modules/concat-stream/node_modules/readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/concat-stream/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "license": "MIT"
- },
- "node_modules/concat-stream/node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
- "node_modules/console-control-strings": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
- "license": "ISC"
- },
- "node_modules/content-disposition": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "5.2.1"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cookie": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
- "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
- "license": "MIT"
- },
- "node_modules/core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
- "license": "MIT"
- },
- "node_modules/cors": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
- "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
- "license": "MIT",
- "dependencies": {
- "object-assign": "^4",
- "vary": "^1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/cosmiconfig": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
- "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
- "license": "MIT",
- "dependencies": {
- "env-paths": "^2.2.1",
- "import-fresh": "^3.3.0",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.2.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/d-fischer"
- },
- "peerDependencies": {
- "typescript": ">=4.9.5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/cpu-features": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
- "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
- "hasInstallScript": true,
- "optional": true,
- "dependencies": {
- "buildcheck": "~0.0.6",
- "nan": "^2.19.0"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/cron-parser": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
- "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
- "license": "MIT",
- "dependencies": {
- "luxon": "^3.2.1"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/css-declaration-sorter": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
- "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
- "license": "ISC",
- "engines": {
- "node": "^14 || ^16 || >=18"
- },
- "peerDependencies": {
- "postcss": "^8.0.9"
- }
- },
- "node_modules/css-select": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
- "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "boolbase": "^1.0.0",
- "css-what": "^6.1.0",
- "domhandler": "^5.0.2",
- "domutils": "^3.0.1",
- "nth-check": "^2.0.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "license": "MIT",
- "dependencies": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
- }
- },
- "node_modules/css-what": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">= 6"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "license": "MIT",
- "bin": {
- "cssesc": "bin/cssesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/cssnano": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.6.tgz",
- "integrity": "sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==",
- "license": "MIT",
- "dependencies": {
- "cssnano-preset-default": "^7.0.6",
- "lilconfig": "^3.1.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/cssnano"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/cssnano-preset-default": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.6.tgz",
- "integrity": "sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "css-declaration-sorter": "^7.2.0",
- "cssnano-utils": "^5.0.0",
- "postcss-calc": "^10.0.2",
- "postcss-colormin": "^7.0.2",
- "postcss-convert-values": "^7.0.4",
- "postcss-discard-comments": "^7.0.3",
- "postcss-discard-duplicates": "^7.0.1",
- "postcss-discard-empty": "^7.0.0",
- "postcss-discard-overridden": "^7.0.0",
- "postcss-merge-longhand": "^7.0.4",
- "postcss-merge-rules": "^7.0.4",
- "postcss-minify-font-values": "^7.0.0",
- "postcss-minify-gradients": "^7.0.0",
- "postcss-minify-params": "^7.0.2",
- "postcss-minify-selectors": "^7.0.4",
- "postcss-normalize-charset": "^7.0.0",
- "postcss-normalize-display-values": "^7.0.0",
- "postcss-normalize-positions": "^7.0.0",
- "postcss-normalize-repeat-style": "^7.0.0",
- "postcss-normalize-string": "^7.0.0",
- "postcss-normalize-timing-functions": "^7.0.0",
- "postcss-normalize-unicode": "^7.0.2",
- "postcss-normalize-url": "^7.0.0",
- "postcss-normalize-whitespace": "^7.0.0",
- "postcss-ordered-values": "^7.0.1",
- "postcss-reduce-initial": "^7.0.2",
- "postcss-reduce-transforms": "^7.0.0",
- "postcss-svgo": "^7.0.1",
- "postcss-unique-selectors": "^7.0.3"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/cssnano-utils": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz",
- "integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==",
- "license": "MIT",
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/csso": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
- "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
- "license": "MIT",
- "dependencies": {
- "css-tree": "~2.2.0"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
- "npm": ">=7.0.0"
- }
- },
- "node_modules/csso/node_modules/css-tree": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
- "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
- "license": "MIT",
- "dependencies": {
- "mdn-data": "2.0.28",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
- "npm": ">=7.0.0"
- }
- },
- "node_modules/csso/node_modules/mdn-data": {
- "version": "2.0.28",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
- "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
- "license": "CC0-1.0"
- },
- "node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true
- },
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/delegates": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
- "license": "MIT"
- },
- "node_modules/denque": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
- "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/detect-libc": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
- "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/detect-node": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
- "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
- "license": "MIT"
- },
- "node_modules/diff": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
- "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/docker-modem": {
- "version": "5.0.6",
- "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
- "integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "debug": "^4.1.1",
- "readable-stream": "^3.5.0",
- "split-ca": "^1.0.1",
- "ssh2": "^1.15.0"
- },
- "engines": {
- "node": ">= 8.0"
- }
- },
- "node_modules/dockerode": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
- "integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@balena/dockerignore": "^1.0.2",
- "@grpc/grpc-js": "^1.11.1",
- "@grpc/proto-loader": "^0.7.13",
- "docker-modem": "^5.0.6",
- "protobufjs": "^7.3.2",
- "tar-fs": "~2.0.1",
- "uuid": "^10.0.0"
- },
- "engines": {
- "node": ">= 8.0"
- }
- },
- "node_modules/dockerode/node_modules/uuid": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
- "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
- "node_modules/dom-serializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "entities": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "node_modules/domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "BSD-2-Clause"
- },
- "node_modules/domhandler": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.3.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/domutils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
- "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "dom-serializer": "^2.0.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "node_modules/dotenv": {
- "version": "16.4.7",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
- "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://dotenvx.com"
- }
- },
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "license": "MIT"
- },
- "node_modules/ecdsa-sig-formatter": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
- "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
- "license": "MIT"
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.74",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz",
- "integrity": "sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==",
- "license": "ISC"
- },
- "node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "license": "MIT"
- },
- "node_modules/enabled": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
- "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
- "license": "MIT"
- },
- "node_modules/encodeurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/encoding-sniffer": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz",
- "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==",
- "license": "MIT",
- "dependencies": {
- "iconv-lite": "^0.6.3",
- "whatwg-encoding": "^3.1.1"
- },
- "funding": {
- "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
- }
- },
- "node_modules/encoding-sniffer/node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "license": "MIT",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/env-paths": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
- "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-object-atoms": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
- "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-goat": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz",
- "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
- "license": "MIT"
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint": {
- "version": "9.20.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz",
- "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.0",
- "@eslint/core": "^0.11.0",
- "@eslint/eslintrc": "^3.2.0",
- "@eslint/js": "9.20.0",
- "@eslint/plugin-kit": "^0.2.5",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.1",
- "@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.2.0",
- "eslint-visitor-keys": "^4.2.0",
- "espree": "^10.3.0",
- "esquery": "^1.5.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-mocha": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz",
- "integrity": "sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==",
- "dev": true,
- "dependencies": {
- "eslint-utils": "^3.0.0",
- "globals": "^13.24.0",
- "rambda": "^7.4.0"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
- }
- },
- "node_modules/eslint-plugin-mocha/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-scope": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
- "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
- "dev": true,
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-utils": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
- "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
- "dev": true,
- "dependencies": {
- "eslint-visitor-keys": "^2.0.0"
- },
- "engines": {
- "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/mysticatea"
- },
- "peerDependencies": {
- "eslint": ">=5"
- }
- },
- "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
- "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
- "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/node_modules/@eslint/core": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz",
- "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/esm": {
- "version": "3.2.25",
- "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
- "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/espree": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
- "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
- "dev": true,
- "dependencies": {
- "acorn": "^8.14.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
- "dev": true,
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "dependencies": {
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/express": {
- "version": "4.21.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
- "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
- "license": "MIT",
- "dependencies": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
- "body-parser": "1.20.3",
- "content-disposition": "0.5.4",
- "content-type": "~1.0.4",
- "cookie": "0.7.1",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "encodeurl": "~2.0.0",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.3.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "merge-descriptors": "1.0.3",
- "methods": "~1.1.2",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.12",
- "proxy-addr": "~2.0.7",
- "qs": "6.13.0",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.2.1",
- "send": "0.19.0",
- "serve-static": "1.16.2",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
- "engines": {
- "node": ">= 0.10.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/express/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/express/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT"
- },
- "node_modules/extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "license": "MIT"
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
- },
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
- },
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true
- },
- "node_modules/fecha": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
- "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
- "license": "MIT"
- },
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
- "dependencies": {
- "flat-cache": "^4.0.0"
- },
- "engines": {
- "node": ">=16.0.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/finalhandler": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
- "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "encodeurl": "~2.0.0",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/finalhandler/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/finalhandler/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT"
- },
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "bin": {
- "flat": "cli.js"
- }
- },
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
- "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
- "dev": true
- },
- "node_modules/fn.name": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
- "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
- "license": "MIT"
- },
- "node_modules/follow-redirects": {
- "version": "1.15.9",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
- "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
- "node_modules/foreground-child": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
- "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
- "license": "ISC",
- "dependencies": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/form-data": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
- "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
- "license": "MIT",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/fs-constants": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
- "license": "MIT"
- },
- "node_modules/fs-minipass": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
- "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
- "license": "ISC",
- "dependencies": {
- "minipass": "^3.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/fs-minipass/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "license": "ISC"
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/gauge": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
- "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
- "deprecated": "This package is no longer supported.",
- "license": "ISC",
- "dependencies": {
- "aproba": "^1.0.3 || ^2.0.0",
- "color-support": "^1.1.2",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.1",
- "object-assign": "^4.1.1",
- "signal-exit": "^3.0.0",
- "string-width": "^4.2.3",
- "strip-ansi": "^6.0.1",
- "wide-align": "^1.1.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/gauge/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/gauge/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/gauge/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "license": "ISC"
- },
- "node_modules/gauge/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/gauge/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/gaxios": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
- "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
- "license": "Apache-2.0",
- "dependencies": {
- "extend": "^3.0.2",
- "https-proxy-agent": "^5.0.0",
- "is-stream": "^2.0.0",
- "node-fetch": "^2.6.9"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "license": "ISC",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/get-intrinsic": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
- "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==",
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "dunder-proto": "^1.0.0",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "function-bind": "^1.1.2",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "math-intrinsics": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/globals": {
- "version": "15.15.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
- "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/handlebars": {
- "version": "4.7.8",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
- "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.5",
- "neo-async": "^2.6.2",
- "source-map": "^0.6.1",
- "wordwrap": "^1.0.0"
- },
- "bin": {
- "handlebars": "bin/handlebars"
- },
- "engines": {
- "node": ">=0.4.7"
- },
- "optionalDependencies": {
- "uglify-js": "^3.1.4"
- }
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/has-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-unicode": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
- "license": "ISC"
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "he": "bin/he"
- }
- },
- "node_modules/helmet": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.0.0.tgz",
- "integrity": "sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw==",
- "license": "MIT",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/htmlnano": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.1.tgz",
- "integrity": "sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==",
- "license": "MIT",
- "dependencies": {
- "cosmiconfig": "^9.0.0",
- "posthtml": "^0.16.5",
- "timsort": "^0.3.0"
- },
- "peerDependencies": {
- "cssnano": "^7.0.0",
- "postcss": "^8.3.11",
- "purgecss": "^6.0.0",
- "relateurl": "^0.2.7",
- "srcset": "5.0.1",
- "svgo": "^3.0.2",
- "terser": "^5.10.0",
- "uncss": "^0.17.3"
- },
- "peerDependenciesMeta": {
- "cssnano": {
- "optional": true
- },
- "postcss": {
- "optional": true
- },
- "purgecss": {
- "optional": true
- },
- "relateurl": {
- "optional": true
- },
- "srcset": {
- "optional": true
- },
- "svgo": {
- "optional": true
- },
- "terser": {
- "optional": true
- },
- "uncss": {
- "optional": true
- }
- }
- },
- "node_modules/htmlparser2": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
- "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
- "funding": [
- "https://github.com/fb55/htmlparser2?sponsor=1",
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.0.1",
- "entities": "^4.4.0"
- }
- },
- "node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "license": "MIT",
- "dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "BSD-3-Clause"
- },
- "node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
- "dev": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/ignore-by-default": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
- "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "license": "MIT",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "license": "ISC"
- },
- "node_modules/ioredis": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.5.0.tgz",
- "integrity": "sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw==",
- "license": "MIT",
- "dependencies": {
- "@ioredis/commands": "^1.1.1",
- "cluster-key-slot": "^1.1.0",
- "debug": "^4.3.4",
- "denque": "^2.1.0",
- "lodash.defaults": "^4.2.0",
- "lodash.isarguments": "^3.1.0",
- "redis-errors": "^1.2.0",
- "redis-parser": "^3.0.0",
- "standard-as-callback": "^2.1.0"
- },
- "engines": {
- "node": ">=12.22.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/ioredis"
- }
- },
- "node_modules/ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "license": "MIT"
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-json": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz",
- "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==",
- "license": "ISC"
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "license": "MIT"
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "license": "ISC"
- },
- "node_modules/isomorphic-unfetch": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz",
- "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==",
- "license": "MIT",
- "dependencies": {
- "node-fetch": "^2.6.1",
- "unfetch": "^4.2.0"
- }
- },
- "node_modules/istanbul-lib-coverage": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
- "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-report/node_modules/make-dir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
- "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/istanbul-reports": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
- "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/jackspeak": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
- "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^8.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- },
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
- }
- },
- "node_modules/jmespath": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
- "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">= 0.6.0"
- }
- },
- "node_modules/joi": {
- "version": "17.13.3",
- "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
- "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@hapi/hoek": "^9.3.0",
- "@hapi/topo": "^5.1.0",
- "@sideway/address": "^4.1.5",
- "@sideway/formula": "^3.0.1",
- "@sideway/pinpoint": "^2.0.0"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "license": "MIT"
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true
- },
- "node_modules/json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "license": "MIT"
- },
- "node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
- },
- "node_modules/json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true
- },
- "node_modules/jsonwebtoken": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
- "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
- "license": "MIT",
- "dependencies": {
- "jws": "^3.2.2",
- "lodash.includes": "^4.3.0",
- "lodash.isboolean": "^3.0.3",
- "lodash.isinteger": "^4.0.4",
- "lodash.isnumber": "^3.0.3",
- "lodash.isplainobject": "^4.0.6",
- "lodash.isstring": "^4.0.1",
- "lodash.once": "^4.0.0",
- "ms": "^2.1.1",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=12",
- "npm": ">=6"
- }
- },
- "node_modules/juice": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/juice/-/juice-11.0.0.tgz",
- "integrity": "sha512-sGF8hPz9/Wg+YXbaNDqc1Iuoaw+J/P9lBHNQKXAGc9pPNjCd4fyPai0Zxj7MRtdjMr0lcgk5PjEIkP2b8R9F3w==",
- "license": "MIT",
- "dependencies": {
- "cheerio": "^1.0.0",
- "commander": "^12.1.0",
- "mensch": "^0.3.4",
- "slick": "^1.12.2",
- "web-resource-inliner": "^7.0.0"
- },
- "bin": {
- "juice": "bin/juice"
- },
- "engines": {
- "node": ">=18.17"
- }
- },
- "node_modules/juice/node_modules/cheerio": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz",
- "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==",
- "license": "MIT",
- "dependencies": {
- "cheerio-select": "^2.1.0",
- "dom-serializer": "^2.0.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.1.0",
- "encoding-sniffer": "^0.2.0",
- "htmlparser2": "^9.1.0",
- "parse5": "^7.1.2",
- "parse5-htmlparser2-tree-adapter": "^7.0.0",
- "parse5-parser-stream": "^7.1.2",
- "undici": "^6.19.5",
- "whatwg-mimetype": "^4.0.0"
- },
- "engines": {
- "node": ">=18.17"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
- }
- },
- "node_modules/juice/node_modules/htmlparser2": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
- "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
- "funding": [
- "https://github.com/fb55/htmlparser2?sponsor=1",
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.1.0",
- "entities": "^4.5.0"
- }
- },
- "node_modules/just-extend": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
- "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/jwa": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
- "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
- "license": "MIT",
- "dependencies": {
- "buffer-equal-constant-time": "1.0.1",
- "ecdsa-sig-formatter": "1.0.11",
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/jws": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
- "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
- "license": "MIT",
- "dependencies": {
- "jwa": "^1.4.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/kareem": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
- "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/kuler": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
- "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
- "license": "MIT"
- },
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/lilconfig": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
- "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
- "license": "MIT",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/antonk52"
- }
- },
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "license": "MIT"
- },
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "license": "MIT"
- },
- "node_modules/lodash.camelcase": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
- "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
- "license": "MIT"
- },
- "node_modules/lodash.defaults": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
- "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
- "license": "MIT"
- },
- "node_modules/lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.includes": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
- "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
- "license": "MIT"
- },
- "node_modules/lodash.isarguments": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
- "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
- "license": "MIT"
- },
- "node_modules/lodash.isboolean": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
- "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
- "license": "MIT"
- },
- "node_modules/lodash.isinteger": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
- "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
- "license": "MIT"
- },
- "node_modules/lodash.isnumber": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
- "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
- "license": "MIT"
- },
- "node_modules/lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
- "license": "MIT"
- },
- "node_modules/lodash.isstring": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
- "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
- "license": "MIT"
- },
- "node_modules/lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
- "license": "MIT"
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true
- },
- "node_modules/lodash.once": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
- "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
- "license": "MIT"
- },
- "node_modules/lodash.uniq": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
- "license": "MIT"
- },
- "node_modules/log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/logform": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
- "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
- "license": "MIT",
- "dependencies": {
- "@colors/colors": "1.6.0",
- "@types/triple-beam": "^1.3.2",
- "fecha": "^4.2.0",
- "ms": "^2.1.1",
- "safe-stable-stringify": "^2.3.1",
- "triple-beam": "^1.3.0"
- },
- "engines": {
- "node": ">= 12.0.0"
- }
- },
- "node_modules/long": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
- "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
- "license": "Apache-2.0"
- },
- "node_modules/loupe": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
- "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
- "license": "ISC"
- },
- "node_modules/luxon": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
- "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/mailersend": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/mailersend/-/mailersend-2.3.0.tgz",
- "integrity": "sha512-pe498Ry7VaAb+oqcYqmPw1V7FlECG/mcqahQ3SiK54en4ZkyRwjyxoQwA9VU4s3npB+I44LlQGUudObZQe4/jA==",
- "license": "MIT",
- "dependencies": {
- "gaxios": "^5.0.1",
- "isomorphic-unfetch": "^3.1.0",
- "qs": "^6.11.0"
- }
- },
- "node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "license": "MIT",
- "dependencies": {
- "semver": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/math-intrinsics": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "license": "CC0-1.0"
- },
- "node_modules/media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/memory-pager": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
- "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
- "license": "MIT"
- },
- "node_modules/mensch": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz",
- "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==",
- "license": "MIT"
- },
- "node_modules/merge-descriptors": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
- "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
- "license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/minizlib": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
- "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
- "license": "MIT",
- "dependencies": {
- "minipass": "^3.0.0",
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/minizlib/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mjml": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml/-/mjml-5.0.0-alpha.6.tgz",
- "integrity": "sha512-unizId6dKTQSHq1nGnRQqe58kpD7VJu9p+vfMsKO4911/+VCrxkFe0oiwS7Q6XA3rc224MVChO2Mvueyeh16jg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "mjml-cli": "5.0.0-alpha.6",
- "mjml-core": "5.0.0-alpha.6",
- "mjml-preset-core": "5.0.0-alpha.6",
- "mjml-validator": "5.0.0-alpha.6"
- },
- "bin": {
- "mjml": "bin/mjml"
- }
- },
- "node_modules/mjml-accordion": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-5.0.0-alpha.6.tgz",
- "integrity": "sha512-K7FS8HZQsfUNkvwUWMjCmaEEtHRrqEDddyaVoOE6+ir4H6aWAJUZqp1j/USQsYwMI3YAS3UDpqQEOagG4pRDqg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-body": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-5.0.0-alpha.6.tgz",
- "integrity": "sha512-otkINnYsBVbKSYqOz/FmhQruOpYzT10w9+UGcOZdBwv+UqDoKHXAD8QeSFZxr7fDSFhc6qeFAKNSY5uciT1ZuQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-button": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-5.0.0-alpha.6.tgz",
- "integrity": "sha512-Z+0J6F2hk7QxKydlABna/vcGtTl7WfMWMLH74x9T1VgNylUAZ2TaWOUAo1AbWICgYBmZfNw1UkDj4EFvb+B1LA==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-carousel": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-5.0.0-alpha.6.tgz",
- "integrity": "sha512-G2n6D6smGmQEKGByjR52UD14D/0gtVokaEI4wjG7UjisSGYGbDXJW4thfDv2RdwIoozXyGhJD3vRuHsoCPeFnw==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-cli": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-5.0.0-alpha.6.tgz",
- "integrity": "sha512-mHmw5MCLNImcUUnYwl1yPF3meAd2EApnriMp8DTEN3zCqXsMg1RV8EB1npp4Camwz8HvVMTvEjSoVotwNWxAMg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "chokidar": "^3.0.0",
- "glob": "^10.3.10",
- "lodash": "^4.17.21",
- "minimatch": "^9.0.3",
- "mjml-core": "5.0.0-alpha.6",
- "mjml-parser-xml": "5.0.0-alpha.6",
- "mjml-validator": "5.0.0-alpha.6",
- "yargs": "^17.7.2"
- },
- "bin": {
- "mjml-cli": "bin/mjml"
- }
- },
- "node_modules/mjml-column": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-5.0.0-alpha.6.tgz",
- "integrity": "sha512-FWpmyCH1kzV3g0P6po1OMVOLTgQXDTbo6z30ew+CylUYnn1w5Vh30T1ZvSPYQDc2jGqjsX1zuWO/JUjOJ5B2RQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-core": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-5.0.0-alpha.6.tgz",
- "integrity": "sha512-rrGr+xrOCnJ+3V/+LeqA4BCp7jrXiRq0q37FlmSs+etE86yNqMMFMgEFEbeGYTTQO07WuqXlhPSuq85ucAoivg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "cheerio": "1.0.0-rc.12",
- "cssnano": "^7.0.1",
- "detect-node": "^2.0.4",
- "htmlnano": "^2.1.1",
- "juice": "^11.0.0",
- "lodash": "^4.17.21",
- "mjml-parser-xml": "5.0.0-alpha.6",
- "mjml-validator": "5.0.0-alpha.6",
- "postcss": "^8.4.33",
- "prettier": "^3.2.4"
- }
- },
- "node_modules/mjml-divider": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-5.0.0-alpha.6.tgz",
- "integrity": "sha512-lw0rQNn2Y4LRcF/ad4JUjVRsyXhYXdaos6yOXPFjRN8aK1T4p7XctZyP5RMl7m8YfXTNxxyN//hN3bQI5DwvgQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-group": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-5.0.0-alpha.6.tgz",
- "integrity": "sha512-No9EeJC9GSpqmxfo0laCmUU0w0xhHb3HaliFJPh7YlzObyRE3nvawHtPYpfNW3lgYNl0U7oYMlN9AQum/DaW2Q==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-5.0.0-alpha.6.tgz",
- "integrity": "sha512-owBfZUcwHV2Wjow32QkRNZClbVHIAthacskvowAODUlOfTG1Xj0czlAM6iCG73cSqmZFuocOD9hy47y0l00Qeg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-attributes": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-5.0.0-alpha.6.tgz",
- "integrity": "sha512-CW/E+IEw3MOLTWkTMbN6egPwoeg22Iup+fICAH9rmITD6SBJ6f37/MFiACSmpkDQIlWdFokBFFKpikbPWFJKQA==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-breakpoint": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-5.0.0-alpha.6.tgz",
- "integrity": "sha512-3RsHM0l3VU+NhG4MAsXevLH50s/uOJqbZznGVKkgEBsodKDhFK2ndlmUWZ5ywDO6H2g/qDrZse4c1K43uGoyzA==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-font": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-5.0.0-alpha.6.tgz",
- "integrity": "sha512-+HZ/Ppd/hfFgob9M27Tz9qio/vWwyTwjo40PsYSI6zXKmIUHC5icBcHaezUKMDGadgd+11aXlPLVfs6zGb0hVg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-html-attributes": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-5.0.0-alpha.6.tgz",
- "integrity": "sha512-INI3irUFHozLfJCW0pi/499DhlwqpsFEoGQ++NdHlFH87hbQ2XTNlrTHWpgjhSuiJkRJinPYHphRuNyM3KLyVw==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-preview": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-5.0.0-alpha.6.tgz",
- "integrity": "sha512-HGLcYmJ7q4aYD5VNTUKIvk6wvpXUawI5j4yl9m+DEhk2ptKYmayxLpjiAvH+LioNuPh0zTCGt7U9f5Ja0948gQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-style": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-5.0.0-alpha.6.tgz",
- "integrity": "sha512-yQcHIvZGH641irNt21r4hzODp5rEI/qKJRvGVJ6vzyGm7poYbaoxhK7Re9cRu+xZxLpkiTRgf1DYFjyZN45SiQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-head-title": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-5.0.0-alpha.6.tgz",
- "integrity": "sha512-m6dGCAItgobZSJ7wlETFPth6rU0+617ZVk1f88Mz5I+YVuOgaAqdNDQWdue5Uj7B19c55NmtdZwsgOKmhHuwow==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-hero": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-5.0.0-alpha.6.tgz",
- "integrity": "sha512-OLOKTK/VW4fQOH8yQz1e5kKHfyF0heECghPhmxgnEHigJLbaJy/rmb4x4aq0Lr74Bme8hsMjkR+oktGej1c4iQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-image": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-5.0.0-alpha.6.tgz",
- "integrity": "sha512-NENjbEOzobM0iQLlxJUNuqiNlFSP5E1DfiP37bY8smYmpVe4MXi5HC5ajTH5ZBKWsTN4/UVZL7t+ZSqniTnS5w==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-navbar": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-5.0.0-alpha.6.tgz",
- "integrity": "sha512-bj328dixWJqox9CaA/rvIKsTZ7c5g1mN9B0gL2vuzVMXgX4qnXkzIfN2hw98TdffaKGsKXC6N25iIDVocj1RrQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-parser-xml": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-5.0.0-alpha.6.tgz",
- "integrity": "sha512-2gxzFJXBFq6l8f2/HlDq7MBLtWQja0KPSqePxPBOXmNLjqw6bdSflnQu+OhWVptdywuHm907LNIQVIq0wSo2FQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "detect-node": "2.1.0",
- "htmlparser2": "^9.1.0",
- "lodash": "^4.17.21"
- }
- },
- "node_modules/mjml-parser-xml/node_modules/htmlparser2": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
- "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
- "funding": [
- "https://github.com/fb55/htmlparser2?sponsor=1",
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.1.0",
- "entities": "^4.5.0"
- }
- },
- "node_modules/mjml-preset-core": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-5.0.0-alpha.6.tgz",
- "integrity": "sha512-6K9sPXlxQfe9Vx/8/wAFW2Sy6ImWU1AP37VG08Ge7Mj117cNeKk3S1piagbj6LpyTd7E7AueO8G+xzvmC5YBBw==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "mjml-accordion": "5.0.0-alpha.6",
- "mjml-body": "5.0.0-alpha.6",
- "mjml-button": "5.0.0-alpha.6",
- "mjml-carousel": "5.0.0-alpha.6",
- "mjml-column": "5.0.0-alpha.6",
- "mjml-divider": "5.0.0-alpha.6",
- "mjml-group": "5.0.0-alpha.6",
- "mjml-head": "5.0.0-alpha.6",
- "mjml-head-attributes": "5.0.0-alpha.6",
- "mjml-head-breakpoint": "5.0.0-alpha.6",
- "mjml-head-font": "5.0.0-alpha.6",
- "mjml-head-html-attributes": "5.0.0-alpha.6",
- "mjml-head-preview": "5.0.0-alpha.6",
- "mjml-head-style": "5.0.0-alpha.6",
- "mjml-head-title": "5.0.0-alpha.6",
- "mjml-hero": "5.0.0-alpha.6",
- "mjml-image": "5.0.0-alpha.6",
- "mjml-navbar": "5.0.0-alpha.6",
- "mjml-raw": "5.0.0-alpha.6",
- "mjml-section": "5.0.0-alpha.6",
- "mjml-social": "5.0.0-alpha.6",
- "mjml-spacer": "5.0.0-alpha.6",
- "mjml-table": "5.0.0-alpha.6",
- "mjml-text": "5.0.0-alpha.6",
- "mjml-wrapper": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-raw": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-5.0.0-alpha.6.tgz",
- "integrity": "sha512-tY2P+g7bcydVHrk0NPPm2sd+rIB9l5TdCeIdtw+NSOXoHN7vXVezcdr7UYK8TePibzeymh8w9YTWEAIN8fWvLg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-section": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-5.0.0-alpha.6.tgz",
- "integrity": "sha512-gJL+9t9hiAfnXl3gG481Xum1qzURXs6bZ+rZnU47R/070+kinOJTCHt82hFGctjyDi264r/BeM6H6y+m0FIq/w==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-social": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-5.0.0-alpha.6.tgz",
- "integrity": "sha512-vPRBYHKeEUwEiVwXLrXgQnWBblizdQTlrZ2V45xEH9+3Jqolw3SlTw+uoZzhcFSBNl7+ytdQgwp7gRC7Bn5IYQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-spacer": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-5.0.0-alpha.6.tgz",
- "integrity": "sha512-ZyLcoAElvkWnijny2eW0ulX6RxoDQT8AZwv5pJ8O16s0mnfcPAPMaZGTCQZHxPbACKJcCKsIoZrQllQuShf3XA==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-table": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-5.0.0-alpha.6.tgz",
- "integrity": "sha512-ze7iNRyT6uX099KxZoV33u1jOlibmfxkUUY/8BXbyfWUIgcwRzb1DfKtIWT/GOqI2WAqh5ZwHd5XZgueimttEA==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-text": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-5.0.0-alpha.6.tgz",
- "integrity": "sha512-k7/pUgwZo9ZwnoCVvohsGdpOrJuLpScH183ZxjyPYO8+kOruSFpE2yrGhK/jeLaV4UhdXndNk8dz80wbN0fKjw==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6"
- }
- },
- "node_modules/mjml-validator": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-5.0.0-alpha.6.tgz",
- "integrity": "sha512-kL5IJGYXdNN7VunXXJEWyAP40oODRiSNu5NIx5moOXREPHvn7lRqi/XS8MXa48/UBLmKMbltDSQ0DcnESJbodQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9"
- }
- },
- "node_modules/mjml-wrapper": {
- "version": "5.0.0-alpha.6",
- "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-5.0.0-alpha.6.tgz",
- "integrity": "sha512-qJisqqkQrtq4U74BTgRaNoPjpf2PwYff/QxH5yvkFDXK3fYEoNt011K7lrm/+u5wS9mx4msHZN0WA4TI/EeeOw==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "lodash": "^4.17.21",
- "mjml-core": "5.0.0-alpha.6",
- "mjml-section": "5.0.0-alpha.6"
- }
- },
- "node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/mkdirp-classic": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
- "license": "MIT"
- },
- "node_modules/mocha": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz",
- "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^4.1.3",
- "browser-stdout": "^1.3.1",
- "chokidar": "^3.5.3",
- "debug": "^4.3.5",
- "diff": "^5.2.0",
- "escape-string-regexp": "^4.0.0",
- "find-up": "^5.0.0",
- "glob": "^10.4.5",
- "he": "^1.2.0",
- "js-yaml": "^4.1.0",
- "log-symbols": "^4.1.0",
- "minimatch": "^5.1.6",
- "ms": "^2.1.3",
- "serialize-javascript": "^6.0.2",
- "strip-json-comments": "^3.1.1",
- "supports-color": "^8.1.1",
- "workerpool": "^6.5.1",
- "yargs": "^17.7.2",
- "yargs-parser": "^21.1.1",
- "yargs-unparser": "^2.0.0"
- },
- "bin": {
- "_mocha": "bin/_mocha",
- "mocha": "bin/mocha.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/mocha/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/mocha/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
- "node_modules/mongodb": {
- "version": "6.13.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz",
- "integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==",
- "license": "Apache-2.0",
- "dependencies": {
- "@mongodb-js/saslprep": "^1.1.9",
- "bson": "^6.10.1",
- "mongodb-connection-string-url": "^3.0.0"
- },
- "engines": {
- "node": ">=16.20.1"
- },
- "peerDependencies": {
- "@aws-sdk/credential-providers": "^3.188.0",
- "@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
- "gcp-metadata": "^5.2.0",
- "kerberos": "^2.0.1",
- "mongodb-client-encryption": ">=6.0.0 <7",
- "snappy": "^7.2.2",
- "socks": "^2.7.1"
- },
- "peerDependenciesMeta": {
- "@aws-sdk/credential-providers": {
- "optional": true
- },
- "@mongodb-js/zstd": {
- "optional": true
- },
- "gcp-metadata": {
- "optional": true
- },
- "kerberos": {
- "optional": true
- },
- "mongodb-client-encryption": {
- "optional": true
- },
- "snappy": {
- "optional": true
- },
- "socks": {
- "optional": true
- }
- }
- },
- "node_modules/mongodb-connection-string-url": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
- "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@types/whatwg-url": "^11.0.2",
- "whatwg-url": "^13.0.0"
- }
- },
- "node_modules/mongoose": {
- "version": "8.10.1",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.10.1.tgz",
- "integrity": "sha512-5beTeBZnJNndRXU9rxPol0JmTWZMAtgkPbooROkGilswvrZALDERY4cJrGZmgGwDS9dl0mxiB7si+Mv9Yms2fg==",
- "license": "MIT",
- "dependencies": {
- "bson": "^6.10.1",
- "kareem": "2.6.3",
- "mongodb": "~6.13.0",
- "mpath": "0.9.0",
- "mquery": "5.0.0",
- "ms": "2.1.3",
- "sift": "17.1.3"
- },
- "engines": {
- "node": ">=16.20.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mongoose"
- }
- },
- "node_modules/mpath": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
- "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/mquery": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
- "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
- "license": "MIT",
- "dependencies": {
- "debug": "4.x"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/msgpackr": {
- "version": "1.11.2",
- "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz",
- "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==",
- "license": "MIT",
- "optionalDependencies": {
- "msgpackr-extract": "^3.0.2"
- }
- },
- "node_modules/msgpackr-extract": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
- "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "node-gyp-build-optional-packages": "5.2.2"
- },
- "bin": {
- "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
- },
- "optionalDependencies": {
- "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
- }
- },
- "node_modules/multer": {
- "version": "1.4.5-lts.1",
- "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
- "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
- "license": "MIT",
- "dependencies": {
- "append-field": "^1.0.0",
- "busboy": "^1.0.0",
- "concat-stream": "^1.5.2",
- "mkdirp": "^0.5.4",
- "object-assign": "^4.1.1",
- "type-is": "^1.6.4",
- "xtend": "^4.0.0"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/nan": {
- "version": "2.22.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
- "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
- },
- "node_modules/negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/neo-async": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
- "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
- "license": "MIT"
- },
- "node_modules/nise": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz",
- "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "@sinonjs/fake-timers": "^13.0.1",
- "@sinonjs/text-encoding": "^0.7.3",
- "just-extend": "^6.2.0",
- "path-to-regexp": "^8.1.0"
- }
- },
- "node_modules/nise/node_modules/path-to-regexp": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
- "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/node-abort-controller": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
- "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
- "license": "MIT"
- },
- "node_modules/node-addon-api": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
- "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
- "license": "MIT"
- },
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "license": "MIT",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/node-fetch/node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "license": "MIT"
- },
- "node_modules/node-fetch/node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "license": "BSD-2-Clause"
- },
- "node_modules/node-fetch/node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
- "node_modules/node-gyp-build-optional-packages": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
- "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "detect-libc": "^2.0.1"
- },
- "bin": {
- "node-gyp-build-optional-packages": "bin.js",
- "node-gyp-build-optional-packages-optional": "optional.js",
- "node-gyp-build-optional-packages-test": "build-test.js"
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
- "license": "MIT"
- },
- "node_modules/nodemailer": {
- "version": "6.10.0",
- "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
- "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
- "license": "MIT-0",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/nodemon": {
- "version": "3.1.9",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
- "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chokidar": "^3.5.2",
- "debug": "^4",
- "ignore-by-default": "^1.0.1",
- "minimatch": "^3.1.2",
- "pstree.remy": "^1.1.8",
- "semver": "^7.5.3",
- "simple-update-notifier": "^2.0.0",
- "supports-color": "^5.5.0",
- "touch": "^3.1.0",
- "undefsafe": "^2.0.5"
- },
- "bin": {
- "nodemon": "bin/nodemon.js"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/nodemon"
- }
- },
- "node_modules/nodemon/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/nodemon/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/nodemon/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/nodemon/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/nopt": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
- "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
- "license": "ISC",
- "dependencies": {
- "abbrev": "1"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/npmlog": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
- "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
- "deprecated": "This package is no longer supported.",
- "license": "ISC",
- "dependencies": {
- "are-we-there-yet": "^2.0.0",
- "console-control-strings": "^1.1.0",
- "gauge": "^3.0.0",
- "set-blocking": "^2.0.0"
- }
- },
- "node_modules/nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "boolbase": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/fb55/nth-check?sponsor=1"
- }
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-inspect": {
- "version": "1.13.3",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
- "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "license": "MIT",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/one-time": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
- "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
- "license": "MIT",
- "dependencies": {
- "fn.name": "1.x.x"
- }
- },
- "node_modules/optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "dev": true,
- "dependencies": {
- "deep-is": "^0.1.3",
- "fast-levenshtein": "^2.0.6",
- "levn": "^0.4.1",
- "prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.5"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/package-json-from-dist": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
- "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
- "license": "BlueOak-1.0.0"
- },
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "license": "MIT",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parse5": {
- "version": "7.2.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
- "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
- "license": "MIT",
- "dependencies": {
- "entities": "^4.5.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/parse5-htmlparser2-tree-adapter": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
- "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
- "license": "MIT",
- "dependencies": {
- "domhandler": "^5.0.3",
- "parse5": "^7.0.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/parse5-parser-stream": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
- "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
- "license": "MIT",
- "dependencies": {
- "parse5": "^7.0.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-scurry": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
- "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^10.2.0",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
- },
- "engines": {
- "node": ">=16 || 14 >=14.18"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/path-to-regexp": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
- "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
- "license": "MIT"
- },
- "node_modules/pathval": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
- "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.16"
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/ping": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/ping/-/ping-0.4.4.tgz",
- "integrity": "sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==",
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/postcss": {
- "version": "8.4.49",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
- "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/postcss-calc": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.2.tgz",
- "integrity": "sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==",
- "license": "MIT",
- "dependencies": {
- "postcss-selector-parser": "^6.1.2",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12 || ^20.9 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.38"
- }
- },
- "node_modules/postcss-colormin": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.2.tgz",
- "integrity": "sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "caniuse-api": "^3.0.0",
- "colord": "^2.9.3",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-convert-values": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.4.tgz",
- "integrity": "sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-comments": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.3.tgz",
- "integrity": "sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==",
- "license": "MIT",
- "dependencies": {
- "postcss-selector-parser": "^6.1.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-duplicates": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.1.tgz",
- "integrity": "sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==",
- "license": "MIT",
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-empty": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz",
- "integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==",
- "license": "MIT",
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-discard-overridden": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz",
- "integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==",
- "license": "MIT",
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-merge-longhand": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.4.tgz",
- "integrity": "sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0",
- "stylehacks": "^7.0.4"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-merge-rules": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.4.tgz",
- "integrity": "sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "caniuse-api": "^3.0.0",
- "cssnano-utils": "^5.0.0",
- "postcss-selector-parser": "^6.1.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-font-values": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz",
- "integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-gradients": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz",
- "integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==",
- "license": "MIT",
- "dependencies": {
- "colord": "^2.9.3",
- "cssnano-utils": "^5.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-params": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.2.tgz",
- "integrity": "sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "cssnano-utils": "^5.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-minify-selectors": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.4.tgz",
- "integrity": "sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==",
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "postcss-selector-parser": "^6.1.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-charset": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz",
- "integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==",
- "license": "MIT",
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-display-values": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz",
- "integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-positions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz",
- "integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-repeat-style": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz",
- "integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-string": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz",
- "integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-timing-functions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz",
- "integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-unicode": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.2.tgz",
- "integrity": "sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-url": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz",
- "integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-normalize-whitespace": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz",
- "integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-ordered-values": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz",
- "integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==",
- "license": "MIT",
- "dependencies": {
- "cssnano-utils": "^5.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-reduce-initial": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.2.tgz",
- "integrity": "sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "caniuse-api": "^3.0.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-reduce-transforms": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz",
- "integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-svgo": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.1.tgz",
- "integrity": "sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.2.0",
- "svgo": "^3.3.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >= 18"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-unique-selectors": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.3.tgz",
- "integrity": "sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==",
- "license": "MIT",
- "dependencies": {
- "postcss-selector-parser": "^6.1.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "license": "MIT"
- },
- "node_modules/posthtml": {
- "version": "0.16.6",
- "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz",
- "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==",
- "license": "MIT",
- "dependencies": {
- "posthtml-parser": "^0.11.0",
- "posthtml-render": "^3.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/posthtml-parser": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz",
- "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==",
- "license": "MIT",
- "dependencies": {
- "htmlparser2": "^7.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/posthtml-parser/node_modules/dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "license": "BSD-2-Clause",
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/posthtml-parser/node_modules/domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.2.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/posthtml-parser/node_modules/domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "node_modules/posthtml-parser/node_modules/entities": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
- "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/posthtml-parser/node_modules/htmlparser2": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
- "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
- "funding": [
- "https://github.com/fb55/htmlparser2?sponsor=1",
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.2",
- "domutils": "^2.8.0",
- "entities": "^3.0.1"
- }
- },
- "node_modules/posthtml-render": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz",
- "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==",
- "license": "MIT",
- "dependencies": {
- "is-json": "^2.0.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/prettier": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
- "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
- "license": "MIT",
- "bin": {
- "prettier": "bin/prettier.cjs"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "license": "MIT"
- },
- "node_modules/protobufjs": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
- "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
- "hasInstallScript": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.2",
- "@protobufjs/base64": "^1.1.2",
- "@protobufjs/codegen": "^2.0.4",
- "@protobufjs/eventemitter": "^1.1.0",
- "@protobufjs/fetch": "^1.1.0",
- "@protobufjs/float": "^1.0.2",
- "@protobufjs/inquire": "^1.1.0",
- "@protobufjs/path": "^1.1.2",
- "@protobufjs/pool": "^1.1.0",
- "@protobufjs/utf8": "^1.1.0",
- "@types/node": ">=13.7.0",
- "long": "^5.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "license": "MIT",
- "dependencies": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
- "license": "MIT"
- },
- "node_modules/pstree.remy": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
- "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/pump": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
- "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/qs": {
- "version": "6.13.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
- "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "side-channel": "^1.0.6"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/rambda": {
- "version": "7.5.0",
- "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz",
- "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==",
- "dev": true
- },
- "node_modules/randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.0"
- }
- },
- "node_modules/range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/raw-body": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
- "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
- "license": "MIT",
- "dependencies": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/redis-errors": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
- "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/redis-parser": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
- "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
- "license": "MIT",
- "dependencies": {
- "redis-errors": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "license": "MIT"
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rimraf/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/rimraf/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rimraf/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/safe-stable-stringify": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
- "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "license": "MIT"
- },
- "node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/send": {
- "version": "0.19.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
- "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/send/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/send/node_modules/debug/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT"
- },
- "node_modules/send/node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "randombytes": "^2.1.0"
- }
- },
- "node_modules/serve-static": {
- "version": "1.16.2",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
- "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
- "license": "MIT",
- "dependencies": {
- "encodeurl": "~2.0.0",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.19.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "license": "ISC"
- },
- "node_modules/setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
- "license": "ISC"
- },
- "node_modules/sharp": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
- "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "dependencies": {
- "color": "^4.2.3",
- "detect-libc": "^2.0.3",
- "semver": "^7.6.3"
- },
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.33.5",
- "@img/sharp-darwin-x64": "0.33.5",
- "@img/sharp-libvips-darwin-arm64": "1.0.4",
- "@img/sharp-libvips-darwin-x64": "1.0.4",
- "@img/sharp-libvips-linux-arm": "1.0.5",
- "@img/sharp-libvips-linux-arm64": "1.0.4",
- "@img/sharp-libvips-linux-s390x": "1.0.4",
- "@img/sharp-libvips-linux-x64": "1.0.4",
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
- "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
- "@img/sharp-linux-arm": "0.33.5",
- "@img/sharp-linux-arm64": "0.33.5",
- "@img/sharp-linux-s390x": "0.33.5",
- "@img/sharp-linux-x64": "0.33.5",
- "@img/sharp-linuxmusl-arm64": "0.33.5",
- "@img/sharp-linuxmusl-x64": "0.33.5",
- "@img/sharp-wasm32": "0.33.5",
- "@img/sharp-win32-ia32": "0.33.5",
- "@img/sharp-win32-x64": "0.33.5"
- }
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
- "side-channel-map": "^1.0.1",
- "side-channel-weakmap": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3",
- "side-channel-map": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/sift": {
- "version": "17.1.3",
- "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
- "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
- "license": "MIT"
- },
- "node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.3.1"
- }
- },
- "node_modules/simple-swizzle/node_modules/is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
- "license": "MIT"
- },
- "node_modules/simple-update-notifier": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
- "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/sinon": {
- "version": "19.0.2",
- "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz",
- "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "@sinonjs/fake-timers": "^13.0.2",
- "@sinonjs/samsam": "^8.0.1",
- "diff": "^7.0.0",
- "nise": "^6.1.1",
- "supports-color": "^7.2.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/sinon"
- }
- },
- "node_modules/sinon/node_modules/diff": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
- "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/slick": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz",
- "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==",
- "license": "MIT (http://mootools.net/license.txt)",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/sparse-bitfield": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
- "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
- "license": "MIT",
- "dependencies": {
- "memory-pager": "^1.0.2"
- }
- },
- "node_modules/split-ca": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
- "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==",
- "license": "ISC"
- },
- "node_modules/ssh2": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
- "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
- "hasInstallScript": true,
- "dependencies": {
- "asn1": "^0.2.6",
- "bcrypt-pbkdf": "^1.0.2"
- },
- "engines": {
- "node": ">=10.16.0"
- },
- "optionalDependencies": {
- "cpu-features": "~0.0.10",
- "nan": "^2.20.0"
- }
- },
- "node_modules/ssl-checker": {
- "version": "2.0.10",
- "resolved": "https://registry.npmjs.org/ssl-checker/-/ssl-checker-2.0.10.tgz",
- "integrity": "sha512-SS6rrZocToJWHM1p6iVNb583ybB3UqT1fymCHSWuEdXDUqKA6O1D5Fb8KJVmhj3XKXE82IEWcr+idJrc4jUzFQ==",
- "license": "MIT"
- },
- "node_modules/stack-trace": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
- "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/standard-as-callback": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
- "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
- "license": "MIT"
- },
- "node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/streamsearch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
- "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.2.0"
- }
- },
- "node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/string-width-cjs": {
- "name": "string-width",
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/string-width-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/strip-ansi-cjs": {
- "name": "strip-ansi",
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/stylehacks": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.4.tgz",
- "integrity": "sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.23.3",
- "postcss-selector-parser": "^6.1.2"
- },
- "engines": {
- "node": "^18.12.0 || ^20.9.0 || >=22.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/svgo": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
- "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
- "license": "MIT",
- "dependencies": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^5.1.0",
- "css-tree": "^2.3.1",
- "css-what": "^6.1.0",
- "csso": "^5.0.5",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "svgo": "bin/svgo"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/svgo"
- }
- },
- "node_modules/svgo/node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "license": "MIT",
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/swagger-ui-dist": {
- "version": "5.18.2",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz",
- "integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@scarf/scarf": "=1.4.0"
- }
- },
- "node_modules/swagger-ui-express": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
- "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
- "license": "MIT",
- "dependencies": {
- "swagger-ui-dist": ">=5.0.0"
- },
- "engines": {
- "node": ">= v0.10.32"
- },
- "peerDependencies": {
- "express": ">=4.0.0 || >=5.0.0-beta"
- }
- },
- "node_modules/tar": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
- "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
- "license": "ISC",
- "dependencies": {
- "chownr": "^2.0.0",
- "fs-minipass": "^2.0.0",
- "minipass": "^5.0.0",
- "minizlib": "^2.1.1",
- "mkdirp": "^1.0.3",
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/tar-fs": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
- "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
- "license": "MIT",
- "dependencies": {
- "chownr": "^1.1.1",
- "mkdirp-classic": "^0.5.2",
- "pump": "^3.0.0",
- "tar-stream": "^2.0.0"
- }
- },
- "node_modules/tar-fs/node_modules/chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "license": "ISC"
- },
- "node_modules/tar-stream": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
- "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
- "license": "MIT",
- "dependencies": {
- "bl": "^4.0.3",
- "end-of-stream": "^1.4.1",
- "fs-constants": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^3.1.1"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/tar/node_modules/minipass": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
- "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "license": "ISC",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/tar/node_modules/mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "license": "MIT",
- "bin": {
- "mkdirp": "bin/cmd.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/test-exclude": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
- "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^10.4.1",
- "minimatch": "^9.0.4"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/text-hex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
- "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
- "license": "MIT"
- },
- "node_modules/timsort": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
- "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==",
- "license": "MIT"
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.6"
- }
- },
- "node_modules/touch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
- "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "nodetouch": "bin/nodetouch.js"
- }
- },
- "node_modules/tr46": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
- "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
- "license": "MIT",
- "dependencies": {
- "punycode": "^2.3.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/triple-beam": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
- "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
- "license": "MIT",
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/tslib": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD"
- },
- "node_modules/tweetnacl": {
- "version": "0.14.5",
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
- "license": "Unlicense"
- },
- "node_modules/type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "license": "MIT",
- "dependencies": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
- "license": "MIT"
- },
- "node_modules/uglify-js": {
- "version": "3.19.3",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
- "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
- "license": "BSD-2-Clause",
- "optional": true,
- "bin": {
- "uglifyjs": "bin/uglifyjs"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/undefsafe": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
- "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/undici": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz",
- "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==",
- "license": "MIT",
- "engines": {
- "node": ">=18.17"
- }
- },
- "node_modules/undici-types": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
- "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
- "license": "MIT"
- },
- "node_modules/unfetch": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
- "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
- "license": "MIT"
- },
- "node_modules/unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
- "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "escalade": "^3.2.0",
- "picocolors": "^1.1.0"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "license": "MIT"
- },
- "node_modules/utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
- "node_modules/v8-to-istanbul": {
- "version": "9.3.0",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
- "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.12",
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^2.0.0"
- },
- "engines": {
- "node": ">=10.12.0"
- }
- },
- "node_modules/valid-data-url": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz",
- "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/web-resource-inliner": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-7.0.0.tgz",
- "integrity": "sha512-NlfnGF8MY9ZUwFjyq3vOUBx7KwF8bmE+ywR781SB0nWB6MoMxN4BA8gtgP1KGTZo/O/AyWJz7HZpR704eaj4mg==",
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^4.1.1",
- "escape-goat": "^3.0.0",
- "htmlparser2": "^5.0.0",
- "mime": "^2.4.6",
- "valid-data-url": "^3.0.0"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/web-resource-inliner/node_modules/dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.2.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/domhandler": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
- "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.0.1"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.2.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "license": "BSD-2-Clause",
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/htmlparser2": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz",
- "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==",
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.0.1",
- "domhandler": "^3.3.0",
- "domutils": "^2.4.2",
- "entities": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/fb55/htmlparser2?sponsor=1"
- }
- },
- "node_modules/web-resource-inliner/node_modules/mime": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
- "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
- "license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-encoding": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
- "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
- "license": "MIT",
- "dependencies": {
- "iconv-lite": "0.6.3"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/whatwg-encoding/node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/whatwg-mimetype": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
- "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/whatwg-url": {
- "version": "13.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
- "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
- "license": "MIT",
- "dependencies": {
- "tr46": "^4.1.1",
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/wide-align": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
- "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^1.0.2 || 2 || 3 || 4"
- }
- },
- "node_modules/wide-align/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wide-align/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/wide-align/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wide-align/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/winston": {
- "version": "3.17.0",
- "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
- "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
- "license": "MIT",
- "dependencies": {
- "@colors/colors": "^1.6.0",
- "@dabh/diagnostics": "^2.0.2",
- "async": "^3.2.3",
- "is-stream": "^2.0.0",
- "logform": "^2.7.0",
- "one-time": "^1.0.0",
- "readable-stream": "^3.4.0",
- "safe-stable-stringify": "^2.3.1",
- "stack-trace": "0.0.x",
- "triple-beam": "^1.3.0",
- "winston-transport": "^4.9.0"
- },
- "engines": {
- "node": ">= 12.0.0"
- }
- },
- "node_modules/winston-transport": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
- "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
- "license": "MIT",
- "dependencies": {
- "logform": "^2.7.0",
- "readable-stream": "^3.6.2",
- "triple-beam": "^1.3.0"
- },
- "engines": {
- "node": ">= 12.0.0"
- }
- },
- "node_modules/word-wrap": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/wordwrap": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
- "license": "MIT"
- },
- "node_modules/workerpool": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
- "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs": {
- "name": "wrap-ansi",
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/wrap-ansi-cjs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "license": "ISC"
- },
- "node_modules/xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4"
- }
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "license": "ISC",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "license": "ISC"
- },
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "license": "MIT",
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-unparser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
- "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "camelcase": "^6.0.0",
- "decamelize": "^4.0.0",
- "flat": "^5.0.2",
- "is-plain-obj": "^2.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yargs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/yargs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yargs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- }
- }
-}
diff --git a/Server/package.json b/Server/package.json
deleted file mode 100644
index d944a1d0c..000000000
--- a/Server/package.json
+++ /dev/null
@@ -1,56 +0,0 @@
-{
- "name": "server",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "type": "module",
- "scripts": {
- "test": "c8 mocha",
- "dev": "nodemon index.js",
- "lint": "eslint .",
- "lint-fix": "eslint --fix .",
- "format": "prettier --write .",
- "format-check": "prettier --check ."
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "dependencies": {
- "axios": "^1.7.2",
- "bcrypt": "5.1.1",
- "bullmq": "5.41.2",
- "cors": "^2.8.5",
- "dockerode": "4.0.4",
- "dotenv": "^16.4.5",
- "express": "^4.19.2",
- "handlebars": "^4.7.8",
- "helmet": "^8.0.0",
- "ioredis": "^5.4.2",
- "jmespath": "^0.16.0",
- "joi": "^17.13.1",
- "jsonwebtoken": "9.0.2",
- "mailersend": "^2.2.0",
- "mjml": "^5.0.0-alpha.4",
- "mongoose": "^8.3.3",
- "multer": "1.4.5-lts.1",
- "nodemailer": "^6.9.14",
- "ping": "0.4.4",
- "sharp": "0.33.5",
- "ssl-checker": "2.0.10",
- "swagger-ui-express": "5.0.1",
- "winston": "^3.13.0"
- },
- "devDependencies": {
- "@eslint/js": "^9.17.0",
- "c8": "10.1.3",
- "chai": "5.2.0",
- "eslint": "^9.17.0",
- "eslint-plugin-mocha": "^10.5.0",
- "esm": "3.2.25",
- "globals": "^15.14.0",
- "mocha": "11.1.0",
- "nodemon": "3.1.9",
- "prettier": "^3.3.3",
- "sinon": "19.0.2"
- }
-}
diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js
deleted file mode 100644
index cebe9a211..000000000
--- a/Server/routes/authRoute.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Router } from "express";
-import { verifyJWT } from "../middleware/verifyJWT.js";
-import { verifyOwnership } from "../middleware/verifyOwnership.js";
-import { isAllowed } from "../middleware/isAllowed.js";
-import multer from "multer";
-import User from "../db/models/User.js";
-
-const upload = multer();
-
-class AuthRoutes {
- constructor(authController) {
- this.router = Router();
- this.authController = authController;
- this.initRoutes();
- }
-
- initRoutes() {
- this.router.post(
- "/register",
- upload.single("profileImage"),
- this.authController.registerUser
- );
- this.router.post("/login", this.authController.loginUser);
- this.router.post("/refresh", this.authController.refreshAuthToken);
-
- this.router.put(
- "/user/:userId",
- upload.single("profileImage"),
- verifyJWT,
- this.authController.editUser
- );
-
- this.router.get("/users/superadmin", this.authController.checkSuperadminExists);
-
- this.router.get(
- "/users",
- verifyJWT,
- isAllowed(["admin", "superadmin"]),
- this.authController.getAllUsers
- );
-
- this.router.delete(
- "/user/:userId",
- verifyJWT,
- verifyOwnership(User, "userId"),
- this.authController.deleteUser
- );
-
- // Recovery routes
- this.router.post("/recovery/request", this.authController.requestRecovery);
- this.router.post("/recovery/validate", this.authController.validateRecovery);
- this.router.post("/recovery/reset/", this.authController.resetPassword);
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default AuthRoutes;
diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js
deleted file mode 100644
index bab45e4d4..000000000
--- a/Server/routes/checkRoute.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Router } from "express";
-import { verifyOwnership } from "../middleware/verifyOwnership.js";
-import { isAllowed } from "../middleware/isAllowed.js";
-import Monitor from "../db/models/Monitor.js";
-
-class CheckRoutes {
- constructor(checkController) {
- this.router = Router();
- this.checkController = checkController;
- this.initRoutes();
- }
-
- initRoutes() {
- this.router.get("/:monitorId", this.checkController.getChecksByMonitor);
- this.router.post(
- "/:monitorId",
- verifyOwnership(Monitor, "monitorId"),
- this.checkController.createCheck
- );
- this.router.delete(
- "/:monitorId",
- verifyOwnership(Monitor, "monitorId"),
- this.checkController.deleteChecks
- );
-
- this.router.get("/team/:teamId", this.checkController.getChecksByTeam);
-
- this.router.delete(
- "/team/:teamId",
- isAllowed(["admin", "superadmin"]),
- this.checkController.deleteChecksByTeamId
- );
-
- this.router.put(
- "/team/ttl",
- isAllowed(["admin", "superadmin"]),
- this.checkController.updateChecksTTL
- );
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default CheckRoutes;
diff --git a/Server/routes/distributedUptimeRoute.js b/Server/routes/distributedUptimeRoute.js
deleted file mode 100644
index 3fbd88d3a..000000000
--- a/Server/routes/distributedUptimeRoute.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Router } from "express";
-
-class DistributedUptimeRoutes {
- constructor(distributedUptimeController) {
- this.router = Router();
- this.distributedUptimeController = distributedUptimeController;
- this.initRoutes();
- }
- initRoutes() {
- this.router.post("/callback", this.distributedUptimeController.resultsCallback);
- this.router.get(
- "/monitors/:teamId",
- this.distributedUptimeController.getDistributedUptimeMonitors
- );
- this.router.get(
- "/monitors/details/:monitorId",
- this.distributedUptimeController.getDistributedUptimeMonitorDetails
- );
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default DistributedUptimeRoutes;
diff --git a/Server/routes/inviteRoute.js b/Server/routes/inviteRoute.js
deleted file mode 100644
index c66c573f1..000000000
--- a/Server/routes/inviteRoute.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Router } from "express";
-import { verifyJWT } from "../middleware/verifyJWT.js";
-import { isAllowed } from "../middleware/isAllowed.js";
-
-class InviteRoutes {
- constructor(inviteController) {
- this.router = Router();
- this.inviteController = inviteController;
- this.initRoutes();
- }
-
- initRoutes() {
- this.router.post(
- "/",
- isAllowed(["admin", "superadmin"]),
- verifyJWT,
- this.inviteController.issueInvitation
- );
- this.router.post("/verify", this.inviteController.inviteVerifyController);
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default InviteRoutes;
diff --git a/Server/routes/maintenanceWindowRoute.js b/Server/routes/maintenanceWindowRoute.js
deleted file mode 100644
index 2665c2bb8..000000000
--- a/Server/routes/maintenanceWindowRoute.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Router } from "express";
-import { verifyOwnership } from "../middleware/verifyOwnership.js";
-import Monitor from "../db/models/Monitor.js";
-
-class MaintenanceWindowRoutes {
- constructor(maintenanceWindowController) {
- this.router = Router();
- this.maintenanceWindowController = maintenanceWindowController;
- this.initRoutes();
- }
- initRoutes() {
- this.router.post("/", this.maintenanceWindowController.createMaintenanceWindows);
-
- this.router.get(
- "/monitor/:monitorId",
- verifyOwnership(Monitor, "monitorId"),
- this.maintenanceWindowController.getMaintenanceWindowsByMonitorId
- );
-
- this.router.get(
- "/team/",
- this.maintenanceWindowController.getMaintenanceWindowsByTeamId
- );
-
- this.router.get("/:id", this.maintenanceWindowController.getMaintenanceWindowById);
-
- this.router.put("/:id", this.maintenanceWindowController.editMaintenanceWindow);
-
- this.router.delete("/:id", this.maintenanceWindowController.deleteMaintenanceWindow);
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default MaintenanceWindowRoutes;
diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js
deleted file mode 100644
index 964bbee7a..000000000
--- a/Server/routes/monitorRoute.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Router } from "express";
-import { isAllowed } from "../middleware/isAllowed.js";
-import { fetchMonitorCertificate } from "../controllers/controllerUtils.js";
-
-class MonitorRoutes {
- constructor(monitorController) {
- this.router = Router();
- this.monitorController = monitorController;
- this.initRoutes();
- }
-
- initRoutes() {
- this.router.get("/", this.monitorController.getAllMonitors);
- this.router.get("/uptime", this.monitorController.getAllMonitorsWithUptimeStats);
- this.router.get("/stats/:monitorId", this.monitorController.getMonitorStatsById);
- this.router.get(
- "/hardware/details/:monitorId",
- this.monitorController.getHardwareDetailsById
- );
-
- this.router.get(
- "/uptime/details/:monitorId",
- this.monitorController.getUptimeDetailsById
- );
- this.router.get("/certificate/:monitorId", (req, res, next) => {
- this.monitorController.getMonitorCertificate(
- req,
- res,
- next,
- fetchMonitorCertificate
- );
- });
- this.router.get("/:monitorId", this.monitorController.getMonitorById);
-
- this.router.get("/team/:teamId", this.monitorController.getMonitorsByTeamId);
-
- this.router.get(
- "/resolution/url",
- isAllowed(["admin", "superadmin"]),
- this.monitorController.checkEndpointResolution
- );
-
- this.router.delete(
- "/:monitorId",
- isAllowed(["admin", "superadmin"]),
- this.monitorController.deleteMonitor
- );
-
- this.router.post(
- "/",
- isAllowed(["admin", "superadmin"]),
- this.monitorController.createMonitor
- );
-
- this.router.put(
- "/:monitorId",
- isAllowed(["admin", "superadmin"]),
- this.monitorController.editMonitor
- );
-
- this.router.delete(
- "/",
- isAllowed(["superadmin"]),
- this.monitorController.deleteAllMonitors
- );
-
- this.router.post(
- "/pause/:monitorId",
- isAllowed(["admin", "superadmin"]),
- this.monitorController.pauseMonitor
- );
-
- this.router.post(
- "/demo",
- isAllowed(["admin", "superadmin"]),
- this.monitorController.addDemoMonitors
- );
-
- this.router.post("/seed", isAllowed(["superadmin"]), this.monitorController.seedDb);
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default MonitorRoutes;
diff --git a/Server/routes/notificationRoute.js b/Server/routes/notificationRoute.js
deleted file mode 100644
index 6e13a53d1..000000000
--- a/Server/routes/notificationRoute.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import express from 'express';
-import { verifyJWT } from '../middleware/verifyJWT.js';
-
-class NotificationRoutes {
- constructor(notificationController) {
- this.notificationController = notificationController;
- this.router = express.Router();
- this.initializeRoutes();
- }
-
- initializeRoutes() {
- this.router.post(
- '/trigger',
- verifyJWT,
- this.notificationController.triggerNotification
- );
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default NotificationRoutes;
\ No newline at end of file
diff --git a/Server/routes/queueRoute.js b/Server/routes/queueRoute.js
deleted file mode 100644
index af0e9d08e..000000000
--- a/Server/routes/queueRoute.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Router } from "express";
-
-class QueueRoutes {
- constructor(queueController) {
- this.router = Router();
- this.queueController = queueController;
- this.initRoutes();
- }
- initRoutes() {
- this.router.get("/metrics", this.queueController.getMetrics);
- this.router.get("/jobs", this.queueController.getJobs);
- this.router.post("/jobs", this.queueController.addJob);
- this.router.post("/obliterate", this.queueController.obliterateQueue);
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default QueueRoutes;
diff --git a/Server/routes/settingsRoute.js b/Server/routes/settingsRoute.js
deleted file mode 100644
index 8da262427..000000000
--- a/Server/routes/settingsRoute.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Router } from "express";
-import { isAllowed } from "../middleware/isAllowed.js";
-
-class SettingsRoutes {
- constructor(settingsController) {
- this.router = Router();
- this.settingsController = settingsController;
- this.initRoutes();
- }
-
- initRoutes() {
- this.router.get("/", this.settingsController.getAppSettings);
- this.router.put(
- "/",
- isAllowed(["superadmin"]),
- this.settingsController.updateAppSettings
- );
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default SettingsRoutes;
diff --git a/Server/routes/statusPageRoute.js b/Server/routes/statusPageRoute.js
deleted file mode 100644
index 3ed6e5684..000000000
--- a/Server/routes/statusPageRoute.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Router } from "express";
-import { verifyJWT } from "../middleware/verifyJWT.js";
-import multer from "multer";
-const upload = multer();
-
-class StatusPageRoutes {
- constructor(statusPageController) {
- this.router = Router();
- this.statusPageController = statusPageController;
- this.initRoutes();
- }
-
- initRoutes() {
- this.router.get("/", this.statusPageController.getStatusPage);
- this.router.get("/team/:teamId", this.statusPageController.getStatusPagesByTeamId);
- this.router.get("/:url", this.statusPageController.getStatusPageByUrl);
-
- this.router.post(
- "/",
- upload.single("logo"),
- verifyJWT,
- this.statusPageController.createStatusPage
- );
- this.router.put(
- "/",
- upload.single("logo"),
- verifyJWT,
- this.statusPageController.updateStatusPage
- );
- this.router.delete("/:url(*)", verifyJWT, this.statusPageController.deleteStatusPage);
- }
-
- getRouter() {
- return this.router;
- }
-}
-
-export default StatusPageRoutes;
diff --git a/Server/service/emailService.js b/Server/service/emailService.js
deleted file mode 100644
index 3aeb2c8a8..000000000
--- a/Server/service/emailService.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import { fileURLToPath } from "url";
-import path from "path";
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-
-const SERVICE_NAME = "EmailService";
-
-/**
- * Represents an email service that can load templates, build, and send emails.
- */
-class EmailService {
- static SERVICE_NAME = SERVICE_NAME;
- /**
- * Constructs an instance of the EmailService, initializing template loaders and the email transporter.
- * @param {Object} settingsService - The settings service to get email configuration.
- * @param {Object} fs - The file system module.
- * @param {Object} path - The path module.
- * @param {Function} compile - The Handlebars compile function.
- * @param {Function} mjml2html - The MJML to HTML conversion function.
- * @param {Object} nodemailer - The nodemailer module.
- * @param {Object} logger - The logger module.
- */
- constructor(settingsService, fs, path, compile, mjml2html, nodemailer, logger) {
- this.settingsService = settingsService;
- this.fs = fs;
- this.path = path;
- this.compile = compile;
- this.mjml2html = mjml2html;
- this.nodemailer = nodemailer;
- this.logger = logger;
-
- /**
- * Loads an email template from the filesystem.
- *
- * @param {string} templateName - The name of the template to load.
- * @returns {Function} A compiled template function that can be used to generate HTML email content.
- */
- this.loadTemplate = (templateName) => {
- try {
- const templatePath = this.path.join(
- __dirname,
- `../templates/${templateName}.mjml`
- );
- const templateContent = this.fs.readFileSync(templatePath, "utf8");
- return this.compile(templateContent);
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "loadTemplate",
- stack: error.stack,
- });
- }
- };
-
- /**
- * A lookup object to access preloaded email templates.
- * @type {Object.}
- * TODO Load less used templates in their respective functions
- */
- this.templateLookup = {
- welcomeEmailTemplate: this.loadTemplate("welcomeEmail"),
- employeeActivationTemplate: this.loadTemplate("employeeActivation"),
- noIncidentsThisWeekTemplate: this.loadTemplate("noIncidentsThisWeek"),
- serverIsDownTemplate: this.loadTemplate("serverIsDown"),
- serverIsUpTemplate: this.loadTemplate("serverIsUp"),
- passwordResetTemplate: this.loadTemplate("passwordReset"),
- hardwareIncidentTemplate: this.loadTemplate("hardwareIncident"),
- };
-
- /**
- * The email transporter used to send emails.
- * @type {Object}
- */
-
- const { systemEmailHost, systemEmailPort, systemEmailAddress, systemEmailPassword } =
- this.settingsService.getSettings();
-
- const emailConfig = {
- host: systemEmailHost,
- port: systemEmailPort,
- secure: true,
- auth: {
- user: systemEmailAddress,
- pass: systemEmailPassword,
- },
- };
-
- this.transporter = this.nodemailer.createTransport(emailConfig);
- }
-
- /**
- * Asynchronously builds and sends an email using a specified template and context.
- *
- * @param {string} template - The name of the template to use for the email body.
- * @param {Object} context - The data context to render the template with.
- * @param {string} to - The recipient's email address.
- * @param {string} subject - The subject of the email.
- * @returns {Promise} A promise that resolves to the messageId of the sent email.
- */
- buildAndSendEmail = async (template, context, to, subject) => {
- const buildHtml = async (template, context) => {
- try {
- const mjml = this.templateLookup[template](context);
- const html = await this.mjml2html(mjml);
- return html.html;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "buildAndSendEmail",
- stack: error.stack,
- });
- }
- };
-
- const sendEmail = async (to, subject, html) => {
- try {
- const info = await this.transporter.sendMail({
- to: to,
- subject: subject,
- html: html,
- });
- return info;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "sendEmail",
- stack: error.stack,
- });
- }
- };
- const html = await buildHtml(template, context);
- const info = await sendEmail(to, subject, html);
- return info?.messageId;
- };
-}
-export default EmailService;
diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js
deleted file mode 100644
index be36383d2..000000000
--- a/Server/service/jobQueue.js
+++ /dev/null
@@ -1,606 +0,0 @@
-const QUEUE_NAMES = ["uptime", "pagespeed", "hardware", "distributed"];
-const SERVICE_NAME = "JobQueue";
-const JOBS_PER_WORKER = 5;
-const QUEUE_LOOKUP = {
- hardware: "hardware",
- http: "uptime",
- ping: "uptime",
- port: "uptime",
- docker: "uptime",
- pagespeed: "pagespeed",
- distributed_http: "distributed",
-};
-const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`;
-
-
-class NewJobQueue {
- static SERVICE_NAME = SERVICE_NAME;
-
- constructor(
- db,
- statusService,
- networkService,
- notificationService,
- settingsService,
- stringService,
- logger,
- Queue,
- Worker
- ) {
- const settings = settingsService.getSettings() || {};
- const { redisHost = "127.0.0.1", redisPort = 6379 } = settings;
- const connection = {
- host: redisHost,
- port: redisPort,
- };
-
- this.queues = {};
- this.workers = {};
-
- this.connection = connection;
- this.db = db;
- this.networkService = networkService;
- this.statusService = statusService;
- this.notificationService = notificationService;
- this.settingsService = settingsService;
- this.logger = logger;
- this.Worker = Worker;
- this.stringService = stringService;
-
- QUEUE_NAMES.forEach((name) => {
- this.queues[name] = new Queue(name, { connection });
- this.workers[name] = [];
- });
- }
-
- /**
- * Initializes job queues by adding jobs for all active monitors
- * @async
- * @function initJobQueue
- * @description Retrieves all monitors from the database and adds jobs for active ones to their respective queues
- * @throws {Error} If there's an error retrieving monitors or adding jobs
- * @returns {Promise}
- */
- async initJobQueue() {
- const monitors = await this.db.getAllMonitors();
- await Promise.all(
- monitors
- .filter((monitor) => monitor.isActive)
- .map(async (monitor) => {
- try {
- await this.addJob(monitor._id, monitor);
- } catch (error) {
- this.logger.error({
- message: `Failed to add job for monitor ${monitor._id}:`,
- service: SERVICE_NAME,
- method: "initJobQueue",
- stack: error.stack,
- });
- }
- })
- );
- }
-
- /**
- * Checks if a monitor is currently in a maintenance window
- * @async
- * @param {string} monitorId - The ID of the monitor to check
- * @returns {Promise} Returns true if the monitor is in an active maintenance window, false otherwise
- * @throws {Error} If there's an error retrieving maintenance windows from the database
- * @description
- * Retrieves all maintenance windows for a monitor and checks if any are currently active.
- * A maintenance window is considered active if:
- * 1. The window is marked as active AND
- * 2. Either:
- * - Current time falls between start and end times
- * - For repeating windows: Current time falls between any repeated interval
- */
- async isInMaintenanceWindow(monitorId) {
- const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId(monitorId);
- // Check for active maintenance window:
- const maintenanceWindowIsActive = maintenanceWindows.reduce((acc, window) => {
- if (window.active) {
- const start = new Date(window.start);
- const end = new Date(window.end);
- const now = new Date();
- const repeatInterval = window.repeat || 0;
-
- // If start is < now and end > now, we're in maintenance
- if (start <= now && end >= now) return true;
-
- // If maintenance window was set in the past with a repeat,
- // we need to advance start and end to see if we are in range
-
- while (start < now && repeatInterval !== 0) {
- start.setTime(start.getTime() + repeatInterval);
- end.setTime(end.getTime() + repeatInterval);
- if (start <= now && end >= now) {
- return true;
- }
- }
- return false;
- }
- return acc;
- }, false);
- return maintenanceWindowIsActive;
- }
-
- /**
- * Creates a job processing handler for monitor checks
- * @function createJobHandler
- * @returns {Function} An async function that processes monitor check jobs
- * @description
- * Creates and returns a job handler that:
- * 1. Checks if monitor is in maintenance window
- * 2. If not in maintenance, performs network status check
- * 3. Updates monitor status in database
- * 4. Triggers notifications if status changed
- *
- * @param {Object} job - The job to process
- * @param {Object} job.data - The monitor data
- * @param {string} job.data._id - Monitor ID
- * @param {string} job.id - Job ID
- *
- * @throws {Error} Logs errors but doesn't throw them to prevent job failure
- * @returns {Promise} Resolves when job processing is complete
- */
- createJobHandler() {
- return async (job) => {
- try {
- // Get all maintenance windows for this monitor
- await job.updateProgress(0);
- const monitorId = job.data._id;
- const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId);
- // If a maintenance window is active, we're done
-
- if (maintenanceWindowActive) {
- await job.updateProgress(100);
- this.logger.info({
- message: `Monitor ${monitorId} is in maintenance window`,
- service: SERVICE_NAME,
- method: "createWorker",
- });
- return false;
- }
-
- // Get the current status
- await job.updateProgress(30);
- const networkResponse = await this.networkService.getStatus(job);
- if (job.data.type === "distributed_http") {
- return;
- }
- // Handle status change
- await job.updateProgress(60);
- const { monitor, statusChanged, prevStatus } =
- await this.statusService.updateStatus(networkResponse);
- // Handle notifications
- await job.updateProgress(80);
- this.notificationService
- .handleNotifications({
- ...networkResponse,
- monitor,
- prevStatus,
- statusChanged,
- })
- .catch((error) => {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "createJobHandler",
- details: `Error sending notifications for job ${job.id}: ${error.message}`,
- stack: error.stack,
- });
- });
- await job.updateProgress(100);
- return true;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: error.service ?? SERVICE_NAME,
- method: error.method ?? "createJobHandler",
- details: `Error processing job ${job.id}: ${error.message}`,
- stack: error.stack,
- });
- throw error;
- }
- };
- }
-
- /**
- * Creates a new worker for processing jobs in a queue
- * @param {Queue} queue - The BullMQ queue to create a worker for
- * @returns {Worker} A new BullMQ worker instance
- * @description
- * Creates and configures a new worker with:
- * - Queue-specific job handler
- * - Redis connection settings
- * - Default worker options
- * The worker processes jobs from the specified queue using the job handler
- * created by createJobHandler()
- *
- * @throws {Error} If worker creation fails or connection is invalid
- */
- createWorker(queue) {
- const worker = new this.Worker(queue.name, this.createJobHandler(), {
- connection: this.connection,
- concurrency: 5,
- });
- return worker;
- }
-
- /**
- * Gets stats related to the workers
- * This is used for scaling workers right now
- * In the future we will likely want to scale based on server performance metrics
- * CPU Usage & memory usage, if too high, scale down workers.
- * When to scale up? If jobs are taking too long to complete?
- * @async
- * @returns {Promise} - Returns the worker stats
- */
- async getWorkerStats(queue) {
- try {
- const jobs = await queue.getRepeatableJobs();
- const load = jobs.length / this.workers[queue.name].length;
- return { jobs, load };
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "getWorkerStats") : null;
- throw error;
- }
- }
-
- /**
- * Scales workers up or down based on queue load
- * @async
- * @param {Object} workerStats - Statistics about current worker load
- * @param {number} workerStats.load - Current load per worker
- * @param {Array} workerStats.jobs - Array of current jobs
- * @param {Queue} queue - The BullMQ queue to scale workers for
- * @returns {Promise} True if scaling occurred, false if no scaling was needed
- * @throws {Error} If no workers array exists for the queue
- * @description
- * Scales workers based on these rules:
- * - Maintains minimum of 5 workers
- * - Adds workers if load exceeds JOBS_PER_WORKER
- * - Removes workers if load is below JOBS_PER_WORKER
- * - Creates initial workers if none exist
- * Worker scaling is calculated based on excess jobs or excess capacity
- */
- async scaleWorkers(workerStats, queue) {
- const workers = this.workers[queue.name];
- if (workers === undefined) {
- throw new Error(`No workers found for ${queue.name}`);
- }
-
- if (workers.length === 0) {
- // There are no workers, need to add one
- for (let i = 0; i < 5; i++) {
- const worker = this.createWorker(queue);
- workers.push(worker);
- }
- return true;
- }
- if (workerStats.load > JOBS_PER_WORKER) {
- // Find out how many more jobs we have than current workers can handle
- const excessJobs = workerStats.jobs.length - workers.length * JOBS_PER_WORKER;
- // Divide by jobs/worker to find out how many workers to add
- const workersToAdd = Math.ceil(excessJobs / JOBS_PER_WORKER);
- for (let i = 0; i < workersToAdd; i++) {
- const worker = this.createWorker(queue);
- workers.push(worker);
- }
- return true;
- }
-
- if (workerStats.load < JOBS_PER_WORKER) {
- // Find out how much excess capacity we have
- const workerCapacity = workers.length * JOBS_PER_WORKER;
- const excessCapacity = workerCapacity - workerStats.jobs.length;
- // Calculate how many workers to remove
- let workersToRemove = Math.floor(excessCapacity / JOBS_PER_WORKER); // Make sure there are always at least 5
- while (workersToRemove > 0 && workers.length > 5) {
- const worker = workers.pop();
- workersToRemove--;
- await worker.close().catch((error) => {
- // Catch the error instead of throwing it
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "scaleWorkers",
- stack: error.stack,
- });
- });
- }
- return true;
- }
- return false;
- }
-
- /**
- * Gets all jobs in the queue.
- *
- * @async
- * @returns {Promise>}
- * @throws {Error} - Throws error if getting jobs fails
- */
- async getJobs(queue) {
- try {
- const jobs = await queue.getRepeatableJobs();
- return jobs;
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "getJobs") : null;
- throw error;
- }
- }
-
- /**
- * Retrieves detailed statistics about jobs and workers for all queues
- * @async
- * @returns {Promise} Queue statistics object
- * @throws {Error} If there's an error retrieving job information
- * @description
- * Returns an object with statistics for each queue including:
- * - List of jobs with their URLs and current states
- * - Number of workers assigned to the queue
- */
- async getJobStats() {
- try {
- let stats = {};
- await Promise.all(
- QUEUE_NAMES.map(async (name) => {
- const queue = this.queues[name];
- const jobs = await queue.getJobs();
- const ret = await Promise.all(
- jobs.map(async (job) => {
- const state = await job.getState();
- return { url: job.data.url, state, progress: job.progress };
- })
- );
- stats[name] = { jobs: ret, workers: this.workers[name].length };
- })
- );
- return stats;
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "getJobStats") : null;
- throw error;
- }
- }
-
- /**
- * Adds both immediate and repeatable jobs to the appropriate queue
- * @async
- * @param {string} jobName - Name identifier for the job
- * @param {Object} monitor - Job data and configuration
- * @param {string} monitor.type - Type of monitor/queue ('uptime', 'pagespeed', 'hardware')
- * @param {string} [monitor.url] - URL to monitor (optional)
- * @param {number} [monitor.interval=60000] - Repeat interval in milliseconds
- * @param {string} monitor._id - Monitor ID
- * @throws {Error} If queue not found for payload type
- * @throws {Error} If job addition fails
- * @description
- * 1. Identifies correct queue based on payload type
- * 2. Adds immediate job execution
- * 3. Adds repeatable job with specified interval
- * 4. Scales workers based on updated queue load
- * Jobs are configured with exponential backoff, single attempt,
- * and automatic removal on completion
- */
- async addJob(jobName, monitor) {
- try {
- this.logger.info({
- message: `Adding job ${monitor?.url ?? "No URL"}`,
- service: SERVICE_NAME,
- method: "addJob",
- });
-
- // Find the correct queue
-
- const queue = this.queues[QUEUE_LOOKUP[monitor.type]];
- if (queue === undefined) {
- throw new Error(`Queue for ${monitor.type} not found`);
- }
-
- const jobTemplate = {
- name: jobName,
- data: monitor,
- opts: {
- attempts: 1,
- backoff: {
- type: "exponential",
- delay: 1000,
- },
- removeOnComplete: true,
- removeOnFail: false,
- timeout: 1 * 60 * 1000,
- },
- };
-
- const schedulerId = getSchedulerId(monitor);
- await queue.upsertJobScheduler(
- schedulerId,
- { every: monitor?.interval ?? 60000 },
- jobTemplate
- );
-
- const workerStats = await this.getWorkerStats(queue);
- await this.scaleWorkers(workerStats, queue);
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "addJob") : null;
- throw error;
- }
- }
-
- /**
- * Deletes a repeatable job from its queue and adjusts worker scaling
- * @async
- * @param {Object} monitor - Monitor object containing job details
- * @param {string} monitor._id - ID of the monitor/job to delete
- * @param {string} monitor.type - Type of monitor determining queue selection
- * @param {number} monitor.interval - Job repeat interval in milliseconds
- * @throws {Error} If queue not found for monitor type
- * @throws {Error} If job deletion fails
- * @description
- * 1. Identifies correct queue based on monitor type
- * 2. Removes repeatable job using monitor ID and interval
- * 3. Logs success or failure of deletion
- * 4. Updates worker scaling based on new queue load
- * Returns void but logs operation result
- */
- async deleteJob(monitor) {
- try {
- const queue = this.queues[QUEUE_LOOKUP[monitor.type]];
- const schedulerId = getSchedulerId(monitor);
- const wasDeleted = await queue.removeJobScheduler(schedulerId);
-
- if (wasDeleted === true) {
- this.logger.info({
- message: this.stringService.jobQueueDeleteJob,
- service: SERVICE_NAME,
- method: "deleteJob",
- details: `Deleted job ${monitor._id}`,
- });
- const workerStats = await this.getWorkerStats(queue);
- await this.scaleWorkers(workerStats, queue);
- } else {
- this.logger.error({
- message: this.stringService.jobQueueDeleteJob,
- service: SERVICE_NAME,
- method: "deleteJob",
- details: `Failed to delete job ${monitor._id}`,
- });
- }
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "deleteJob") : null;
- throw error;
- }
- }
-
- /**
- * Retrieves comprehensive metrics for all queues
- * @async
- * @returns {Promise>} Object with metrics for each queue
- * @throws {Error} If metrics retrieval fails
- * @description
- * Collects the following metrics for each queue:
- * - Number of waiting jobs
- * - Number of active jobs
- * - Number of completed jobs
- * - Number of failed jobs
- * - Number of delayed jobs
- * - Number of repeatable jobs
- * - Number of active workers
- *
- * @typedef {Object} QueueMetrics
- * @property {number} waiting - Count of jobs waiting to be processed
- * @property {number} active - Count of jobs currently being processed
- * @property {number} completed - Count of successfully completed jobs
- * @property {number} failed - Count of failed jobs
- * @property {number} delayed - Count of delayed jobs
- * @property {number} repeatableJobs - Count of repeatable job patterns
- * @property {number} workers - Count of active workers for this queue
- */
- async getMetrics() {
- try {
- let metrics = {};
-
- await Promise.all(
- QUEUE_NAMES.map(async (name) => {
- const queue = this.queues[name];
- const workers = this.workers[name];
- const [waiting, active, completed, failed, delayed, repeatableJobs] =
- await Promise.all([
- queue.getWaitingCount(),
- queue.getActiveCount(),
- queue.getCompletedCount(),
- queue.getFailedCount(),
- queue.getDelayedCount(),
- queue.getRepeatableJobs(),
- ]);
-
- metrics[name] = {
- waiting,
- active,
- completed,
- failed,
- delayed,
- repeatableJobs: repeatableJobs.length,
- workers: workers.length,
- };
- })
- );
-
- return metrics;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: SERVICE_NAME,
- method: "getMetrics",
- stack: error.stack,
- });
- }
- }
-
- /**
- * @async
- * @returns {Promise} - Returns true if obliteration is successful
- */
-
- async obliterate() {
- try {
- this.logger.info({
- message: "Attempting to obliterate job queue...",
- service: SERVICE_NAME,
- method: "obliterate",
- });
- await Promise.all(
- QUEUE_NAMES.map(async (name) => {
- const queue = this.queues[name];
- await queue.pause();
- const jobs = await this.getJobs(queue);
-
- // Remove all repeatable jobs
- for (const job of jobs) {
- await queue.removeRepeatableByKey(job.key);
- await queue.remove(job.id);
- }
- })
- );
-
- // Close workers
- await Promise.all(
- QUEUE_NAMES.map(async (name) => {
- const workers = this.workers[name];
- await Promise.all(
- workers.map(async (worker) => {
- await worker.close();
- })
- );
- })
- );
-
- QUEUE_NAMES.forEach(async (name) => {
- const queue = this.queues[name];
- await queue.obliterate();
- });
-
- const metrics = await this.getMetrics();
- this.logger.info({
- message: this.stringService.jobQueueObliterate,
- service: SERVICE_NAME,
- method: "obliterate",
- details: metrics,
- });
- return true;
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "obliterate") : null;
- throw error;
- }
- }
-}
-
-export default NewJobQueue;
diff --git a/Server/service/networkService.js b/Server/service/networkService.js
deleted file mode 100644
index 984939bd4..000000000
--- a/Server/service/networkService.js
+++ /dev/null
@@ -1,509 +0,0 @@
-import jmespath from "jmespath";
-const SERVICE_NAME = "NetworkService";
-const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
-
-/**
- * Constructs a new NetworkService instance.
- *
- * @param {Object} axios - The axios instance for HTTP requests.
- * @param {Object} ping - The ping utility for network checks.
- * @param {Object} logger - The logger instance for logging.
- * @param {Object} http - The HTTP utility for network operations.
- * @param {Object} net - The net utility for network operations.
- */
-class NetworkService {
- static SERVICE_NAME = SERVICE_NAME;
-
- constructor(axios, ping, logger, http, Docker, net, stringService) {
- this.TYPE_PING = "ping";
- this.TYPE_HTTP = "http";
- this.TYPE_PAGESPEED = "pagespeed";
- this.TYPE_HARDWARE = "hardware";
- this.TYPE_DOCKER = "docker";
- this.TYPE_PORT = "port";
- this.TYPE_DISTRIBUTED_HTTP = "distributed_http";
- this.SERVICE_NAME = SERVICE_NAME;
- this.NETWORK_ERROR = 5000;
- this.PING_ERROR = 5001;
- this.axios = axios;
- this.ping = ping;
- this.logger = logger;
- this.http = http;
- this.Docker = Docker;
- this.net = net;
- this.stringService = stringService;
- }
-
- /**
- * Times the execution of an asynchronous operation.
- *
- * @param {Function} operation - The asynchronous operation to be timed.
- * @returns {Promise} An object containing the response, response time, and optionally an error.
- * @property {Object|null} response - The response from the operation, or null if an error occurred.
- * @property {number} responseTime - The time taken for the operation to complete, in milliseconds.
- * @property {Error} [error] - The error object if an error occurred during the operation.
- */
- async timeRequest(operation) {
- const startTime = Date.now();
- try {
- const response = await operation();
- const endTime = Date.now();
- const responseTime = endTime - startTime;
- return { response, responseTime };
- } catch (error) {
- const endTime = Date.now();
- const responseTime = endTime - startTime;
- return { response: null, responseTime, error };
- }
- }
-
- /**
- * Sends a ping request to the specified URL and returns the response.
- *
- * @param {Object} job - The job object containing the data for the ping request.
- * @param {Object} job.data - The data object within the job.
- * @param {string} job.data.url - The URL to ping.
- * @param {string} job.data._id - The monitor ID for the ping request.
- * @returns {Promise} An object containing the ping response details.
- * @property {string} monitorId - The monitor ID for the ping request.
- * @property {string} type - The type of request, which is "ping".
- * @property {number} responseTime - The time taken for the ping request to complete, in milliseconds.
- * @property {Object} payload - The response payload from the ping request.
- * @property {boolean} status - The status of the ping request (true if successful, false otherwise).
- * @property {number} code - The response code (200 if successful, error code otherwise).
- * @property {string} message - The message indicating the result of the ping request.
- */
- async requestPing(job) {
- try {
- const url = job.data.url;
- const { response, responseTime, error } = await this.timeRequest(() =>
- this.ping.promise.probe(url)
- );
-
- const pingResponse = {
- monitorId: job.data._id,
- type: "ping",
- responseTime,
- payload: response,
- };
- if (error) {
- pingResponse.status = false;
- pingResponse.code = this.PING_ERROR;
- pingResponse.message = "No response";
- return pingResponse;
- }
-
- pingResponse.code = 200;
- pingResponse.status = response.alive;
- pingResponse.message = "Success";
- return pingResponse;
- } catch (error) {
- error.service = this.SERVICE_NAME;
- error.method = "requestPing";
- throw error;
- }
- }
-
- /**
- * Sends an HTTP GET request to the specified URL and returns the response.
- *
- * @param {Object} job - The job object containing the data for the HTTP request.
- * @param {Object} job.data - The data object within the job.
- * @param {string} job.data.url - The URL to send the HTTP GET request to.
- * @param {string} job.data._id - The monitor ID for the HTTP request.
- * @param {string} [job.data.secret] - Secret for authorization if provided.
- * @returns {Promise} An object containing the HTTP response details.
- * @property {string} monitorId - The monitor ID for the HTTP request.
- * @property {string} type - The type of request, which is "http".
- * @property {number} responseTime - The time taken for the HTTP request to complete, in milliseconds.
- * @property {Object} payload - The response payload from the HTTP request.
- * @property {boolean} status - The status of the HTTP request (true if successful, false otherwise).
- * @property {number} code - The response code (200 if successful, error code otherwise).
- * @property {string} message - The message indicating the result of the HTTP request.
- */
- async requestHttp(job) {
- try {
- const {
- url,
- secret,
- _id,
- name,
- teamId,
- type,
- jsonPath,
- matchMethod,
- expectedValue,
- } = job.data;
- const config = {};
-
- secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
-
- const { response, responseTime, error } = await this.timeRequest(() =>
- this.axios.get(url, config)
- );
-
- const httpResponse = {
- monitorId: _id,
- teamId,
- type,
- responseTime,
- payload: response?.data,
- };
-
- if (error) {
- const code = error.response?.status || this.NETWORK_ERROR;
- httpResponse.code = code;
- httpResponse.status = false;
- httpResponse.message =
- this.http.STATUS_CODES[code] || this.stringService.httpNetworkError;
- return httpResponse;
- }
-
- httpResponse.code = response.status;
-
- if (!expectedValue) {
- // not configure expected value, return
- httpResponse.status = true;
- httpResponse.message = this.http.STATUS_CODES[response.status];
- return httpResponse;
- }
-
- // validate if response data match expected value
- let result = response?.data;
-
- this.logger.info({
- service: this.SERVICE_NAME,
- method: "requestHttp",
- message: `Job: [${name}](${_id}) match result with expected value`,
- details: { expectedValue, result, jsonPath, matchMethod },
- });
-
- if (jsonPath) {
- const contentType = response.headers["content-type"];
-
- const isJson = contentType?.includes("application/json");
- if (!isJson) {
- httpResponse.status = false;
- httpResponse.message = this.stringService.httpNotJson;
- return httpResponse;
- }
-
- try {
- result = jmespath.search(result, jsonPath);
- } catch (error) {
- httpResponse.status = false;
- httpResponse.message = this.stringService.httpJsonPathError;
- return httpResponse;
- }
- }
-
- if (result === null || result === undefined) {
- httpResponse.status = false;
- httpResponse.message = this.stringService.httpEmptyResult;
- return httpResponse;
- }
-
- let match;
- result = typeof result === "object" ? JSON.stringify(result) : result.toString();
- if (matchMethod === "include") match = result.includes(expectedValue);
- else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
- else match = result === expectedValue;
-
- httpResponse.status = match;
- httpResponse.message = match
- ? this.stringService.httpMatchSuccess
- : this.stringService.httpMatchFail;
- return httpResponse;
- } catch (error) {
- error.service = this.SERVICE_NAME;
- error.method = "requestHttp";
- throw error;
- }
- }
-
- /**
- * Sends a request to the Google PageSpeed Insights API for the specified URL and returns the response.
- *
- * @param {Object} job - The job object containing the data for the PageSpeed request.
- * @param {Object} job.data - The data object within the job.
- * @param {string} job.data.url - The URL to analyze with PageSpeed Insights.
- * @param {string} job.data._id - The monitor ID for the PageSpeed request.
- * @returns {Promise} An object containing the PageSpeed response details.
- * @property {string} monitorId - The monitor ID for the PageSpeed request.
- * @property {string} type - The type of request, which is "pagespeed".
- * @property {number} responseTime - The time taken for the PageSpeed request to complete, in milliseconds.
- * @property {Object} payload - The response payload from the PageSpeed request.
- * @property {boolean} status - The status of the PageSpeed request (true if successful, false otherwise).
- * @property {number} code - The response code (200 if successful, error code otherwise).
- * @property {string} message - The message indicating the result of the PageSpeed request.
- */
- async requestPagespeed(job) {
- try {
- const url = job.data.url;
- const updatedJob = { ...job };
- const pagespeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`;
- updatedJob.data.url = pagespeedUrl;
- return await this.requestHttp(updatedJob);
- } catch (error) {
- error.service = this.SERVICE_NAME;
- error.method = "requestPagespeed";
- throw error;
- }
- }
-
- /**
- * Sends an HTTP request to check hardware status and returns the response.
- *
- * @param {Object} job - The job object containing the data for the hardware request.
- * @param {Object} job.data - The data object within the job.
- * @param {string} job.data.url - The URL to send the hardware status request to.
- * @param {string} job.data._id - The monitor ID for the hardware request.
- * @param {string} job.data.type - The type of request, which is "hardware".
- * @returns {Promise} An object containing the hardware status response details.
- * @property {string} monitorId - The monitor ID for the hardware request.
- * @property {string} type - The type of request ("hardware").
- * @property {number} responseTime - The time taken for the request to complete, in milliseconds.
- * @property {Object} payload - The response payload from the hardware status request.
- * @property {boolean} status - The status of the request (true if successful, false otherwise).
- * @property {number} code - The response code (200 if successful, error code otherwise).
- * @property {string} message - The message indicating the result of the hardware status request.
- */
- async requestHardware(job) {
- try {
- return await this.requestHttp(job);
- } catch (error) {
- error.service = this.SERVICE_NAME;
- error.method = "requestHardware";
- throw error;
- }
- }
-
- /**
- * Sends a request to inspect a Docker container and returns its status.
- *
- * @param {Object} job - The job object containing the data for the Docker request.
- * @param {Object} job.data - The data object within the job.
- * @param {string} job.data.url - The container ID or name to inspect.
- * @param {string} job.data._id - The monitor ID for the Docker request.
- * @param {string} job.data.type - The type of request, which is "docker".
- * @returns {Promise} An object containing the Docker container status details.
- * @property {string} monitorId - The monitor ID for the Docker request.
- * @property {string} type - The type of request ("docker").
- * @property {number} responseTime - The time taken for the Docker inspection to complete, in milliseconds.
- * @property {boolean} status - The status of the container (true if running, false otherwise).
- * @property {number} code - The response code (200 if successful, error code otherwise).
- * @property {string} message - The message indicating the result of the Docker inspection.
- */
- async requestDocker(job) {
- try {
- const docker = new this.Docker({
- socketPath: "/var/run/docker.sock",
- handleError: true, // Enable error handling
- });
-
- const containers = await docker.listContainers({ all: true });
- const containerExists = containers.some((c) => c.Id.startsWith(job.data.url));
- if (!containerExists) {
- throw new Error(this.stringService.dockerNotFound);
- }
- const container = docker.getContainer(job.data.url);
-
- const { response, responseTime, error } = await this.timeRequest(() =>
- container.inspect()
- );
-
- const dockerResponse = {
- monitorId: job.data._id,
- type: job.data.type,
- responseTime,
- };
-
- if (error) {
- dockerResponse.status = false;
- dockerResponse.code = error.statusCode || this.NETWORK_ERROR;
- dockerResponse.message =
- error.reason || "Failed to fetch Docker container information";
- return dockerResponse;
- }
- dockerResponse.status = response?.State?.Status === "running" ? true : false;
- dockerResponse.code = 200;
- dockerResponse.message = "Docker container status fetched successfully";
- return dockerResponse;
- } catch (error) {
- error.service = this.SERVICE_NAME;
- error.method = "requestDocker";
- throw error;
- }
- }
-
- async requestPort(job) {
- try {
- const { url, port } = job.data;
- const { response, responseTime, error } = await this.timeRequest(async () => {
- return new Promise((resolve, reject) => {
- const socket = this.net.createConnection(
- {
- host: url,
- port,
- },
- () => {
- socket.end();
- socket.destroy();
- resolve({ success: true });
- }
- );
-
- socket.setTimeout(5000);
- socket.on("timeout", () => {
- socket.destroy();
- reject(new Error("Connection timeout"));
- });
-
- socket.on("error", (err) => {
- socket.destroy();
- reject(err);
- });
- });
- });
-
- const portResponse = {
- monitorId: job.data._id,
- type: job.data.type,
- responseTime,
- };
-
- if (error) {
- portResponse.status = false;
- portResponse.code = this.NETWORK_ERROR;
- portResponse.message = this.stringService.portFail;
- return portResponse;
- }
-
- portResponse.status = response.success;
- portResponse.code = 200;
- portResponse.message = this.stringService.portSuccess;
- return portResponse;
- } catch (error) {
- error.service = this.SERVICE_NAME;
- error.method = "requestTCP";
- throw error;
- }
- }
-
- async requestDistributedHttp(job) {
- try {
- const monitor = job.data;
- const CALLBACK_URL = process.env.CALLBACK_URL;
-
- const response = await this.axios.post(
- UPROCK_ENDPOINT,
- {
- id: monitor._id,
- url: monitor.url,
- callback: `${CALLBACK_URL}/api/v1/distributed-uptime/callback`,
- },
- {
- headers: {
- "Content-Type": "application/json",
- "x-checkmate-key": process.env.UPROCK_API_KEY,
- },
- }
- );
- if (response.data.success === false) {
- throw new Error(response.data.message);
- }
- } catch (error) {
- console.log(error.message);
- error.service = this.SERVICE_NAME;
- error.method = "requestDistributedHttp";
- throw error;
- }
- }
-
- /**
- * Handles unsupported job types by throwing an error with details.
- *
- * @param {string} type - The unsupported job type that was provided
- * @throws {Error} An error with service name, method name and unsupported type message
- */
- handleUnsupportedType(type) {
- const err = new Error(`Unsupported type: ${type}`);
- err.service = this.SERVICE_NAME;
- err.method = "getStatus";
- throw err;
- }
-
- async requestWebhook(platform, url, message) {
- try {
- const response = await this.axios.post(url, message, {
- headers: {
- 'Content-Type': 'application/json'
- }
- });
-
- return {
- type: 'webhook',
- status: true,
- code: response.status,
- message: `Successfully sent ${platform} notification`,
- payload: response.data
- };
-
- } catch (error) {
- this.logger.warn({
- message: error.message,
- service: this.SERVICE_NAME,
- method: 'requestWebhook',
- url,
- platform,
- error: error.message,
- statusCode: error.response?.status,
- responseData: error.response?.data,
- requestPayload: message
- });
-
- return {
- type: 'webhook',
- status: false,
- code: error.response?.status || this.NETWORK_ERROR,
- message: `Failed to send ${platform} notification`,
- payload: error.response?.data
- };
- }
- }
-
-
- /**
- * Gets the status of a job based on its type and returns the appropriate response.
- *
- * @param {Object} job - The job object containing the data for the status request.
- * @param {Object} job.data - The data object within the job.
- * @param {string} job.data.type - The type of the job (e.g., "ping", "http", "pagespeed", "hardware").
- * @returns {Promise} The response object from the appropriate request method.
- * @throws {Error} Throws an error if the job type is unsupported.
- */
- async getStatus(job) {
- const type = job?.data?.type ?? "unknown";
- switch (type) {
- case this.TYPE_PING:
- return await this.requestPing(job);
- case this.TYPE_HTTP:
- return await this.requestHttp(job);
- case this.TYPE_PAGESPEED:
- return await this.requestPagespeed(job);
- case this.TYPE_HARDWARE:
- return await this.requestHardware(job);
- case this.TYPE_DOCKER:
- return await this.requestDocker(job);
- case this.TYPE_PORT:
- return await this.requestPort(job);
- case this.TYPE_DISTRIBUTED_HTTP:
- return await this.requestDistributedHttp(job);
-
- default:
- return this.handleUnsupportedType(type);
- }
- }
-}
-
-export default NetworkService;
diff --git a/Server/service/notificationService.js b/Server/service/notificationService.js
deleted file mode 100644
index 2546df3f9..000000000
--- a/Server/service/notificationService.js
+++ /dev/null
@@ -1,298 +0,0 @@
-const SERVICE_NAME = "NotificationService";
-const TELEGRAM_API_BASE_URL = "https://api.telegram.org/bot";
-const PLATFORM_TYPES = ['telegram', 'slack', 'discord'];
-
-const MESSAGE_FORMATTERS = {
- telegram: (messageText, chatId) => ({ chat_id: chatId, text: messageText }),
- slack: (messageText) => ({ text: messageText }),
- discord: (messageText) => ({ content: messageText })
-};
-
-class NotificationService {
- static SERVICE_NAME = SERVICE_NAME;
- /**
- * Creates an instance of NotificationService.
- *
- * @param {Object} emailService - The email service used for sending notifications.
- * @param {Object} db - The database instance for storing notification data.
- * @param {Object} logger - The logger instance for logging activities.
- * @param {Object} networkService - The network service for sending webhook notifications.
- */
- constructor(emailService, db, logger, networkService, stringService) {
- this.SERVICE_NAME = SERVICE_NAME;
- this.emailService = emailService;
- this.db = db;
- this.logger = logger;
- this.networkService = networkService;
- this.stringService = stringService;
- }
-
- /**
- * Formats a notification message based on the monitor status and platform.
- *
- * @param {Object} monitor - The monitor object.
- * @param {string} monitor.name - The name of the monitor.
- * @param {string} monitor.url - The URL of the monitor.
- * @param {boolean} status - The current status of the monitor (true for up, false for down).
- * @param {string} platform - The notification platform (e.g., "telegram", "slack", "discord").
- * @param {string} [chatId] - The chat ID for platforms that require it (e.g., Telegram).
- * @returns {Object|null} The formatted message object for the specified platform, or null if the platform is unsupported.
- */
-
- formatNotificationMessage(monitor, status, platform, chatId) {
- const messageText = this.stringService.getMonitorStatus(
- monitor.name,
- status,
- monitor.url
- );
-
- if (!PLATFORM_TYPES.includes(platform)) {
- return undefined;
- }
-
- return MESSAGE_FORMATTERS[platform](messageText, chatId);
- }
-
- /**
- * Sends a webhook notification to a specified platform.
- *
- * @param {Object} networkResponse - The response object from the network.
- * @param {Object} networkResponse.monitor - The monitor object.
- * @param {boolean} networkResponse.status - The monitor's status (true for up, false for down).
- * @param {Object} notification - The notification settings.
- * @param {string} notification.platform - The target platform ("telegram", "slack", "discord").
- * @param {Object} notification.config - The configuration object for the webhook.
- * @param {string} notification.config.webhookUrl - The webhook URL for the platform.
- * @param {string} [notification.config.botToken] - The bot token for Telegram notifications.
- * @param {string} [notification.config.chatId] - The chat ID for Telegram notifications.
- * @returns {Promise} A promise that resolves to true if the notification was sent successfully, otherwise false.
- */
-
- async sendWebhookNotification(networkResponse, notification) {
- const { monitor, status } = networkResponse;
- const { platform } = notification;
- const { webhookUrl, botToken, chatId } = notification.config;
-
- // Early return if platform is not supported
- if (!PLATFORM_TYPES.includes(platform)) {
- this.logger.warn({
- message: this.stringService.getWebhookUnsupportedPlatform(platform),
- service: this.SERVICE_NAME,
- method: 'sendWebhookNotification',
- platform
- });
- return false;
- }
-
- // Early return for telegram if required fields are missing
- if (platform === 'telegram' && (!botToken || !chatId)) {
- this.logger.warn({
- message: 'Missing required fields for Telegram notification',
- service: this.SERVICE_NAME,
- method: 'sendWebhookNotification',
- platform
- });
- return false;
- }
-
- let url = webhookUrl;
- if (platform === 'telegram') {
- url = `${TELEGRAM_API_BASE_URL}${botToken}/sendMessage`;
- }
-
- // Now that we know the platform is valid, format the message
- const message = this.formatNotificationMessage(monitor, status, platform, chatId);
-
- try {
- const response = await this.networkService.requestWebhook(platform, url, message);
- return response.status;
- } catch (error) {
- this.logger.error({
- message: this.stringService.getWebhookSendError(platform),
- service: this.SERVICE_NAME,
- method: 'sendWebhookNotification',
- error: error.message,
- stack: error.stack,
- url,
- platform,
- requestPayload: message
- });
- return false;
- }
- }
-
- /**
- * Sends an email notification for hardware infrastructure alerts
- *
- * @async
- * @function sendHardwareEmail
- * @param {Object} networkResponse - Response object containing monitor information
- * @param {string} address - Email address to send the notification to
- * @param {Array} [alerts=[]] - List of hardware alerts to include in the email
- * @returns {Promise} - Indicates whether email was sent successfully
- * @throws {Error}
- */
- async sendHardwareEmail(networkResponse, address, alerts = []) {
- if (alerts.length === 0) return false;
- const { monitor, status, prevStatus } = networkResponse;
- const template = "hardwareIncidentTemplate";
- const context = { monitor: monitor.name, url: monitor.url, alerts };
- const subject = `Monitor ${monitor.name} infrastructure alerts`;
- this.emailService.buildAndSendEmail(template, context, address, subject);
- return true;
- }
-
- /**
- * Sends an email notification about monitor status change
- *
- * @async
- * @function sendEmail
- * @param {Object} networkResponse - Response object containing monitor status information
- * @param {string} address - Email address to send the notification to
- * @returns {Promise} - Indicates email was sent successfully
- */
- async sendEmail(networkResponse, address) {
- const { monitor, status, prevStatus } = networkResponse;
- const template = prevStatus === false ? "serverIsUpTemplate" : "serverIsDownTemplate";
- const context = { monitor: monitor.name, url: monitor.url };
- const subject = `Monitor ${monitor.name} is ${status === true ? "up" : "down"}`;
- this.emailService.buildAndSendEmail(template, context, address, subject);
- return true;
- }
-
- async handleStatusNotifications(networkResponse) {
- try {
- // If status hasn't changed, we're done
- if (networkResponse.statusChanged === false) return false;
- // if prevStatus is undefined, monitor is resuming, we're done
- if (networkResponse.prevStatus === undefined) return false;
-
- const notifications = await this.db.getNotificationsByMonitorId(networkResponse.monitorId);
-
- for (const notification of notifications) {
- if (notification.type === "email") {
- await this.sendEmail(networkResponse, notification.address);
- } else if (notification.type === "webhook") {
- await this.sendWebhookNotification(networkResponse, notification);
- }
- // Handle other types of notifications here
- }
- return true;
- } catch (error) {
- this.logger.warn({
- message: error.message,
- service: this.SERVICE_NAME,
- method: "handleNotifications",
- stack: error.stack,
- });
- }
- }
- /**
- * Handles status change notifications for a monitor
- *
- * @async
- * @function handleStatusNotifications
- * @param {Object} networkResponse - Response object containing monitor status information
- * @returns {Promise} - Indicates whether notifications were processed
- * @throws {Error}
- */
- async handleHardwareNotifications(networkResponse) {
- const thresholds = networkResponse?.monitor?.thresholds;
- if (thresholds === undefined) return false; // No thresholds set, we're done
-
- // Get thresholds from monitor
- const {
- usage_cpu: cpuThreshold = -1,
- usage_memory: memoryThreshold = -1,
- usage_disk: diskThreshold = -1,
- } = thresholds;
-
- // Get metrics from response
- const metrics = networkResponse?.payload?.data ?? null;
- if (metrics === null) return false;
-
- const {
- cpu: { usage_percent: cpuUsage = -1 } = {},
- memory: { usage_percent: memoryUsage = -1 } = {},
- disk = [],
- } = metrics;
-
- const alerts = {
- cpu: cpuThreshold !== -1 && cpuUsage > cpuThreshold ? true : false,
- memory: memoryThreshold !== -1 && memoryUsage > memoryThreshold ? true : false,
- disk: disk.some((d) => diskThreshold !== -1 && d.usage_percent > diskThreshold)
- ? true
- : false,
- };
-
- const notifications = await this.db.getNotificationsByMonitorId(
- networkResponse.monitorId
- );
- for (const notification of notifications) {
- const alertsToSend = [];
- const alertTypes = ["cpu", "memory", "disk"];
-
- for (const type of alertTypes) {
- // Iterate over each alert type to see if any need to be decremented
- if (alerts[type] === true) {
- notification[`${type}AlertThreshold`]--; // Decrement threshold if an alert is triggered
-
- if (notification[`${type}AlertThreshold`] <= 0) {
- // If threshold drops below 0, reset and send notification
- notification[`${type}AlertThreshold`] = notification.alertThreshold;
-
- const formatAlert = {
- cpu: () =>
- `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`,
- memory: () =>
- `Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`,
- disk: () =>
- `Your current disk usage: ${disk
- .map((d, idx) => `(Disk${idx}: ${(d.usage_percent * 100).toFixed(0)}%)`)
- .join(
- ", "
- )} is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`,
- };
- alertsToSend.push(formatAlert[type]());
- }
- }
- }
-
- await notification.save();
-
- if (alertsToSend.length === 0) continue; // No alerts to send, we're done
-
- if (notification.type === "email") {
- this.sendHardwareEmail(networkResponse, notification.address, alertsToSend);
- }
- }
- return true;
- }
-
- /**
- * Handles notifications for different monitor types
- *
- * @async
- * @function handleNotifications
- * @param {Object} networkResponse - Response object containing monitor information
- * @returns {Promise} - Indicates whether notifications were processed successfully
- */
- async handleNotifications(networkResponse) {
- try {
- if (networkResponse.monitor.type === "hardware") {
- this.handleHardwareNotifications(networkResponse);
- }
- this.handleStatusNotifications(networkResponse);
- return true;
- } catch (error) {
- this.logger.warn({
- message: error.message,
- service: this.SERVICE_NAME,
- method: "handleNotifications",
- stack: error.stack,
- });
- }
- }
-}
-
-export default NotificationService;
diff --git a/Server/service/serviceRegistry.js b/Server/service/serviceRegistry.js
deleted file mode 100644
index 69a3b8be5..000000000
--- a/Server/service/serviceRegistry.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const SERVICE_NAME = "ServiceRegistry";
-import logger from "../utils/logger.js";
-class ServiceRegistry {
- static SERVICE_NAME = SERVICE_NAME;
- constructor() {
- this.services = {};
- }
-
- register(name, service) {
- logger.info({
- message: `Registering service ${name}`,
- service: SERVICE_NAME,
- method: "register",
- });
- this.services[name] = service;
- }
-
- get(name) {
- if (!this.services[name]) {
- logger.error({
- message: `Service ${name} is not registered`,
- service: SERVICE_NAME,
- method: "get",
- });
- throw new Error(`Service ${name} is not registered`);
- }
- return this.services[name];
- }
-
- listServices() {
- return Object.keys(this.services);
- }
-}
-
-export default new ServiceRegistry();
diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js
deleted file mode 100644
index 24931f6ce..000000000
--- a/Server/service/settingsService.js
+++ /dev/null
@@ -1,84 +0,0 @@
-const SERVICE_NAME = "SettingsService";
-import dotenv from "dotenv";
-dotenv.config();
-const envConfig = {
- logLevel: process.env.LOG_LEVEL,
- apiBaseUrl: undefined,
- clientHost: process.env.CLIENT_HOST,
- jwtSecret: process.env.JWT_SECRET,
- refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET,
- dbType: process.env.DB_TYPE,
- dbConnectionString: process.env.DB_CONNECTION_STRING,
- redisHost: process.env.REDIS_HOST,
- redisPort: process.env.REDIS_PORT,
- jwtTTL: process.env.TOKEN_TTL,
- refreshTokenTTL: process.env.REFRESH_TOKEN_TTL,
- pagespeedApiKey: process.env.PAGESPEED_API_KEY,
- systemEmailHost: process.env.SYSTEM_EMAIL_HOST,
- systemEmailPort: process.env.SYSTEM_EMAIL_PORT,
- systemEmailAddress: process.env.SYSTEM_EMAIL_ADDRESS,
- systemEmailPassword: process.env.SYSTEM_EMAIL_PASSWORD,
-};
-/**
- * SettingsService
- *
- * This service is responsible for loading and managing the application settings.
- * It gives priority to environment variables and will only load settings
- * from the database if they are not set in the environment.
- */
-class SettingsService {
- static SERVICE_NAME = SERVICE_NAME;
- /**
- * Constructs a new SettingsService
- * @constructor
- * @throws {Error}
- */ constructor(appSettings) {
- this.appSettings = appSettings;
- this.settings = { ...envConfig };
- }
- /**
- * Load settings from the database and merge with environment settings.
- * If there are any settings that weren't set by environment variables, use user settings from the database.
- * @returns {Promise} The merged settings.
- * @throws Will throw an error if settings are not found in the database or if settings have not been loaded.
- */ async loadSettings() {
- try {
- const dbSettings = await this.appSettings.findOne();
- if (!this.settings) {
- throw new Error("Settings not found");
- }
-
- // If there are any settings that weren't set by environment variables, use user settings from DB
- for (const key in envConfig) {
- if (envConfig[key] === undefined && dbSettings[key] !== undefined) {
- this.settings[key] = dbSettings[key];
- }
- }
- return this.settings;
- } catch (error) {
- error.service === undefined ? (error.service = SERVICE_NAME) : null;
- error.method === undefined ? (error.method = "loadSettings") : null;
- throw error;
- }
- }
- /**
- * Reload settings by calling loadSettings.
- * @returns {Promise} The reloaded settings.
- */
- async reloadSettings() {
- return this.loadSettings();
- }
- /**
- * Get the current settings.
- * @returns {Object} The current settings.
- * @throws Will throw an error if settings have not been loaded.
- */
- getSettings() {
- if (!this.settings) {
- throw new Error("Settings have not been loaded");
- }
- return this.settings;
- }
-}
-
-export default SettingsService;
diff --git a/Server/service/statusService.js b/Server/service/statusService.js
deleted file mode 100644
index fa263da75..000000000
--- a/Server/service/statusService.js
+++ /dev/null
@@ -1,200 +0,0 @@
-const SERVICE_NAME = "StatusService";
-
-class StatusService {
- static SERVICE_NAME = SERVICE_NAME;
- /**
- * Creates an instance of StatusService.
- *
- * @param {Object} db - The database instance.
- * @param {Object} logger - The logger instance.
- */
- constructor(db, logger) {
- this.db = db;
- this.logger = logger;
- this.SERVICE_NAME = SERVICE_NAME;
- }
-
- getStatusString = (status) => {
- if (status === true) return "up";
- if (status === false) return "down";
- return "unknown";
- };
- /**
- * Updates the status of a monitor based on the network response.
- *
- * @param {Object} networkResponse - The network response containing monitorId and status.
- * @param {string} networkResponse.monitorId - The ID of the monitor.
- * @param {string} networkResponse.status - The new status of the monitor.
- * @returns {Promise} - A promise that resolves to an object containinfg the monitor, statusChanged flag, and previous status if the status changed, or false if an error occurred.
- * @returns {Promise} returnObject - The object returned by the function.
- * @returns {Object} returnObject.monitor - The monitor object.
- * @returns {boolean} returnObject.statusChanged - Flag indicating if the status has changed.
- * @returns {boolean} returnObject.prevStatus - The previous status of the monitor
- */
- updateStatus = async (networkResponse) => {
- this.insertCheck(networkResponse);
- try {
- const { monitorId, status } = networkResponse;
- const monitor = await this.db.getMonitorById(monitorId);
- // No change in monitor status, return early
- if (monitor.status === status)
- return { monitor, statusChanged: false, prevStatus: monitor.status };
- // Monitor status changed, save prev status and update monitor
-
- this.logger.info({
- service: this.SERVICE_NAME,
- message: `${monitor.name} went from ${this.getStatusString(monitor.status)} to ${this.getStatusString(status)}`,
- prevStatus: monitor.status,
- newStatus: status,
- });
-
- const prevStatus = monitor.status;
- monitor.status = status;
- await monitor.save();
-
- return {
- monitor,
- statusChanged: true,
- prevStatus: prevStatus,
- };
- //
- } catch (error) {
- this.logger.error({
- service: this.SERVICE_NAME,
- message: error.message,
- method: "updateStatus",
- stack: error.stack,
- });
- throw error;
- }
- };
-
- /**
- * Builds a check object from the network response.
- *
- * @param {Object} networkResponse - The network response object.
- * @param {string} networkResponse.monitorId - The monitor ID.
- * @param {string} networkResponse.type - The type of the response.
- * @param {string} networkResponse.status - The status of the response.
- * @param {number} networkResponse.responseTime - The response time.
- * @param {number} networkResponse.code - The status code.
- * @param {string} networkResponse.message - The message.
- * @param {Object} networkResponse.payload - The payload of the response.
- * @returns {Object} The check object.
- */
- buildCheck = (networkResponse) => {
- const {
- monitorId,
- teamId,
- type,
- status,
- responseTime,
- code,
- message,
- payload,
- first_byte_took,
- body_read_took,
- dns_took,
- conn_took,
- connect_took,
- tls_took,
- } = networkResponse;
-
- const check = {
- monitorId,
- teamId,
- status,
- statusCode: code,
- responseTime,
- message,
- first_byte_took,
- body_read_took,
- dns_took,
- conn_took,
- connect_took,
- tls_took,
- };
-
- if (type === "distributed_http") {
- check.continent = payload.continent;
- check.countryCode = payload.country_code;
- check.city = payload.city;
- check.location = payload.location;
- check.uptBurnt = payload.upt_burnt;
- check.first_byte_took = payload.first_byte_took;
- check.body_read_took = payload.body_read_took;
- check.dns_took = payload.dns_took;
- check.conn_took = payload.conn_took;
- check.connect_took = payload.connect_took;
- check.tls_took = payload.tls_took;
- }
-
- if (type === "pagespeed") {
- const categories = payload.lighthouseResult?.categories;
- const audits = payload.lighthouseResult?.audits;
- const {
- "cumulative-layout-shift": cls = 0,
- "speed-index": si = 0,
- "first-contentful-paint": fcp = 0,
- "largest-contentful-paint": lcp = 0,
- "total-blocking-time": tbt = 0,
- } = audits;
- check.accessibility = (categories.accessibility?.score || 0) * 100;
- check.bestPractices = (categories["best-practices"]?.score || 0) * 100;
- check.seo = (categories.seo?.score || 0) * 100;
- check.performance = (categories.performance?.score || 0) * 100;
- check.audits = { cls, si, fcp, lcp, tbt };
- }
-
- if (type === "hardware") {
- const { cpu, memory, disk, host } = payload?.data ?? {};
- const { errors } = payload?.errors ?? [];
- check.cpu = cpu ?? {};
- check.memory = memory ?? {};
- check.disk = disk ?? {};
- check.host = host ?? {};
- check.errors = errors ?? [];
- }
- return check;
- };
-
- /**
- * Inserts a check into the database based on the network response.
- *
- * @param {Object} networkResponse - The network response object.
- * @param {string} networkResponse.monitorId - The monitor ID.
- * @param {string} networkResponse.type - The type of the response.
- * @param {string} networkResponse.status - The status of the response.
- * @param {number} networkResponse.responseTime - The response time.
- * @param {number} networkResponse.code - The status code.
- * @param {string} networkResponse.message - The message.
- * @param {Object} networkResponse.payload - The payload of the response.
- * @returns {Promise} A promise that resolves when the check is inserted.
- */
- insertCheck = async (networkResponse) => {
- try {
- const operationMap = {
- http: this.db.createCheck,
- ping: this.db.createCheck,
- pagespeed: this.db.createPageSpeedCheck,
- hardware: this.db.createHardwareCheck,
- docker: this.db.createCheck,
- port: this.db.createCheck,
- distributed_http: this.db.createDistributedCheck,
- };
- const operation = operationMap[networkResponse.type];
-
- const check = this.buildCheck(networkResponse);
- await operation(check);
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: this.SERVICE_NAME,
- method: "insertCheck",
- details: `Error inserting check for monitor: ${networkResponse?.monitorId}`,
- stack: error.stack,
- });
- }
- };
-}
-export default StatusService;
diff --git a/Server/service/stringService.js b/Server/service/stringService.js
deleted file mode 100644
index e3ab03ca5..000000000
--- a/Server/service/stringService.js
+++ /dev/null
@@ -1,388 +0,0 @@
-class StringService {
- static SERVICE_NAME = "StringService";
-
- constructor(translationService) {
- if (StringService.instance) {
- return StringService.instance;
- }
-
- this.translationService = translationService;
- this._language = 'en'; // default language
- StringService.instance = this;
- }
-
- setLanguage(language) {
- this._language = language;
- }
-
- get language() {
- return this._language;
- }
-
- // Auth Messages
- get dontHaveAccount() {
- return this.translationService.getTranslation('dontHaveAccount');
- }
-
- get email() {
- return this.translationService.getTranslation('email');
- }
-
- get forgotPassword() {
- return this.translationService.getTranslation('forgotPassword');
- }
-
- get password() {
- return this.translationService.getTranslation('password');
- }
-
- get signUp() {
- return this.translationService.getTranslation('signUp');
- }
-
- get submit() {
- return this.translationService.getTranslation('submit');
- }
-
- get title() {
- return this.translationService.getTranslation('title');
- }
-
- get continue() {
- return this.translationService.getTranslation('continue');
- }
-
- get enterEmail() {
- return this.translationService.getTranslation('enterEmail');
- }
-
- get authLoginTitle() {
- return this.translationService.getTranslation('authLoginTitle');
- }
-
- get authLoginEnterPassword() {
- return this.translationService.getTranslation('authLoginEnterPassword');
- }
-
- get commonPassword() {
- return this.translationService.getTranslation('commonPassword');
- }
-
- get commonBack() {
- return this.translationService.getTranslation('commonBack');
- }
-
- get authForgotPasswordTitle() {
- return this.translationService.getTranslation('authForgotPasswordTitle');
- }
-
- get authForgotPasswordResetPassword() {
- return this.translationService.getTranslation('authForgotPasswordResetPassword');
- }
-
- get createPassword() {
- return this.translationService.getTranslation('createPassword');
- }
-
- get createAPassword() {
- return this.translationService.getTranslation('createAPassword');
- }
-
- get authRegisterAlreadyHaveAccount() {
- return this.translationService.getTranslation('authRegisterAlreadyHaveAccount');
- }
-
- get commonAppName() {
- return this.translationService.getTranslation('commonAppName');
- }
-
- get authLoginEnterEmail() {
- return this.translationService.getTranslation('authLoginEnterEmail');
- }
-
- get authRegisterTitle() {
- return this.translationService.getTranslation('authRegisterTitle');
- }
-
- get monitorGetAll() {
- return this.translationService.getTranslation('monitorGetAll');
- }
-
- get monitorGetById() {
- return this.translationService.getTranslation('monitorGetById');
- }
-
- get monitorCreate() {
- return this.translationService.getTranslation('monitorCreate');
- }
-
- get monitorEdit() {
- return this.translationService.getTranslation('monitorEdit');
- }
-
- get monitorDelete() {
- return this.translationService.getTranslation('monitorDelete');
- }
-
- get monitorPause() {
- return this.translationService.getTranslation('monitorPause');
- }
-
- get monitorResume() {
- return this.translationService.getTranslation('monitorResume');
- }
-
- get monitorDemoAdded() {
- return this.translationService.getTranslation('monitorDemoAdded');
- }
-
- get monitorStatsById() {
- return this.translationService.getTranslation('monitorStatsById');
- }
-
- get monitorCertificate() {
- return this.translationService.getTranslation('monitorCertificate');
- }
-
- // Maintenance Window Messages
- get maintenanceWindowCreate() {
- return this.translationService.getTranslation('maintenanceWindowCreate');
- }
-
- get maintenanceWindowGetById() {
- return this.translationService.getTranslation('maintenanceWindowGetById');
- }
-
- get maintenanceWindowGetByTeam() {
- return this.translationService.getTranslation('maintenanceWindowGetByTeam');
- }
-
- get maintenanceWindowDelete() {
- return this.translationService.getTranslation('maintenanceWindowDelete');
- }
-
- get maintenanceWindowEdit() {
- return this.translationService.getTranslation('maintenanceWindowEdit');
- }
-
- // Webhook Messages
- get webhookUnsupportedPlatform() {
- return this.translationService.getTranslation('webhookUnsupportedPlatform');
- }
-
- get webhookSendError() {
- return this.translationService.getTranslation('webhookSendError');
- }
-
- get webhookSendSuccess() {
- return this.translationService.getTranslation('webhookSendSuccess');
- }
-
- getWebhookUnsupportedPlatform(platform) {
- return this.translationService.getTranslation('webhookUnsupportedPlatform')
- .replace('{platform}', platform);
- }
-
- getWebhookSendError(platform) {
- return this.translationService.getTranslation('webhookSendError')
- .replace('{platform}', platform);
- }
-
- getMonitorStatus(name, status, url) {
- return this.translationService.getTranslation('monitorStatus')
- .replace('{name}', name)
- .replace('{status}', status ? "up" : "down")
- .replace('{url}', url);
- }
-
- // Error Messages
- get unknownError() {
- return this.translationService.getTranslation('unknownError');
- }
-
- get friendlyError() {
- return this.translationService.getTranslation('friendlyError');
- }
-
- get authIncorrectPassword() {
- return this.translationService.getTranslation('authIncorrectPassword');
- }
-
- get unauthorized() {
- return this.translationService.getTranslation('unauthorized');
- }
-
- get authAdminExists() {
- return this.translationService.getTranslation('authAdminExists');
- }
-
- get authInviteNotFound() {
- return this.translationService.getTranslation('authInviteNotFound');
- }
-
- get unknownService() {
- return this.translationService.getTranslation('unknownService');
- }
-
- get noAuthToken() {
- return this.translationService.getTranslation('noAuthToken');
- }
-
- get invalidAuthToken() {
- return this.translationService.getTranslation('invalidAuthToken');
- }
-
- get expiredAuthToken() {
- return this.translationService.getTranslation('expiredAuthToken');
- }
-
- // Queue Messages
- get queueGetMetrics() {
- return this.translationService.getTranslation('queueGetMetrics');
- }
-
- get queueAddJob() {
- return this.translationService.getTranslation('queueAddJob');
- }
-
- get queueObliterate() {
- return this.translationService.getTranslation('queueObliterate');
- }
-
- // Job Queue Messages
- get jobQueueDeleteJobSuccess() {
- return this.translationService.getTranslation('jobQueueDeleteJobSuccess');
- }
-
- get jobQueuePauseJob() {
- return this.translationService.getTranslation('jobQueuePauseJob');
- }
-
- get jobQueueResumeJob() {
- return this.translationService.getTranslation('jobQueueResumeJob');
- }
-
- // Status Page Messages
- get statusPageByUrl() {
- return this.translationService.getTranslation('statusPageByUrl');
- }
-
- get statusPageCreate() {
- return this.translationService.getTranslation('statusPageCreate');
- }
-
- get statusPageDelete() {
- return this.translationService.getTranslation('statusPageDelete');
- }
-
- get statusPageUpdate() {
- return this.translationService.getTranslation('statusPageUpdate');
- }
-
- get statusPageNotFound() {
- return this.translationService.getTranslation('statusPageNotFound');
- }
-
- get statusPageByTeamId() {
- return this.translationService.getTranslation('statusPageByTeamId');
- }
-
- get statusPageUrlNotUnique() {
- return this.translationService.getTranslation('statusPageUrlNotUnique');
- }
-
- // Docker Messages
- get dockerFail() {
- return this.translationService.getTranslation('dockerFail');
- }
-
- get dockerNotFound() {
- return this.translationService.getTranslation('dockerNotFound');
- }
-
- get dockerSuccess() {
- return this.translationService.getTranslation('dockerSuccess');
- }
-
- // Port Messages
- get portFail() {
- return this.translationService.getTranslation('portFail');
- }
-
- get portSuccess() {
- return this.translationService.getTranslation('portSuccess');
- }
-
- // Alert Messages
- get alertCreate() {
- return this.translationService.getTranslation('alertCreate');
- }
-
- get alertGetByUser() {
- return this.translationService.getTranslation('alertGetByUser');
- }
-
- get alertGetByMonitor() {
- return this.translationService.getTranslation('alertGetByMonitor');
- }
-
- get alertGetById() {
- return this.translationService.getTranslation('alertGetById');
- }
-
- get alertEdit() {
- return this.translationService.getTranslation('alertEdit');
- }
-
- get alertDelete() {
- return this.translationService.getTranslation('alertDelete');
- }
-
- getDeletedCount(count) {
- return this.translationService.getTranslation('deletedCount')
- .replace('{count}', count);
- }
-
- get pingSuccess() {
- return this.translationService.getTranslation('pingSuccess');
- }
-
- get getAppSettings() {
- return this.translationService.getTranslation('getAppSettings');
- }
-
- get httpNetworkError() {
- return this.translationService.getTranslation('httpNetworkError');
- }
-
- get httpNotJson() {
- return this.translationService.getTranslation('httpNotJson');
- }
-
- get httpJsonPathError() {
- return this.translationService.getTranslation('httpJsonPathError');
- }
-
- get httpEmptyResult() {
- return this.translationService.getTranslation('httpEmptyResult');
- }
-
- get httpMatchSuccess() {
- return this.translationService.getTranslation('httpMatchSuccess');
- }
-
- get httpMatchFail() {
- return this.translationService.getTranslation('httpMatchFail');
- }
-
- get updateAppSettings() {
- return this.translationService.getTranslation('updateAppSettings');
- }
-
- getDbFindMonitorById(monitorId) {
- return this.translationService.getTranslation('dbFindMonitorById')
- .replace('${monitorId}', monitorId);
- }
-}
-
-export default StringService;
\ No newline at end of file
diff --git a/Server/service/translationService.js b/Server/service/translationService.js
deleted file mode 100644
index 64c62bff2..000000000
--- a/Server/service/translationService.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import fs from 'fs';
-import path from 'path';
-
-class TranslationService {
- static SERVICE_NAME = 'TranslationService';
-
- constructor(logger) {
- this.logger = logger;
- this.translations = {};
- this._language = 'en';
- this.localesDir = path.join(process.cwd(), 'locales');
- }
-
- setLanguage(language) {
- this._language = language;
- }
-
- get language() {
- return this._language;
- }
-
- async initialize() {
- try {
- await this.loadFromFiles();
-
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: 'TranslationService',
- method: 'initialize',
- stack: error.stack
- });
- }
- }
-
- async loadFromFiles() {
- try {
- if (!fs.existsSync(this.localesDir)) {
- return false;
- }
-
- const files = fs.readdirSync(this.localesDir).filter(file => file.endsWith('.json'));
-
- if (files.length === 0) {
- return false;
- }
-
- for (const file of files) {
- const language = file.replace('.json', '');
- const filePath = path.join(this.localesDir, file);
- const content = fs.readFileSync(filePath, 'utf8');
- this.translations[language] = JSON.parse(content);
- }
-
- this.logger.info({
- message: 'Translations loaded from files successfully',
- service: 'TranslationService',
- method: 'loadFromFiles'
- });
-
- return true;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: 'TranslationService',
- method: 'loadFromFiles',
- stack: error.stack
- });
- return false;
- }
- }
-
- getTranslation(key) {
- let language = this._language;
-
- try {
- return this.translations[language]?.[key] || this.translations['en']?.[key] || key;
- } catch (error) {
- this.logger.error({
- message: error.message,
- service: 'TranslationService',
- method: 'getTranslation',
- stack: error.stack
- });
- return key;
- }
- }
-}
-
-export default TranslationService;
\ No newline at end of file
diff --git a/Server/templates/addReview.mjml b/Server/templates/addReview.mjml
deleted file mode 100644
index 22adbe8b0..000000000
--- a/Server/templates/addReview.mjml
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
-
-
-
-
- Hello {{name}}!
-
-We hope youโre finding Checkmate helpful in monitoring your infrastructure. Your support means a lot to us, and we truly appreciate having you as part of our community.
-
-
-If youโre happy with Checkmate, weโd love to hear about your experience! Leaving a review on G2 helps others discover Checkmate and supports our ongoing improvements.
-
-G2 Link: TBD
-
-Thank you for taking the time to share your thoughts - we greatly appreciate it!
-
-Checkmate Team
-
-
-
-
-
- This email was sent by Checkmate.
-
-
-
-
-
diff --git a/Server/templates/employeeActivation.mjml b/Server/templates/employeeActivation.mjml
deleted file mode 100644
index 34d5ea7c4..000000000
--- a/Server/templates/employeeActivation.mjml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
-
-
-
- Hello {{name}}!
-
- One of the admins created an account for you on the Checkmate server.
-
- You can go ahead and create your account using this link.
- {{link}}
- Thank you.
-
-
-
-
-
- This email was sent by Checkmate.
-
-
-
-
-
diff --git a/Server/templates/hardwareIncident.mjml b/Server/templates/hardwareIncident.mjml
deleted file mode 100644
index e27933fb9..000000000
--- a/Server/templates/hardwareIncident.mjml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from BlueWave Infrastructure Monitoring
-
-
-
-
- Infrastructure Alerts
-
-
-
-
-
-
-
- Hello {{name}}!
- {{monitor}} at {{url}} has the following infrastructure alerts:
- {{#each alerts}}
- โข {{this}}
- {{/each}}
-
-
-
-
- View Infrastructure Details
-
- This email was sent by BlueWave Infrastructure Monitoring.
-
-
-
-
-
\ No newline at end of file
diff --git a/Server/templates/noIncidentsThisWeek.mjml b/Server/templates/noIncidentsThisWeek.mjml
deleted file mode 100644
index d31d63d10..000000000
--- a/Server/templates/noIncidentsThisWeek.mjml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
- No incidents this week!
-
-
-
-
-
-
- Hello {{name}}!
- There were no incidents this week. Good job!
- Current monitors:
- Google: 100% availability
- Canada.ca: 100% availability
-
-
-
-
-
- This email was sent by Checkmate.
-
-
-
-
-
diff --git a/Server/templates/passwordReset.mjml b/Server/templates/passwordReset.mjml
deleted file mode 100644
index e0b85a4f4..000000000
--- a/Server/templates/passwordReset.mjml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
-
-
-
-
- Hello {{name}}!
-
- You are receiving this email because a password reset request has been made
- for {{email}}. Please use the link below on the site to reset your password.
-
- Reset Password
- If you didn't request this, please ignore this email.
-
- Thank you.
-
-
-
-
-
- This email was sent by Checkmate.
-
-
-
-
-
diff --git a/Server/templates/serverIsDown.mjml b/Server/templates/serverIsDown.mjml
deleted file mode 100644
index 16df7a0a1..000000000
--- a/Server/templates/serverIsDown.mjml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
-
-
- {{monitor}} is down
-
-
-
-
-
-
-
- Hello {{name}}!
-
- We detected an incident on one of your monitors. Your service is currently
- down. We'll send a message to you once it is up again.
-
- Monitor name: {{monitor}}
- URL: {{url}}
-
-
-
-
- View incident details
-
- This email was sent by Checkmate.
-
-
-
-
-
diff --git a/Server/templates/serverIsUp.mjml b/Server/templates/serverIsUp.mjml
deleted file mode 100644
index 8dbaaf011..000000000
--- a/Server/templates/serverIsUp.mjml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
-
-
- {{monitor}} is up
-
-
-
-
-
-
-
- Hello {{name}}!
- Your latest incident is resolved and your monitored service is up again.
- Monitor name: {{monitor}}
- URL: {{url}}
-
-
-
-
- View incident details
-
- This email was sent by Checkmate.
-
-
-
-
-
\ No newline at end of file
diff --git a/Server/templates/welcomeEmail.mjml b/Server/templates/welcomeEmail.mjml
deleted file mode 100644
index af1bfc190..000000000
--- a/Server/templates/welcomeEmail.mjml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Message from Checkmate Service
-
-
-
-
-
-
- Hello {{name}}!
-
- Thank you for trying out Checkmate! We developed it with great care to
- meet our own needs, and we're excited to share it with you.
-
-
- Checkmate is an automated way of checking whether a service such as a
- website or an application is available or not.
-
- We hope you find our service as valuable as we do.
- Thank you.
-
-
-
-
-
- This email was sent by Checkmate.
-
-
-
-
-
diff --git a/Server/tests/controllers/authController.test.js b/Server/tests/controllers/authController.test.js
deleted file mode 100644
index 3f45d1c42..000000000
--- a/Server/tests/controllers/authController.test.js
+++ /dev/null
@@ -1,975 +0,0 @@
-import {
- issueToken,
- registerUser,
- loginUser,
- refreshAuthToken,
- editUser,
- checkSuperadminExists,
- requestRecovery,
- validateRecovery,
- resetPassword,
- deleteUser,
- getAllUsers,
-} from "../../controllers/authController.js";
-import jwt from "jsonwebtoken";
-import { errorMessages, successMessages } from "../../utils/messages.js";
-import sinon from "sinon";
-import { getTokenFromHeaders, tokenType } from "../../utils/utils.js";
-import logger from "../../utils/logger.js";
-import e from "cors";
-
-describe("Auth Controller - issueToken", function() {
- let stub;
-
- afterEach(function() {
- sinon.restore(); // Restore stubs after each test
- });
-
- it("should reject with an error if jwt.sign fails", function() {
- const error = new Error("jwt.sign error");
- stub = sinon.stub(jwt, "sign").throws(error);
- const payload = { id: "123" };
- const appSettings = { jwtSecret: "my_secret" };
- expect(() => issueToken(payload, tokenType.ACCESS_TOKEN, appSettings)).to.throw(
- error
- );
- });
-
- it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function() {
- const payload = { id: "123" };
- const appSettings = { jwtSecret: "my_secret" };
- const expectedToken = "mockToken";
-
- stub = sinon.stub(jwt, "sign").returns(expectedToken);
- const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings);
- expect(token).to.equal(expectedToken);
- });
-
- it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", function() {
- const payload = { id: "123" };
- const appSettings = { jwtSecret: "my_secret", jwtTTL: "1s" };
- const expectedToken = "mockToken";
-
- stub = sinon.stub(jwt, "sign").returns(expectedToken);
- const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings);
- expect(token).to.equal(expectedToken);
- });
-
- it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", function() {
- const payload = {};
- const appSettings = { refreshTokenSecret: "my_refresh_secret" };
- const expectedToken = "mockRefreshToken";
-
- stub = sinon.stub(jwt, "sign").returns(expectedToken);
- const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings);
- expect(token).to.equal(expectedToken);
- });
-
- it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", function() {
- const payload = {};
- const appSettings = {
- refreshTokenSecret: "my_refresh_secret",
- refreshTokenTTL: "7d",
- };
- const expectedToken = "mockRefreshToken";
-
- stub = sinon.stub(jwt, "sign").returns(expectedToken);
- const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings);
- expect(token).to.equal(expectedToken);
- });
-});
-
-describe("Auth Controller - registerUser", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: {
- firstName: "firstname",
- lastName: "lastname",
- email: "test@test.com",
- password: "Uptime1!",
- role: ["admin"],
- teamId: "123",
- inviteToken: "invite",
- },
- db: {
- checkSuperadmin: sinon.stub(),
- getInviteTokenAndDelete: sinon.stub(),
- updateAppSettings: sinon.stub(),
- insertUser: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub().resolves({
- jwtSecret: "my_secret",
- refreshTokenSecret: "my_refresh_secret",
- }),
- },
- emailService: {
- buildAndSendEmail: sinon.stub(),
- },
- file: {},
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- sinon.stub(logger, "error");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if body validation fails", async function() {
- req.body = {};
- await registerUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if checkSuperadmin fails", async function() {
- req.db.checkSuperadmin.throws(new Error("checkSuperadmin error"));
- await registerUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error");
- });
-
- it("should reject with an error if getInviteTokenAndDelete fails", async function() {
- req.db.checkSuperadmin.returns(true);
- req.db.getInviteTokenAndDelete.throws(new Error("getInviteTokenAndDelete error"));
- await registerUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getInviteTokenAndDelete error");
- });
-
- it("should reject with an error if updateAppSettings fails", async function() {
- req.db.checkSuperadmin.returns(false);
- req.db.updateAppSettings.throws(new Error("updateAppSettings error"));
- await registerUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("updateAppSettings error");
- });
-
- it("should reject with an error if insertUser fails", async function() {
- req.db.checkSuperadmin.resolves(false);
- req.db.updateAppSettings.resolves();
- req.db.insertUser.rejects(new Error("insertUser error"));
- await registerUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("insertUser error");
- });
-
- it("should reject with an error if settingsService.getSettings fails", async function() {
- req.db.checkSuperadmin.resolves(false);
- req.db.updateAppSettings.resolves();
- req.db.insertUser.resolves({ _id: "123" });
- req.settingsService.getSettings.rejects(
- new Error("settingsService.getSettings error")
- );
- await registerUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error");
- });
-
- it("should log an error if emailService.buildAndSendEmail fails", async function() {
- req.db.checkSuperadmin.resolves(false);
- req.db.updateAppSettings.resolves();
- req.db.insertUser.returns({ _id: "123" });
- req.settingsService.getSettings.returns({
- jwtSecret: "my_secret",
- refreshTokenSecret: "my_secret",
- });
- req.emailService.buildAndSendEmail.rejects(new Error("emailService error"));
- await registerUser(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal("emailService error");
- });
-
- it("should return a success message and data if all operations are successful", async function() {
- const user = { _id: "123" };
- req.db.checkSuperadmin.resolves(false);
- req.db.updateAppSettings.resolves();
- req.db.insertUser.returns(user);
- req.settingsService.getSettings.returns({
- jwtSecret: "my_secret",
- refreshTokenSecret: "my_secret",
- });
- req.emailService.buildAndSendEmail.resolves("message-id");
- await registerUser(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_CREATE_USER,
- data: { user, token: sinon.match.string, refreshToken: sinon.match.string },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should return a success message and data if all operations are successful and superAdmin true", async function() {
- const user = { _id: "123" };
- req.db.checkSuperadmin.resolves(true);
- req.db.updateAppSettings.resolves();
- req.db.insertUser.returns(user);
- req.settingsService.getSettings.returns({
- jwtSecret: "my_secret",
- refreshTokenSecret: "my_secret",
- });
- req.emailService.buildAndSendEmail.resolves("message-id");
- await registerUser(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_CREATE_USER,
- data: { user, token: sinon.match.string, refreshToken: sinon.match.string },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-});
-
-describe("Auth Controller - loginUser", function() {
- let req, res, next, user;
-
- beforeEach(function() {
- req = {
- body: { email: "test@example.com", password: "Password123!" },
- db: {
- getUserByEmail: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub().resolves({
- jwtSecret: "my_secret",
- refreshTokenSecret: "my_refresh_token",
- }),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- user = {
- _doc: {
- email: "test@example.com",
- },
- comparePassword: sinon.stub(),
- };
- });
-
- it("should reject with an error if validation fails", async function() {
- req.body = {};
- await loginUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if getUserByEmail fails", async function() {
- req.db.getUserByEmail.rejects(new Error("getUserByEmail error"));
- await loginUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getUserByEmail error");
- });
-
- it("should login user successfully", async function() {
- req.db.getUserByEmail.resolves(user);
- user.comparePassword.resolves(true);
- await loginUser(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_LOGIN_USER,
- data: {
- user: {
- email: "test@example.com",
- avatarImage: undefined,
- },
- token: sinon.match.string,
- refreshToken: sinon.match.string,
- },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should reject a user with an incorrect password", async function() {
- req.body = {
- email: "test@test.com",
- password: "Password123!",
- };
- req.db.getUserByEmail.resolves(user);
- user.comparePassword.resolves(false);
- await loginUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal(
- errorMessages.AUTH_INCORRECT_PASSWORD
- );
- });
-});
-
-describe("Auth Controller - refreshAuthToken", function() {
- let req, res, next, issueTokenStub;
-
- beforeEach(function() {
- req = {
- headers: {
- "x-refresh-token": "valid_refresh_token",
- authorization: "Bearer old_auth_token",
- },
- settingsService: {
- getSettings: sinon.stub().resolves({
- jwtSecret: "my_secret",
- refreshTokenSecret: "my_refresh_secret",
- }),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- sinon.stub(jwt, "verify");
-
- issueTokenStub = sinon.stub().returns("new_auth_token");
- sinon.replace({ issueToken }, "issueToken", issueTokenStub);
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject if no refresh token is provided", async function() {
- delete req.headers["x-refresh-token"];
- await refreshAuthToken(req, res, next);
-
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal(errorMessages.NO_REFRESH_TOKEN);
- expect(next.firstCall.args[0].status).to.equal(401);
- });
-
- it("should reject if the refresh token is invalid", async function() {
- jwt.verify.yields(new Error("invalid token"));
- await refreshAuthToken(req, res, next);
-
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal(errorMessages.INVALID_REFRESH_TOKEN);
- expect(next.firstCall.args[0].status).to.equal(401);
- });
-
- it("should reject if the refresh token is expired", async function() {
- const error = new Error("Token expired");
- error.name = "TokenExpiredError";
- jwt.verify.yields(error);
- await refreshAuthToken(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal(errorMessages.EXPIRED_REFRESH_TOKEN);
- expect(next.firstCall.args[0].status).to.equal(401);
- });
-
- it("should reject if settingsService.getSettings fails", async function() {
- req.settingsService.getSettings.rejects(
- new Error("settingsService.getSettings error")
- );
- await refreshAuthToken(req, res, next);
-
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error");
- });
-
- it("should generate a new auth token if the refresh token is valid", async function() {
- const decodedPayload = { expiresIn: "60" };
- jwt.verify.callsFake(() => {
- return decodedPayload;
- });
- await refreshAuthToken(req, res, next);
-
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_TOKEN_REFRESHED,
- data: {
- user: decodedPayload,
- token: sinon.match.string,
- refreshToken: "valid_refresh_token",
- },
- })
- ).to.be.true;
- });
-});
-
-describe("Auth Controller - editUser", function() {
- let req, res, next, stub, user;
-
- beforeEach(function() {
- req = {
- params: { userId: "123" },
- body: { password: "Password1!", newPassword: "Password2!" },
- headers: { authorization: "Bearer token" },
- user: { _id: "123" },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }),
- },
- db: {
- getUserByEmail: sinon.stub(),
- updateUser: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- stub = sinon.stub(jwt, "verify").returns({ email: "test@example.com" });
- });
-
- afterEach(function() {
- sinon.restore();
- stub.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await editUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if body validation fails", async function() {
- req.body = { invalid: 1 };
- await editUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if param.userId !== req.user._id", async function() {
- req.params = { userId: "456" };
- await editUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(401);
- });
-
- it("should reject with an error if !req.body.password and getUserByEmail fails", async function() {
- req.db.getUserByEmail.rejects(new Error("getUserByEmail error"));
- await editUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getUserByEmail error");
- });
-
- it("should reject with an error if user.comparePassword fails", async function() {
- req.db.getUserByEmail.returns({
- comparePassword: sinon.stub().rejects(new Error("Bad Password Match")),
- });
- await editUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Bad Password Match");
- });
-
- it("should reject with an error if user.comparePassword returns false", async function() {
- req.db.getUserByEmail.returns({
- comparePassword: sinon.stub().returns(false),
- });
- await editUser(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(401);
- expect(next.firstCall.args[0].message).to.equal(
- errorMessages.AUTH_INCORRECT_PASSWORD
- );
- });
-
- it("should edit a user if it receives a proper request", async function() {
- const user = {
- comparePassword: sinon.stub().resolves(true),
- };
- req.db.getUserByEmail.resolves(user);
- req.db.updateUser.resolves({ email: "test@example.com" });
-
- await editUser(req, res, next);
-
- expect(req.db.getUserByEmail.calledOnce).to.be.true;
- expect(req.db.updateUser.calledOnce).to.be.true;
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_UPDATE_USER,
- data: { email: "test@example.com" },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should edit a user if it receives a proper request and both password fields are undefined", async function() {
- req.body.password = undefined;
- req.body.newPassword = undefined;
- req.db.getUserByEmail.resolves(user);
- req.db.updateUser.resolves({ email: "test@example.com" });
-
- await editUser(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_UPDATE_USER,
- data: { email: "test@example.com" },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should reject an edit request if password format is incorrect", async function() {
- req.body = { password: "bad_password", newPassword: "bad_password" };
- const user = {
- comparePassword: sinon.stub().resolves(true),
- };
- req.db.getUserByEmail.resolves(user);
-
- await editUser(req, res, next);
-
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-});
-
-describe("Auth Controller - checkSuperadminExists", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- db: {
- checkSuperadmin: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- it("should reject with an error if checkSuperadmin fails", async function() {
- req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error"));
- await checkSuperadminExists(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error");
- });
-
- it("should return true if a superadmin exists", async function() {
- req.db.checkSuperadmin.resolves(true);
- await checkSuperadminExists(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_SUPERADMIN_EXISTS,
- data: true,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should return false if a superadmin does not exist", async function() {
- req.db.checkSuperadmin.resolves(false);
- await checkSuperadminExists(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.AUTH_SUPERADMIN_EXISTS,
- data: false,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-});
-
-describe("Auth Controller - requestRecovery", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: { email: "test@test.com" },
- db: {
- getUserByEmail: sinon.stub(),
- requestRecoveryToken: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub().returns({ clientHost: "http://localhost" }),
- },
- emailService: {
- buildAndSendEmail: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- it("should reject with an error if validation fails", async function() {
- req.body = {};
- await requestRecovery(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if getUserByEmail fails", async function() {
- req.db.getUserByEmail.rejects(new Error("getUserByEmail error"));
- await requestRecovery(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getUserByEmail error");
- });
-
- it("should throw an error if the user is not found", async function() {
- req.db.getUserByEmail.resolves(null);
- await requestRecovery(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- // expect(next.firstCall.args[0].message).to.equal(
- // errorMessages.FRIENDLY_ERROR
- // );
- });
-
- it("should throw an error if the email is not provided", async function() {
- req.body = {};
- await requestRecovery(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should return a success message if the email is provided", async function() {
- const user = { firstName: "John" };
- const recoveryToken = { token: "recovery-token" };
- const msgId = "message-id";
- req.db.getUserByEmail.resolves(user);
- req.db.requestRecoveryToken.resolves(recoveryToken);
- req.emailService.buildAndSendEmail.resolves(msgId);
- await requestRecovery(req, res, next);
- expect(req.db.getUserByEmail.calledOnceWith("test@test.com")).to.be.true;
- expect(req.db.requestRecoveryToken.calledOnceWith(req, res)).to.be.true;
- expect(
- req.emailService.buildAndSendEmail.calledOnceWith(
- "passwordResetTemplate",
- {
- name: "John",
- email: "test@test.com",
- url: "http://localhost/set-new-password/recovery-token",
- },
- "test@test.com",
- "Checkmate Password Reset"
- )
- ).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN,
- data: msgId,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-});
-
-describe("Auth Controller - validateRecovery", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: { recoveryToken: "recovery-token" },
- db: {
- validateRecoveryToken: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- it("should reject with an error if validation fails", async function() {
- req.body = {};
- await validateRecovery(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if validateRecoveryToken fails", async function() {
- req.db.validateRecoveryToken.rejects(new Error("validateRecoveryToken error"));
- await validateRecovery(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("validateRecoveryToken error");
- });
-
- it("should return a success message if the token is valid", async function() {
- req.db.validateRecoveryToken.resolves();
- await validateRecovery(req, res, next);
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-});
-
-describe("Auth Controller - resetPassword", function() {
- let req, res, next, newPasswordValidation, handleValidationError, handleError;
-
- beforeEach(function() {
- req = {
- body: {
- recoveryToken: "recovery-token",
- password: "Password1!",
- },
- db: {
- resetPassword: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- newPasswordValidation = {
- validateAsync: sinon.stub(),
- };
- handleValidationError = sinon.stub();
- handleError = sinon.stub();
- });
-
- it("should reject with an error if validation fails", async function() {
- req.body = { password: "bad_password" };
- await resetPassword(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if resetPassword fails", async function() {
- const error = new Error("resetPassword error");
- newPasswordValidation.validateAsync.resolves();
- req.db.resetPassword.rejects(error);
- await resetPassword(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("resetPassword error");
- });
-
- it("should reset password successfully", async function() {
- const user = { _doc: {} };
- const appSettings = { jwtSecret: "my_secret" };
- const token = "token";
-
- newPasswordValidation.validateAsync.resolves();
- req.db.resetPassword.resolves(user);
- req.settingsService.getSettings.resolves(appSettings);
-
- await resetPassword(req, res, next);
-
- expect(req.db.resetPassword.calledOnceWith(req, res)).to.be.true;
- expect(req.settingsService.getSettings.calledOnce).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.AUTH_RESET_PASSWORD,
- data: { user: sinon.match.object, token: sinon.match.string },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-});
-
-describe("Auth Controller - deleteUser", function() {
- let req, res, next, handleError;
-
- beforeEach(function() {
- req = {
- headers: {
- authorization: "Bearer token",
- },
- db: {
- getUserByEmail: sinon.stub(),
- getMonitorsByTeamId: sinon.stub(),
- deleteJob: sinon.stub(),
- deleteChecks: sinon.stub(),
- deletePageSpeedChecksByMonitorId: sinon.stub(),
- deleteNotificationsByMonitorId: sinon.stub(),
- deleteTeam: sinon.stub(),
- deleteAllOtherUsers: sinon.stub(),
- deleteMonitorsByUserId: sinon.stub(),
- deleteUser: sinon.stub(),
- },
- jobQueue: {
- deleteJob: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
-
- sinon.stub(jwt, "decode");
-
- handleError = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should throw an error if user is not found", async function() {
- jwt.decode.returns({ email: "test@example.com" });
- req.db.getUserByEmail.throws(new Error(errorMessages.DB_USER_NOT_FOUND));
-
- await deleteUser(req, res, next);
-
- expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true;
- expect(next.calledOnce).to.be.true;
- expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND);
- expect(res.status.notCalled).to.be.true;
- expect(res.json.notCalled).to.be.true;
- });
-
- it("should delete user and associated data if user is superadmin", async function() {
- const user = {
- _id: "user_id",
- email: "test@example.com",
- role: ["superadmin"],
- teamId: "team_id",
- };
- const monitors = [{ _id: "monitor_id" }];
-
- jwt.decode.returns({ email: "test@example.com" });
- req.db.getUserByEmail.resolves(user);
- req.db.getMonitorsByTeamId.resolves({ monitors });
-
- await deleteUser(req, res, next);
-
- expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true;
- expect(
- req.db.getMonitorsByTeamId.calledOnceWith({
- params: { teamId: "team_id" },
- })
- ).to.be.true;
- expect(req.jobQueue.deleteJob.calledOnceWith(monitors[0])).to.be.true;
- expect(req.db.deleteChecks.calledOnceWith("monitor_id")).to.be.true;
- expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id")).to.be
- .true;
- expect(req.db.deleteNotificationsByMonitorId.calledOnceWith("monitor_id")).to.be.true;
- expect(req.db.deleteTeam.calledOnceWith("team_id")).to.be.true;
- expect(req.db.deleteAllOtherUsers.calledOnce).to.be.true;
- expect(req.db.deleteMonitorsByUserId.calledOnceWith("user_id")).to.be.true;
- expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.AUTH_DELETE_USER,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should delete user if user is not superadmin", async function() {
- const user = {
- _id: "user_id",
- email: "test@example.com",
- role: ["user"],
- teamId: "team_id",
- };
-
- jwt.decode.returns({ email: "test@example.com" });
- req.db.getUserByEmail.resolves(user);
-
- await deleteUser(req, res, next);
-
- expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true;
- expect(
- req.db.getMonitorsByTeamId.calledOnceWith({
- params: { teamId: "team_id" },
- })
- ).to.be.true;
- expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.AUTH_DELETE_USER,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should handle errors", async function() {
- const error = new Error("Something went wrong");
- const SERVICE_NAME = "AuthController";
- jwt.decode.returns({ email: "test@example.com" });
- req.db.getUserByEmail.rejects(error);
- await deleteUser(req, res, next);
- expect(next.calledOnce).to.be.true;
- expect(next.firstCall.args[0].message).to.equal("Something went wrong");
- expect(res.status.notCalled).to.be.true;
- expect(res.json.notCalled).to.be.true;
- });
-});
-
-describe("Auth Controller - getAllUsers", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- db: {
- getAllUsers: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore(); // Restore the original methods after each test
- });
-
- it("should return 200 and all users", async function() {
- const allUsers = [{ id: 1, name: "John Doe" }];
- req.db.getAllUsers.resolves(allUsers);
-
- await getAllUsers(req, res, next);
-
- expect(req.db.getAllUsers.calledOnce).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: "Got all users",
- data: allUsers,
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should call next with error when an exception occurs", async function() {
- const error = new Error("Something went wrong");
- req.db.getAllUsers.rejects(error);
- await getAllUsers(req, res, next);
- expect(req.db.getAllUsers.calledOnce).to.be.true;
- expect(next.calledOnce).to.be.true;
- expect(res.status.notCalled).to.be.true;
- expect(res.json.notCalled).to.be.true;
- });
-});
diff --git a/Server/tests/controllers/checkController.test.js b/Server/tests/controllers/checkController.test.js
deleted file mode 100644
index 0c00b9487..000000000
--- a/Server/tests/controllers/checkController.test.js
+++ /dev/null
@@ -1,379 +0,0 @@
-import {
- createCheck,
- getChecks,
- getTeamChecks,
- deleteChecks,
- deleteChecksByTeamId,
- updateChecksTTL,
-} from "../../controllers/checkController.js";
-import jwt from "jsonwebtoken";
-import { errorMessages, successMessages } from "../../utils/messages.js";
-import sinon from "sinon";
-describe("Check Controller - createCheck", function() {
- let req, res, next, handleError;
-
- beforeEach(function() {
- req = {
- params: {},
- body: {},
- db: {
- createCheck: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- handleError = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore(); // Restore the original methods after each test
- });
-
- it("should reject with a validation if params are invalid", async function() {
- await createCheck(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with a validation error if body is invalid", async function() {
- req.params = {
- monitorId: "monitorId",
- };
- await createCheck(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should call next with error if data retrieval fails", async function() {
- req.params = {
- monitorId: "monitorId",
- };
- req.body = {
- monitorId: "monitorId",
- status: true,
- responseTime: 100,
- statusCode: 200,
- message: "message",
- };
- req.db.createCheck.rejects(new Error("error"));
- await createCheck(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- });
-
- it("should return a success message if check is created", async function() {
- req.params = {
- monitorId: "monitorId",
- };
- req.db.createCheck.resolves({ id: "123" });
- req.body = {
- monitorId: "monitorId",
- status: true,
- responseTime: 100,
- statusCode: 200,
- message: "message",
- };
- await createCheck(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.CHECK_CREATE,
- data: { id: "123" },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-});
-
-describe("Check Controller - getChecks", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- query: {},
- db: {
- getChecks: sinon.stub(),
- getChecksCount: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with a validation error if params are invalid", async function() {
- await getChecks(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should return a success message if checks are found", async function() {
- req.params = {
- monitorId: "monitorId",
- };
- req.db.getChecks.resolves([{ id: "123" }]);
- req.db.getChecksCount.resolves(1);
- await getChecks(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: successMessages.CHECK_GET,
- data: { checksCount: 1, checks: [{ id: "123" }] },
- })
- ).to.be.true;
- expect(next.notCalled).to.be.true;
- });
-
- it("should call next with error if data retrieval fails", async function() {
- req.params = {
- monitorId: "monitorId",
- };
- req.db.getChecks.rejects(new Error("error"));
- await getChecks(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- });
-});
-
-describe("Check Controller - getTeamChecks", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- query: {},
- db: {
- getTeamChecks: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with a validation error if params are invalid", async function() {
- await getTeamChecks(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should return 200 and check data on successful validation and data retrieval", async function() {
- req.params = { teamId: "1" };
- const checkData = [{ id: 1, name: "Check 1" }];
- req.db.getTeamChecks.resolves(checkData);
-
- await getTeamChecks(req, res, next);
- expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.CHECK_GET,
- data: checkData,
- })
- ).to.be.true;
- });
-
- it("should call next with error if data retrieval fails", async function() {
- req.params = { teamId: "1" };
- req.db.getTeamChecks.rejects(new Error("Retrieval Error"));
- await getTeamChecks(req, res, next);
- expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true;
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(res.status.notCalled).to.be.true;
- expect(res.json.notCalled).to.be.true;
- });
-});
-
-describe("Check Controller - deleteChecks", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- db: {
- deleteChecks: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- await deleteChecks(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should call next with error if data retrieval fails", async function() {
- req.params = { monitorId: "1" };
- req.db.deleteChecks.rejects(new Error("Deletion Error"));
- await deleteChecks(req, res, next);
- expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true;
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(res.status.notCalled).to.be.true;
- expect(res.json.notCalled).to.be.true;
- });
-
- it("should delete checks successfully", async function() {
- req.params = { monitorId: "123" };
- req.db.deleteChecks.resolves(1);
- await deleteChecks(req, res, next);
- expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.CHECK_DELETE,
- data: { deletedCount: 1 },
- })
- ).to.be.true;
- });
-});
-
-describe("Check Controller - deleteChecksByTeamId", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- db: {
- deleteChecksByTeamId: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- await deleteChecksByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should call next with error if data retrieval fails", async function() {
- req.params = { teamId: "1" };
- req.db.deleteChecksByTeamId.rejects(new Error("Deletion Error"));
- await deleteChecksByTeamId(req, res, next);
- expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be.true;
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(res.status.notCalled).to.be.true;
- expect(res.json.notCalled).to.be.true;
- });
-
- it("should delete checks successfully", async function() {
- req.params = { teamId: "123" };
- req.db.deleteChecksByTeamId.resolves(1);
- await deleteChecksByTeamId(req, res, next);
- expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.CHECK_DELETE,
- data: { deletedCount: 1 },
- })
- ).to.be.true;
- });
-});
-
-describe("Check Controller - updateCheckTTL", function() {
- let stub, req, res, next;
-
- beforeEach(function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
-
- req = {
- body: {},
- headers: { authorization: "Bearer token" },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }),
- },
- db: {
- updateChecksTTL: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- stub.restore();
- });
-
- it("should reject if body validation fails", async function() {
- await updateChecksTTL(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should throw a JwtError if verification fails", async function() {
- stub.restore();
- req.body = {
- ttl: 1,
- };
- await updateChecksTTL(req, res, next);
- expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError);
- });
-
- it("should call next with error if data retrieval fails", async function() {
- req.body = {
- ttl: 1,
- };
- req.db.updateChecksTTL.rejects(new Error("Update Error"));
- await updateChecksTTL(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- });
-
- it("should update TTL successfully", async function() {
- req.body = {
- ttl: 1,
- };
- req.db.updateChecksTTL.resolves();
- await updateChecksTTL(req, res, next);
- expect(req.db.updateChecksTTL.calledOnceWith("123", 1 * 86400)).to.be.true;
- expect(res.status.calledOnceWith(200)).to.be.true;
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.CHECK_UPDATE_TTL,
- })
- ).to.be.true;
- });
-});
diff --git a/Server/tests/controllers/controllerUtils.test.js b/Server/tests/controllers/controllerUtils.test.js
deleted file mode 100644
index cb298e8a1..000000000
--- a/Server/tests/controllers/controllerUtils.test.js
+++ /dev/null
@@ -1,161 +0,0 @@
-import sinon from "sinon";
-
-import {
- handleValidationError,
- handleError,
- fetchMonitorCertificate,
-} from "../../controllers/controllerUtils.js";
-import { expect } from "chai";
-import sslChecker from "ssl-checker";
-import { afterEach } from "node:test";
-import exp from "constants";
-
-describe("controllerUtils - handleValidationError", function() {
- it("should set status to 422", function() {
- const error = {};
- const serviceName = "TestService";
- const result = handleValidationError(error, serviceName);
- expect(result.status).to.equal(422);
- });
-
- it("should set service to the provided serviceName", function() {
- const error = {};
- const serviceName = "TestService";
- const result = handleValidationError(error, serviceName);
- expect(result.service).to.equal(serviceName);
- });
-
- it("should set message to error.details[0].message if present", function() {
- const error = {
- details: [{ message: "Detail message" }],
- };
- const serviceName = "TestService";
- const result = handleValidationError(error, serviceName);
- expect(result.message).to.equal("Detail message");
- });
-
- it("should set message to error.message if error.details is not present", function() {
- const error = {
- message: "Error message",
- };
- const serviceName = "TestService";
- const result = handleValidationError(error, serviceName);
- expect(result.message).to.equal("Error message");
- });
-
- it('should set message to "Validation Error" if neither error.details nor error.message is present', function() {
- const error = {};
- const serviceName = "TestService";
- const result = handleValidationError(error, serviceName);
- expect(result.message).to.equal("Validation Error");
- });
-});
-
-describe("controllerUtils - handleError", function() {
- it("should set stats to the provided status if error.code is undefined", function() {
- const error = {};
- const serviceName = "TestService";
- const method = "testMethod";
- const status = 400;
- const result = handleError(error, serviceName, method, status);
- expect(result.status).to.equal(status);
- });
-
- it("should not overwrite error.code if it is already defined", function() {
- const error = { status: 404 };
- const serviceName = "TestService";
- const method = "testMethod";
- const status = 400;
- const result = handleError(error, serviceName, method, status);
- expect(result.status).to.equal(404);
- });
-
- it("should set service to the provided serviceName if error.service is undefined", function() {
- const error = {};
- const serviceName = "TestService";
- const method = "testMethod";
- const result = handleError(error, serviceName, method);
- expect(result.service).to.equal(serviceName);
- });
-
- it("should not overwrite error.service if it is already defined", function() {
- const error = { service: "ExistingService" };
- const serviceName = "TestService";
- const method = "testMethod";
- const result = handleError(error, serviceName, method);
- expect(result.service).to.equal("ExistingService");
- });
-
- it("should set method to the provided method if error.method is undefined", function() {
- const error = {};
- const serviceName = "TestService";
- const method = "testMethod";
- const result = handleError(error, serviceName, method);
- expect(result.method).to.equal(method);
- });
-
- it("should not overwrite error.method if it is already defined", function() {
- const error = { method: "existingMethod" };
- const serviceName = "TestService";
- const method = "testMethod";
- const result = handleError(error, serviceName, method);
- expect(result.method).to.equal("existingMethod");
- });
-
- it("should set code to 500 if error.code is undefined and no code is provided", function() {
- const error = {};
- const serviceName = "TestService";
- const method = "testMethod";
- const result = handleError(error, serviceName, method);
- expect(result.status).to.equal(500);
- });
-});
-
-describe("controllerUtils - fetchMonitorCertificate", function() {
- let sslChecker, monitor;
-
- beforeEach(function() {
- monitor = {
- url: "https://www.google.com",
- };
- sslChecker = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should reject with an error if a URL does not parse", async function() {
- monitor.url = "invalidurl";
- try {
- await fetchMonitorCertificate(sslChecker, monitor);
- } catch (error) {
- expect(error).to.be.an("error");
- expect(error.message).to.equal("Invalid URL");
- }
- });
-
- it("should reject with an error if sslChecker throws an error", async function() {
- sslChecker.rejects(new Error("Test error"));
- try {
- await fetchMonitorCertificate(sslChecker, monitor);
- } catch (error) {
- expect(error).to.be.an("error");
- expect(error.message).to.equal("Test error");
- }
- });
-
- it("should return a certificate if sslChecker resolves", async function() {
- sslChecker.resolves({ validTo: "2022-01-01" });
- const result = await fetchMonitorCertificate(sslChecker, monitor);
- expect(result).to.deep.equal({ validTo: "2022-01-01" });
- });
-
- it("should throw an error if a ssl-checker returns null", async function() {
- sslChecker.returns(null);
- await fetchMonitorCertificate(sslChecker, monitor).catch((error) => {
- expect(error).to.be.an("error");
- expect(error.message).to.equal("Certificate not found");
- });
- });
-});
diff --git a/Server/tests/controllers/inviteController.test.js b/Server/tests/controllers/inviteController.test.js
deleted file mode 100644
index 389c4f06d..000000000
--- a/Server/tests/controllers/inviteController.test.js
+++ /dev/null
@@ -1,205 +0,0 @@
-import {
- issueInvitation,
- inviteVerifyController,
-} from "../../controllers/inviteController.js";
-import jwt from "jsonwebtoken";
-import sinon from "sinon";
-import joi from "joi";
-describe("inviteController - issueInvitation", function() {
- let req, res, next, stub;
-
- beforeEach(function() {
- req = {
- headers: { authorization: "Bearer token" },
- body: {
- email: "test@test.com",
- role: ["admin"],
- teamId: "123",
- },
- db: { requestInviteToken: sinon.stub() },
- settingsService: { getSettings: sinon.stub() },
- emailService: { buildAndSendEmail: sinon.stub() },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if role validation fails", async function() {
- stub = sinon.stub(jwt, "decode").callsFake(() => {
- return { role: ["bad_role"], firstname: "first_name", teamId: "1" };
- });
- await issueInvitation(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0]).to.be.instanceOf(joi.ValidationError);
- expect(next.firstCall.args[0].status).to.equal(422);
- stub.restore();
- });
-
- it("should reject with an error if body validation fails", async function() {
- stub = sinon.stub(jwt, "decode").callsFake(() => {
- return { role: ["admin"], firstname: "first_name", teamId: "1" };
- });
- req.body = {};
- await issueInvitation(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- stub.restore();
- });
-
- it("should reject with an error if DB operations fail", async function() {
- stub = sinon.stub(jwt, "decode").callsFake(() => {
- return { role: ["admin"], firstname: "first_name", teamId: "1" };
- });
- req.db.requestInviteToken.throws(new Error("DB error"));
- await issueInvitation(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- stub.restore();
- });
-
- it("should send an invite successfully", async function() {
- const token = "token";
- const decodedToken = {
- role: "admin",
- firstname: "John",
- teamId: "team123",
- };
- const inviteToken = { token: "inviteToken" };
- const clientHost = "http://localhost";
-
- stub = sinon.stub(jwt, "decode").callsFake(() => {
- return decodedToken;
- });
- req.db.requestInviteToken.resolves(inviteToken);
- req.settingsService.getSettings.returns({ clientHost });
- req.emailService.buildAndSendEmail.resolves();
- await issueInvitation(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: "Invite sent",
- data: inviteToken,
- })
- ).to.be.true;
- stub.restore();
- });
-
- it("should send an email successfully", async function() {
- const token = "token";
- const decodedToken = {
- role: "admin",
- firstname: "John",
- teamId: "team123",
- };
- const inviteToken = { token: "inviteToken" };
- const clientHost = "http://localhost";
-
- stub = sinon.stub(jwt, "decode").callsFake(() => {
- return decodedToken;
- });
- req.db.requestInviteToken.resolves(inviteToken);
- req.settingsService.getSettings.returns({ clientHost });
- req.emailService.buildAndSendEmail.resolves();
-
- await issueInvitation(req, res, next);
- expect(req.emailService.buildAndSendEmail.calledOnce).to.be.true;
- expect(
- req.emailService.buildAndSendEmail.calledWith(
- "employeeActivationTemplate",
- {
- name: "John",
- link: "http://localhost/register/inviteToken",
- },
- "test@test.com",
- "Welcome to Uptime Monitor"
- )
- ).to.be.true;
- stub.restore();
- });
-
- it("should continue executing if sending an email fails", async function() {
- const token = "token";
- req.emailService.buildAndSendEmail.rejects(new Error("Email error"));
- const decodedToken = {
- role: "admin",
- firstname: "John",
- teamId: "team123",
- };
- const inviteToken = { token: "inviteToken" };
- const clientHost = "http://localhost";
-
- stub = sinon.stub(jwt, "decode").callsFake(() => {
- return decodedToken;
- });
- req.db.requestInviteToken.resolves(inviteToken);
- req.settingsService.getSettings.returns({ clientHost });
- await issueInvitation(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- msg: "Invite sent",
- data: inviteToken,
- })
- ).to.be.true;
- stub.restore();
- });
-});
-
-describe("inviteController - inviteVerifyController", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: { token: "token" },
- db: {
- getInviteToken: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if body validation fails", async function() {
- req.body = {};
- await inviteVerifyController(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getInviteToken.throws(new Error("DB error"));
- await inviteVerifyController(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return 200 and invite data when validation and invite retrieval are successful", async function() {
- req.db.getInviteToken.resolves({ invite: "data" });
- await inviteVerifyController(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- status: "success",
- msg: "Invite verified",
- data: { invite: "data" },
- })
- ).to.be.true;
- expect(next.called).to.be.false;
- });
-});
diff --git a/Server/tests/controllers/maintenanceWindowController.test.js b/Server/tests/controllers/maintenanceWindowController.test.js
deleted file mode 100644
index 974bc07be..000000000
--- a/Server/tests/controllers/maintenanceWindowController.test.js
+++ /dev/null
@@ -1,422 +0,0 @@
-import {
- createMaintenanceWindows,
- getMaintenanceWindowById,
- getMaintenanceWindowsByTeamId,
- getMaintenanceWindowsByMonitorId,
- deleteMaintenanceWindow,
- editMaintenanceWindow,
-} from "../../controllers/maintenanceWindowController.js";
-
-import jwt from "jsonwebtoken";
-import { successMessages } from "../../utils/messages.js";
-import sinon from "sinon";
-
-describe("maintenanceWindowController - createMaintenanceWindows", function() {
- let req, res, next, stub;
-
- beforeEach(function() {
- req = {
- body: {
- monitors: ["66ff52e7c5911c61698ac724"],
- name: "window",
- active: true,
- start: "2024-10-11T05:27:13.747Z",
- end: "2024-10-11T05:27:14.747Z",
- repeat: "123",
- },
- headers: {
- authorization: "Bearer token",
- },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }),
- },
- db: {
- createMaintenanceWindow: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if body validation fails", async function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- req.body = {};
- await createMaintenanceWindows(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- stub.restore();
- });
-
- it("should reject with an error if jwt.verify fails", async function() {
- stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError());
- await createMaintenanceWindows(req, res, next);
- expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError);
- stub.restore();
- });
-
- it("should reject with an error DB operations fail", async function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- req.db.createMaintenanceWindow.throws(new Error("DB error"));
- await createMaintenanceWindows(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- stub.restore();
- });
-
- it("should return success message if all operations are successful", async function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- await createMaintenanceWindows(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(201);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_CREATE,
- })
- ).to.be.true;
- stub.restore();
- });
-
- it("should return success message if all operations are successful with active set to undefined", async function() {
- req.body.active = undefined;
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- await createMaintenanceWindows(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(201);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_CREATE,
- })
- ).to.be.true;
- stub.restore();
- });
-});
-
-describe("maintenanceWindowController - getMaintenanceWindowById", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: {},
- params: {
- id: "123",
- },
- headers: {
- authorization: "Bearer token",
- },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }),
- },
- db: {
- getMaintenanceWindowById: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- it("should reject if param validation fails", async function() {
- req.params = {};
- await getMaintenanceWindowById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject if DB operations fail", async function() {
- req.db.getMaintenanceWindowById.throws(new Error("DB error"));
- await getMaintenanceWindowById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message with data if all operations are successful", async function() {
- req.db.getMaintenanceWindowById.returns({ id: "123" });
- await getMaintenanceWindowById(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID,
- data: { id: "123" },
- })
- ).to.be.true;
- });
-});
-
-describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function() {
- let req, res, next, stub;
-
- beforeEach(function() {
- req = {
- body: {},
- params: {},
- query: {},
- headers: {
- authorization: "Bearer token",
- },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }),
- },
- db: {
- getMaintenanceWindowsByTeamId: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- it("should reject if query validation fails", async function() {
- req.query = {
- invalid: 1,
- };
- await getMaintenanceWindowsByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject if jwt.verify fails", async function() {
- stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError());
- await getMaintenanceWindowsByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError);
- stub.restore();
- });
-
- it("should reject with an error if DB operations fail", async function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- req.db.getMaintenanceWindowsByTeamId.throws(new Error("DB error"));
- await getMaintenanceWindowsByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- stub.restore();
- });
-
- it("should return success message with data if all operations are successful", async function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- req.db.getMaintenanceWindowsByTeamId.returns([{ id: "123" }]);
- await getMaintenanceWindowsByTeamId(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM,
- data: [{ id: jwt.verify().teamId }],
- })
- ).to.be.true;
- stub.restore();
- });
-});
-
-describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: {},
- params: {
- monitorId: "123",
- },
- query: {},
- headers: {
- authorization: "Bearer token",
- },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }),
- },
- db: {
- getMaintenanceWindowsByMonitorId: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject if param validation fails", async function() {
- req.params = {};
- await getMaintenanceWindowsByMonitorId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getMaintenanceWindowsByMonitorId.throws(new Error("DB error"));
- await getMaintenanceWindowsByMonitorId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message with data if all operations are successful", async function() {
- const data = [{ monitorId: "123" }];
- req.db.getMaintenanceWindowsByMonitorId.returns(data);
- await getMaintenanceWindowsByMonitorId(req, res, next);
- expect(req.db.getMaintenanceWindowsByMonitorId.calledOnceWith(req.params.monitorId));
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR,
- data: data,
- })
- ).to.be.true;
- });
-});
-
-describe("maintenanceWindowController - deleteMaintenanceWindow", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: {},
- params: {
- id: "123",
- },
- query: {},
- headers: {
- authorization: "Bearer token",
- },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }),
- },
- db: {
- deleteMaintenanceWindowById: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject if param validation fails", async function() {
- req.params = {};
- await deleteMaintenanceWindow(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.deleteMaintenanceWindowById.throws(new Error("DB error"));
- await deleteMaintenanceWindow(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message if all operations are successful", async function() {
- await deleteMaintenanceWindow(req, res, next);
- expect(req.db.deleteMaintenanceWindowById.calledOnceWith(req.params.id));
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_DELETE,
- })
- ).to.be.true;
- });
-});
-
-describe("maintenanceWindowController - editMaintenanceWindow", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- body: {
- active: true,
- name: "test",
- },
- params: {
- id: "123",
- },
- query: {},
- headers: {
- authorization: "Bearer token",
- },
- settingsService: {
- getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }),
- },
- db: {
- editMaintenanceWindowById: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject if param validation fails", async function() {
- req.params = {};
- await editMaintenanceWindow(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject if body validation fails", async function() {
- req.body = { invalid: 1 };
- await editMaintenanceWindow(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.editMaintenanceWindowById.throws(new Error("DB error"));
- await editMaintenanceWindow(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message with data if all operations are successful", async function() {
- const data = { id: "123" };
- req.db.editMaintenanceWindowById.returns(data);
-
- await editMaintenanceWindow(req, res, next);
- expect(req.db.editMaintenanceWindowById.calledOnceWith(req.params.id, req.body));
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MAINTENANCE_WINDOW_EDIT,
- data: data,
- })
- ).to.be.true;
- });
-});
diff --git a/Server/tests/controllers/monitorController.test.js b/Server/tests/controllers/monitorController.test.js
deleted file mode 100644
index 33440d897..000000000
--- a/Server/tests/controllers/monitorController.test.js
+++ /dev/null
@@ -1,1124 +0,0 @@
-import {
- getAllMonitors,
- getAllMonitorsWithUptimeStats,
- getMonitorStatsById,
- getMonitorCertificate,
- getMonitorById,
- getMonitorsAndSummaryByTeamId,
- getMonitorsByTeamId,
- createMonitor,
- checkEndpointResolution,
- deleteMonitor,
- deleteAllMonitors,
- editMonitor,
- pauseMonitor,
- addDemoMonitors,
-} from "../../controllers/monitorController.js";
-import jwt from "jsonwebtoken";
-import sinon from "sinon";
-import { successMessages } from "../../utils/messages.js";
-import logger from "../../utils/logger.js";
-import axios from "axios";
-const SERVICE_NAME = "monitorController";
-
-describe("Monitor Controller - getAllMonitors", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- query: {},
- body: {},
- db: {
- getAllMonitors: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getAllMonitors.throws(new Error("DB error"));
- await getAllMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const data = [{ monitor: "data" }];
- req.db.getAllMonitors.returns(data);
- await getAllMonitors(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_GET_ALL,
- data: data,
- })
- ).to.be.true;
- });
-});
-describe("Monitor Controller - getAllMonitorsWithUptimeStats", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- query: {},
- body: {},
- db: {
- getAllMonitorsWithUptimeStats: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getAllMonitorsWithUptimeStats.throws(new Error("DB error"));
- await getAllMonitorsWithUptimeStats(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const data = [{ monitor: "data" }];
- req.db.getAllMonitorsWithUptimeStats.returns(data);
- await getAllMonitorsWithUptimeStats(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_GET_ALL,
- data: data,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - getMonitorStatsById", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {
- monitorId: "123",
- },
- query: {},
- body: {},
- db: {
- getMonitorStatsById: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await getMonitorStatsById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if query validation fails", async function() {
- req.query = { invalid: 1 };
- await getMonitorStatsById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getMonitorStatsById.throws(new Error("DB error"));
- await getMonitorStatsById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const data = [{ monitorStats: "data" }];
- req.db.getMonitorStatsById.returns(data);
- await getMonitorStatsById(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_STATS_BY_ID,
- data: data,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - getMonitorCertificate", function() {
- let req, res, next, fetchMonitorCertificate;
-
- beforeEach(function() {
- req = {
- params: {
- monitorId: "123",
- },
- query: {},
- body: {},
- db: {
- getMonitorById: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- fetchMonitorCertificate = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await getMonitorCertificate(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if getMonitorById operation fails", async function() {
- req.db.getMonitorById.throws(new Error("DB error"));
- await getMonitorCertificate(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message and data if all operations succeed with a valid cert", async function() {
- req.db.getMonitorById.returns({ url: "https://www.google.com" });
- const data = { certificate: "cert", validTo: "2024/08/08" };
- fetchMonitorCertificate.returns(data);
- await getMonitorCertificate(req, res, next, fetchMonitorCertificate);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_CERTIFICATE,
- data: { certificateDate: new Date(data.validTo) },
- })
- ).to.be.true;
- });
-
- it("should return an error if fetchMonitorCertificate fails", async function() {
- req.db.getMonitorById.returns({ url: "https://www.google.com" });
- fetchMonitorCertificate.throws(new Error("Certificate error"));
- await getMonitorCertificate(req, res, next, fetchMonitorCertificate);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Certificate error");
- });
-});
-
-describe("Monitor Controller - getMonitorById", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {
- monitorId: "123",
- },
- query: {},
- body: {},
- db: {
- getMonitorById: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await getMonitorById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if query param validation fails", async function() {
- req.query = { invalid: 1 };
- await getMonitorById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getMonitorById.throws(new Error("DB error"));
- await getMonitorById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return 404 if a monitor is not found", async function() {
- const error = new Error("Monitor not found");
- error.status = 404;
- req.db.getMonitorById.throws(error);
- await getMonitorById(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(404);
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const data = { monitor: "data" };
- req.db.getMonitorById.returns(data);
- await getMonitorById(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_GET_BY_ID,
- data: data,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {
- teamId: "123",
- },
- query: {},
- body: {},
- db: {
- getMonitorsAndSummaryByTeamId: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await getMonitorsAndSummaryByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if query validation fails", async function() {
- req.query = { invalid: 1 };
- await getMonitorsAndSummaryByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getMonitorsAndSummaryByTeamId.throws(new Error("DB error"));
- await getMonitorsAndSummaryByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const data = { monitors: "data", summary: "data" };
- req.db.getMonitorsAndSummaryByTeamId.returns(data);
- await getMonitorsAndSummaryByTeamId(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId),
- data: data,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - getMonitorsByTeamId", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {
- teamId: "123",
- },
- query: {},
- body: {},
- db: {
- getMonitorsByTeamId: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await getMonitorsByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if query validation fails", async function() {
- req.query = { invalid: 1 };
- await getMonitorsByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB operations fail", async function() {
- req.db.getMonitorsByTeamId.throws(new Error("DB error"));
- await getMonitorsByTeamId(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const data = { monitors: "data" };
- req.db.getMonitorsByTeamId.returns(data);
- await getMonitorsByTeamId(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId),
- data: data,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - createMonitor", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- query: {},
- body: {
- userId: "123",
- teamId: "123",
- name: "test_monitor",
- description: "test_monitor_desc",
- type: "http",
- url: "https://example.com",
- notifications: [{ email: "example@example.com" }],
- },
- db: {
- createMonitor: sinon.stub(),
- createNotification: sinon.stub(),
- },
- jobQueue: {
- addJob: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if body validation fails", async function() {
- req.body = {};
- await createMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB createMonitor operation fail", async function() {
- req.db.createMonitor.throws(new Error("DB error"));
- await createMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if DB createNotification operation fail", async function() {
- req.db.createNotification.throws(new Error("DB error"));
- req.db.createMonitor.returns({ _id: "123" });
- await createMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if monitor.save operation fail", async function() {
- req.db.createMonitor.returns({
- _id: "123",
- save: sinon.stub().throws(new Error("Monitor save error")),
- });
- await createMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Monitor save error");
- });
-
- it("should throw an error if addJob operation fails", async function() {
- req.db.createMonitor.returns({ _id: "123", save: sinon.stub() });
- req.jobQueue.addJob.throws(new Error("Job error"));
- await createMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Job error");
- });
-
- it("should return success message and data if all operations succeed", async function() {
- const monitor = { _id: "123", save: sinon.stub() };
- req.db.createMonitor.returns(monitor);
- await createMonitor(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(201);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_CREATE,
- data: monitor,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - checkEndpointResolution", function() {
- let req, res, next, axiosGetStub;
-
- beforeEach(function() {
- req = { query: { monitorURL: "https://example.com" } };
- res = { status: sinon.stub().returnsThis(), json: sinon.stub() };
- next = sinon.stub();
- axiosGetStub = sinon.stub(axios, "get");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should resolve the URL successfully", async function() {
- axiosGetStub.resolves({ status: 200, statusText: "OK" });
- await checkEndpointResolution(req, res, next);
- expect(res.status.calledWith(200)).to.be.true;
- expect(
- res.json.calledWith({
- success: true,
- code: 200,
- statusText: "OK",
- msg: "URL resolved successfully",
- })
- ).to.be.true;
- expect(next.called).to.be.false;
- });
-
- it("should return an error if endpoint resolution fails", async function() {
- const axiosError = new Error("resolution failed");
- axiosError.code = "ENOTFOUND";
- axiosGetStub.rejects(axiosError);
- await checkEndpointResolution(req, res, next);
- expect(next.calledOnce).to.be.true;
- const errorPassedToNext = next.getCall(0).args[0];
- expect(errorPassedToNext).to.be.an.instanceOf(Error);
- expect(errorPassedToNext.message).to.include("resolution failed");
- expect(errorPassedToNext.code).to.equal("ENOTFOUND");
- expect(errorPassedToNext.status).to.equal(500);
- });
-
- it("should reject with an error if query validation fails", async function() {
- req.query.monitorURL = "invalid-url";
- await checkEndpointResolution(req, res, next);
- expect(next.calledOnce).to.be.true;
- const error = next.getCall(0).args[0];
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- expect(error.message).to.equal('"monitorURL" must be a valid uri');
- });
-});
-
-describe("Monitor Controller - deleteMonitor", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {
- monitorId: "123",
- },
- query: {},
- body: {},
- db: {
- deleteMonitor: sinon.stub(),
- deleteChecks: sinon.stub(),
- deletePageSpeedChecksByMonitorId: sinon.stub(),
- deleteNotificationsByMonitorId: sinon.stub(),
- },
- jobQueue: {
- deleteJob: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- sinon.stub(logger, "error");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await deleteMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if DB deleteMonitor operation fail", async function() {
- req.db.deleteMonitor.throws(new Error("DB error"));
- await deleteMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should log an error if deleteJob throws an error", async function() {
- const error = new Error("Job error");
- const monitor = { name: "test_monitor", _id: "123" };
- req.db.deleteMonitor.returns(monitor);
- req.jobQueue.deleteJob.rejects(error);
- await deleteMonitor(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`
- );
- });
-
- it("should log an error if deleteChecks throws an error", async function() {
- const error = new Error("Checks error");
- const monitor = { name: "test_monitor", _id: "123" };
- req.db.deleteMonitor.returns(monitor);
- req.db.deleteChecks.rejects(error);
- await deleteMonitor(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`
- );
- });
-
- it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function() {
- const error = new Error("PageSpeed error");
- const monitor = { name: "test_monitor", _id: "123" };
- req.db.deleteMonitor.returns(monitor);
- req.db.deletePageSpeedChecksByMonitorId.rejects(error);
- await deleteMonitor(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`
- );
- });
-
- it("should log an error if deleteNotificationsByMonitorId throws an error", async function() {
- const error = new Error("Notifications error");
- const monitor = { name: "test_monitor", _id: "123" };
- req.db.deleteMonitor.returns(monitor);
- req.db.deleteNotificationsByMonitorId.rejects(error);
- await deleteMonitor(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`
- );
- });
-
- it("should return success message if all operations succeed", async function() {
- const monitor = { name: "test_monitor", _id: "123" };
- req.db.deleteMonitor.returns(monitor);
- await deleteMonitor(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_DELETE,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - deleteAllMonitors", function() {
- let req, res, next, stub;
-
- beforeEach(function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { teamId: "123" };
- });
- req = {
- headers: {
- authorization: "Bearer token",
- },
- params: {
- monitorId: "123",
- },
- query: {},
- body: {},
- db: {
- deleteAllMonitors: sinon.stub(),
- deleteChecks: sinon.stub(),
- deletePageSpeedChecksByMonitorId: sinon.stub(),
- deleteNotificationsByMonitorId: sinon.stub(),
- },
- jobQueue: {
- deleteJob: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- sinon.stub(logger, "error");
- });
-
- afterEach(function() {
- sinon.restore();
- stub.restore();
- });
-
- it("should reject with an error if getTokenFromHeaders throws an error", async function() {
- req.headers = {};
- await deleteAllMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("No auth headers");
- expect(next.firstCall.args[0].status).to.equal(500);
- });
-
- it("should reject with an error if token validation fails", async function() {
- stub.restore();
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- await deleteAllMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError);
- });
-
- it("should reject with an error if DB deleteAllMonitors operation fail", async function() {
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.deleteAllMonitors.throws(new Error("DB error"));
- await deleteAllMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should log an error if deleteChecks throws an error", async function() {
- const monitors = [{ name: "test_monitor", _id: "123" }];
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 });
- const error = new Error("Check error");
- req.db.deleteChecks.rejects(error);
- await deleteAllMonitors(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}`
- );
- });
-
- it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function() {
- const monitors = [{ name: "test_monitor", _id: "123" }];
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 });
- const error = new Error("Pagespeed Check error");
- req.db.deletePageSpeedChecksByMonitorId.rejects(error);
- await deleteAllMonitors(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}`
- );
- });
-
- it("should log an error if deleteNotificationsByMonitorId throws an error", async function() {
- const monitors = [{ name: "test_monitor", _id: "123" }];
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 });
- const error = new Error("Notifications Check error");
- req.db.deleteNotificationsByMonitorId.rejects(error);
- await deleteAllMonitors(req, res, next);
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal(
- `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}`
- );
- });
-
- it("should return success message if all operations succeed", async function() {
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.deleteAllMonitors.returns({
- monitors: [{ name: "test_monitor", _id: "123" }],
- deletedCount: 1,
- });
- await deleteAllMonitors(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: "Deleted 1 monitors",
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - editMonitor", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {
- monitorId: "123",
- },
- query: {},
- body: {
- notifications: [{ email: "example@example.com" }],
- },
- db: {
- getMonitorById: sinon.stub(),
- editMonitor: sinon.stub(),
- deleteNotificationsByMonitorId: sinon.stub(),
- createNotification: sinon.stub(),
- },
- jobQueue: {
- deleteJob: sinon.stub(),
- addJob: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if body validation fails", async function() {
- req.body = { invalid: 1 };
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if getMonitorById operation fails", async function() {
- req.db.getMonitorById.throws(new Error("DB error"));
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if editMonitor operation fails", async function() {
- req.db.getMonitorById.returns({ teamId: "123" });
- req.db.editMonitor.throws(new Error("DB error"));
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if deleteNotificationsByMonitorId operation fails", async function() {
- req.db.getMonitorById.returns({ teamId: "123" });
- req.db.editMonitor.returns({ _id: "123" });
- req.db.deleteNotificationsByMonitorId.throws(new Error("DB error"));
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if createNotification operation fails", async function() {
- req.db.getMonitorById.returns({ teamId: "123" });
- req.db.editMonitor.returns({ _id: "123" });
- req.db.createNotification.throws(new Error("DB error"));
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if deleteJob operation fails", async function() {
- req.db.getMonitorById.returns({ teamId: "123" });
- req.db.editMonitor.returns({ _id: "123" });
- req.jobQueue.deleteJob.throws(new Error("Job error"));
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Job error");
- });
-
- it("should reject with an error if addJob operation fails", async function() {
- req.db.getMonitorById.returns({ teamId: "123" });
- req.db.editMonitor.returns({ _id: "123" });
- req.jobQueue.addJob.throws(new Error("Add Job error"));
- await editMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Add Job error");
- });
-
- it("should return success message with data if all operations succeed", async function() {
- const monitor = { _id: "123" };
- req.db.getMonitorById.returns({ teamId: "123" });
- req.db.editMonitor.returns(monitor);
- await editMonitor(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_EDIT,
- data: monitor,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - pauseMonitor", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {
- monitorId: "123",
- },
- query: {},
- body: {},
- db: {
- getMonitorById: sinon.stub(),
- },
- jobQueue: {
- deleteJob: sinon.stub(),
- addJob: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should reject with an error if param validation fails", async function() {
- req.params = {};
- await pauseMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if getMonitorById operation fails", async function() {
- req.db.getMonitorById.throws(new Error("DB error"));
- await pauseMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if deleteJob operation fails", async function() {
- const monitor = { _id: req.params.monitorId, isActive: true };
- req.db.getMonitorById.returns(monitor);
- req.jobQueue.deleteJob.throws(new Error("Delete Job error"));
- await pauseMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Delete Job error");
- });
-
- it("should reject with an error if addJob operation fails", async function() {
- const monitor = { _id: req.params.monitorId, isActive: false };
- req.db.getMonitorById.returns(monitor);
- req.jobQueue.addJob.throws(new Error("Add Job error"));
- await pauseMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Add Job error");
- });
-
- it("should reject with an error if monitor.save operation fails", async function() {
- const monitor = {
- _id: req.params.monitorId,
- active: false,
- save: sinon.stub().throws(new Error("Save error")),
- };
- req.db.getMonitorById.returns(monitor);
- await pauseMonitor(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Save error");
- });
-
- it("should return success pause message with data if all operations succeed with inactive monitor", async function() {
- const monitor = {
- _id: req.params.monitorId,
- isActive: false,
- save: sinon.stub().resolves(),
- };
- req.db.getMonitorById.returns(monitor);
- await pauseMonitor(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_PAUSE,
- data: monitor,
- })
- ).to.be.true;
- });
-
- it("should return success resume message with data if all operations succeed with active monitor", async function() {
- const monitor = {
- _id: req.params.monitorId,
- isActive: true,
- save: sinon.stub().resolves(),
- };
- req.db.getMonitorById.returns(monitor);
- await pauseMonitor(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_RESUME,
- data: monitor,
- })
- ).to.be.true;
- });
-});
-
-describe("Monitor Controller - addDemoMonitors", function() {
- let req, res, next, stub;
-
- beforeEach(function() {
- stub = sinon.stub(jwt, "verify").callsFake(() => {
- return { _id: "123", teamId: "123" };
- });
- req = {
- headers: {
- authorization: "Bearer token",
- },
- params: {},
- query: {},
- body: {},
- db: {
- addDemoMonitors: sinon.stub(),
- },
- settingsService: {
- getSettings: sinon.stub(),
- },
- jobQueue: {
- addJob: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- stub.restore();
- });
-
- it("should reject with an error if getTokenFromHeaders fails", async function() {
- req.headers = {};
- await addDemoMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("No auth headers");
- expect(next.firstCall.args[0].status).to.equal(500);
- });
-
- it("should reject with an error if getting settings fails", async function() {
- req.settingsService.getSettings.throws(new Error("Settings error"));
- await addDemoMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Settings error");
- });
-
- it("should reject with an error if JWT validation fails", async function() {
- stub.restore();
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- await addDemoMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError);
- });
-
- it("should reject with an error if addDemoMonitors operation fails", async function() {
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.addDemoMonitors.throws(new Error("DB error"));
- await addDemoMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("DB error");
- });
-
- it("should reject with an error if addJob operation fails", async function() {
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.addDemoMonitors.returns([{ _id: "123" }]);
- req.jobQueue.addJob.throws(new Error("Add Job error"));
- await addDemoMonitors(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("Add Job error");
- });
-
- it("should return success message with data if all operations succeed", async function() {
- const monitors = [{ _id: "123" }];
- req.settingsService.getSettings.returns({ jwtSecret: "my_secret" });
- req.db.addDemoMonitors.returns(monitors);
- await addDemoMonitors(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(
- res.json.calledOnceWith({
- success: true,
- msg: successMessages.MONITOR_DEMO_ADDED,
- data: monitors.length,
- })
- ).to.be.true;
- });
-});
diff --git a/Server/tests/controllers/queueController.test.js b/Server/tests/controllers/queueController.test.js
deleted file mode 100644
index b04504186..000000000
--- a/Server/tests/controllers/queueController.test.js
+++ /dev/null
@@ -1,181 +0,0 @@
-import { afterEach } from "node:test";
-import {
- getMetrics,
- getJobs,
- addJob,
- obliterateQueue,
-} from "../../controllers/queueController.js";
-import { successMessages } from "../../utils/messages.js";
-import sinon from "sinon";
-
-describe("Queue Controller - getMetrics", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {},
- body: {},
- db: {},
- jobQueue: {
- getMetrics: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should throw an error if getMetrics throws an error", async function() {
- req.jobQueue.getMetrics.throws(new Error("getMetrics error"));
- await getMetrics(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getMetrics error");
- });
-
- it("should return a success message and data if getMetrics is successful", async function() {
- const data = { data: "metrics" };
- req.jobQueue.getMetrics.returns(data);
- await getMetrics(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0]).to.deep.equal({
- success: true,
- msg: successMessages.QUEUE_GET_METRICS,
- data,
- });
- });
-});
-
-describe("Queue Controller - getJobs", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {},
- body: {},
- db: {},
- jobQueue: {
- getJobStats: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should reject with an error if getJobs throws an error", async function() {
- req.jobQueue.getJobStats.throws(new Error("getJobs error"));
- await getJobs(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getJobs error");
- });
-
- it("should return a success message and data if getJobs is successful", async function() {
- const data = { data: "jobs" };
- req.jobQueue.getJobStats.returns(data);
- await getJobs(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0]).to.deep.equal({
- success: true,
- msg: successMessages.QUEUE_GET_METRICS,
- data,
- });
- });
-});
-
-describe("Queue Controller - addJob", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {},
- body: {},
- db: {},
- jobQueue: {
- addJob: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should reject with an error if addJob throws an error", async function() {
- req.jobQueue.addJob.throws(new Error("addJob error"));
- await addJob(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("addJob error");
- });
-
- it("should return a success message if addJob is successful", async function() {
- req.jobQueue.addJob.resolves();
- await addJob(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0]).to.deep.equal({
- success: true,
- msg: successMessages.QUEUE_ADD_JOB,
- });
- });
-});
-
-describe("Queue Controller - obliterateQueue", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {},
- body: {},
- db: {},
- jobQueue: {
- obliterate: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should reject with an error if obliterateQueue throws an error", async function() {
- req.jobQueue.obliterate.throws(new Error("obliterateQueue error"));
- await obliterateQueue(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("obliterateQueue error");
- });
-
- it("should return a success message if obliterateQueue is successful", async function() {
- req.jobQueue.obliterate.resolves();
- await obliterateQueue(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0]).to.deep.equal({
- success: true,
- msg: successMessages.QUEUE_OBLITERATE,
- });
- });
-});
diff --git a/Server/tests/controllers/settingsController.test.js b/Server/tests/controllers/settingsController.test.js
deleted file mode 100644
index 665a13938..000000000
--- a/Server/tests/controllers/settingsController.test.js
+++ /dev/null
@@ -1,112 +0,0 @@
-import { afterEach } from "node:test";
-import {
- getAppSettings,
- updateAppSettings,
-} from "../../controllers/settingsController.js";
-
-import { successMessages } from "../../utils/messages.js";
-import sinon from "sinon";
-
-describe("Settings Controller - getAppSettings", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {},
- body: {},
- db: {},
- settingsService: {
- getSettings: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should throw an error if getSettings throws an error", async function() {
- req.settingsService.getSettings.throws(new Error("getSettings error"));
- await getAppSettings(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("getSettings error");
- });
-
- it("should return a success message and data if getSettings is successful", async function() {
- const data = { data: "settings" };
- req.settingsService.getSettings.returns(data);
- await getAppSettings(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0]).to.deep.equal({
- success: true,
- msg: successMessages.GET_APP_SETTINGS,
- data,
- });
- });
-});
-
-describe("Settings Controller - updateAppSettings", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- headers: {},
- params: {},
- body: {},
- db: {
- updateAppSettings: sinon.stub(),
- },
- settingsService: {
- reloadSettings: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should reject with an error if body validation fails", async function() {
- req.body = { invalid: 1 };
- await updateAppSettings(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].status).to.equal(422);
- });
-
- it("should reject with an error if updateAppSettings throws an error", async function() {
- req.db.updateAppSettings.throws(new Error("updateAppSettings error"));
- await updateAppSettings(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("updateAppSettings error");
- });
-
- it("should reject with an error if reloadSettings throws an error", async function() {
- req.settingsService.reloadSettings.throws(new Error("reloadSettings error"));
- await updateAppSettings(req, res, next);
- expect(next.firstCall.args[0]).to.be.an("error");
- expect(next.firstCall.args[0].message).to.equal("reloadSettings error");
- });
-
- it("should return a success message and data if updateAppSettings is successful", async function() {
- const data = { data: "settings" };
- req.settingsService.reloadSettings.returns(data);
- await updateAppSettings(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0]).to.deep.equal({
- success: true,
- msg: successMessages.UPDATE_APP_SETTINGS,
- data,
- });
- });
-});
diff --git a/Server/tests/controllers/statusPageController.test.js b/Server/tests/controllers/statusPageController.test.js
deleted file mode 100644
index e1371194d..000000000
--- a/Server/tests/controllers/statusPageController.test.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import sinon from "sinon";
-import {
- createStatusPage,
- getStatusPageByUrl,
-} from "../../controllers/statusPageController.js";
-
-describe("statusPageController", function() {
- let req, res, next;
-
- beforeEach(function() {
- req = {
- params: {},
- body: {},
- db: {
- createStatusPage: sinon.stub(),
- getStatusPageByUrl: sinon.stub(),
- },
- };
- res = {
- status: sinon.stub().returnsThis(),
- json: sinon.stub(),
- };
- next = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createStatusPage", function() {
- beforeEach(function() {
- req.body = {
- companyName: "Test Company",
- url: "123456",
- timezone: "America/Toronto",
- color: "#000000",
- theme: "light",
- monitors: ["67309ca673788e808884c8ac", "67309ca673788e808884c8ac"],
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should handle a validation error", async function() {
- req.body = {
- // Invalid data that will trigger validation error
- companyName: "",
- url: "",
- timezone: "",
- color: "invalid",
- theme: "invalid",
- monitors: ["invalid-id"],
- };
- try {
- await createStatusPage(req, res, next);
- } catch (error) {
- expect(error).to.be.an.instanceOf(Error);
- expect(error.message).to.equal("Validation error");
- }
- });
-
- it("should handle a db error", async function() {
- const err = new Error("DB error");
- req.db.createStatusPage.throws(err);
-
- try {
- await createStatusPage(req, res, next);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should insert a properly formatted status page", async function() {
- const result = await createStatusPage(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0].success).to.be.true;
- });
- });
-
- describe("getStatusPageByUrl", function() {
- beforeEach(function() {
- req.params = {
- url: "123456",
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should handle a validation error", async function() {
- req.params = {
- url: "",
- };
-
- try {
- await getStatusPageByUrl(req, res, next);
- } catch (error) {
- expect(error).to.be.an.instanceOf(Error);
- expect(error.message).to.equal("Validation error");
- }
- });
-
- it("should handle a DB error", async function() {
- const err = new Error("DB error");
- req.db.getStatusPageByUrl.throws(err);
-
- try {
- await getStatusPageByUrl(req, res, next);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should return a status page", async function() {
- const statusPage = {
- _id: "123456",
- companyName: "Test Company",
- url: "123456",
- };
- req.db.getStatusPageByUrl.resolves(statusPage);
- const result = await getStatusPageByUrl(req, res, next);
- expect(res.status.firstCall.args[0]).to.equal(200);
- expect(res.json.firstCall.args[0].success).to.be.true;
- expect(res.json.firstCall.args[0].data).to.deep.equal(statusPage);
- });
- });
-});
diff --git a/Server/tests/db/checkModule.test.js b/Server/tests/db/checkModule.test.js
deleted file mode 100644
index 986fa1457..000000000
--- a/Server/tests/db/checkModule.test.js
+++ /dev/null
@@ -1,593 +0,0 @@
-import sinon from "sinon";
-import {
- createCheck,
- getChecksCount,
- getChecks,
- getTeamChecks,
- deleteChecks,
- deleteChecksByTeamId,
- updateChecksTTL,
-} from "../../db/mongo/modules/checkModule.js";
-import Check from "../../db/models/Check.js";
-import Monitor from "../../db/models/Monitor.js";
-import User from "../../db/models/User.js";
-import logger from "../../utils/logger.js";
-
-describe("checkModule", function() {
- describe("createCheck", function() {
- let checkCountDocumentsStub, checkSaveStub, monitorFindByIdStub, monitorSaveStub;
- const mockMonitor = {
- _id: "123",
- uptimePercentage: 0.5,
- status: true,
- save: () => this,
- };
- const mockCheck = { active: true };
-
- beforeEach(function() {
- checkSaveStub = sinon.stub(Check.prototype, "save");
- checkCountDocumentsStub = sinon.stub(Check, "countDocuments");
- monitorFindByIdStub = sinon.stub(Monitor, "findById");
- monitorSaveStub = sinon.stub(Monitor.prototype, "save");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return undefined early if no monitor is found", async function() {
- monitorFindByIdStub.returns(null);
- const check = await createCheck({ monitorId: "123" });
- expect(check).to.be.undefined;
- });
-
- it("should return a check", async function() {
- monitorFindByIdStub.returns(mockMonitor);
- checkSaveStub.returns(mockCheck);
- monitorSaveStub.returns(mockMonitor);
- const check = await createCheck({ monitorId: "123", status: true });
- expect(check).to.deep.equal(mockCheck);
- });
-
- it("should return a check if status is down", async function() {
- mockMonitor.status = false;
- monitorFindByIdStub.returns(mockMonitor);
- checkSaveStub.returns(mockCheck);
- monitorSaveStub.returns(mockMonitor);
- const check = await createCheck({ monitorId: "123", status: false });
- expect(check).to.deep.equal(mockCheck);
- });
-
- it("should return a check if uptimePercentage is undefined", async function() {
- mockMonitor.uptimePercentage = undefined;
- monitorFindByIdStub.returns(mockMonitor);
- checkSaveStub.returns(mockCheck);
- monitorSaveStub.returns(mockMonitor);
- const check = await createCheck({ monitorId: "123", status: true });
- expect(check).to.deep.equal(mockCheck);
- });
-
- it("should return a check if uptimePercentage is undefined and status is down", async function() {
- mockMonitor.uptimePercentage = undefined;
- monitorFindByIdStub.returns(mockMonitor);
- checkSaveStub.returns(mockCheck);
- monitorSaveStub.returns(mockMonitor);
- const check = await createCheck({ monitorId: "123", status: false });
- expect(check).to.deep.equal(mockCheck);
- });
-
- it("should monitor save error", async function() {
- const err = new Error("Save Error");
- monitorSaveStub.throws(err);
- try {
- await createCheck({ monitorId: "123" });
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should handle errors", async function() {
- const err = new Error("DB Error");
- checkCountDocumentsStub.throws(err);
- try {
- await createCheck({ monitorId: "123" });
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getChecksCount", function() {
- let checkCountDocumentStub;
-
- beforeEach(function() {
- checkCountDocumentStub = sinon.stub(Check, "countDocuments");
- });
-
- afterEach(function() {
- checkCountDocumentStub.restore();
- });
-
- it("should return count with basic monitorId query", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: {},
- };
- checkCountDocumentStub.resolves(5);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(5);
- expect(checkCountDocumentStub.calledOnce).to.be.true;
- expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({
- monitorId: "test123",
- });
- });
-
- it("should include dateRange in query when provided", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { dateRange: "day" },
- };
- checkCountDocumentStub.resolves(3);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(3);
- expect(checkCountDocumentStub.firstCall.args[0]).to.have.property("createdAt");
- expect(checkCountDocumentStub.firstCall.args[0].createdAt).to.have.property("$gte");
- });
-
- it('should handle "all" filter correctly', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "all" },
- };
- checkCountDocumentStub.resolves(2);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(2);
- expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({
- monitorId: "test123",
- status: false,
- });
- });
-
- it('should handle "down" filter correctly', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "down" },
- };
- checkCountDocumentStub.resolves(2);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(2);
- expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({
- monitorId: "test123",
- status: false,
- });
- });
-
- it('should handle "resolve" filter correctly', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "resolve" },
- };
- checkCountDocumentStub.resolves(1);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(1);
- expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({
- monitorId: "test123",
- status: false,
- statusCode: 5000,
- });
- });
-
- it("should handle unknown filter correctly", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "unknown" },
- };
- checkCountDocumentStub.resolves(1);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(1);
- expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({
- monitorId: "test123",
- status: false,
- });
- });
-
- it("should combine dateRange and filter in query", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: {
- dateRange: "week",
- filter: "down",
- },
- };
- checkCountDocumentStub.resolves(4);
-
- const result = await getChecksCount(req);
-
- expect(result).to.equal(4);
- expect(checkCountDocumentStub.firstCall.args[0]).to.have.all.keys(
- "monitorId",
- "createdAt",
- "status"
- );
- });
- });
-
- describe("getChecks", function() {
- let checkFindStub, monitorFindStub;
-
- beforeEach(function() {
- checkFindStub = sinon.stub(Check, "find").returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns([{ id: 1 }, { id: 2 }]),
- }),
- }),
- });
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return checks with basic monitorId query", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: {},
- };
-
- const result = await getChecks(req);
-
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it("should return checks with limit query", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { limit: 10 },
- };
-
- const result = await getChecks(req);
-
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it("should handle pagination correctly", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: {
- page: 2,
- rowsPerPage: 10,
- },
- };
-
- const result = await getChecks(req);
-
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it("should handle dateRange filter", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { dateRange: "week" },
- };
- const result = await getChecks(req);
-
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it('should handle "all" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "all" },
- };
-
- await getChecks(req);
- const result = await getChecks(req);
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it('should handle "down" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "down" },
- };
-
- await getChecks(req);
- const result = await getChecks(req);
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it('should handle "resolve" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "resolve" },
- };
-
- await getChecks(req);
- const result = await getChecks(req);
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it('should handle "unknown" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "unknown" },
- };
-
- await getChecks(req);
- const result = await getChecks(req);
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it("should handle ascending sort order", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { sortOrder: "asc" },
- };
-
- await getChecks(req);
- const result = await getChecks(req);
- expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]);
- });
-
- it("should handle error case", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: {},
- };
-
- checkFindStub.throws(new Error("Database error"));
-
- try {
- await getChecks(req);
- } catch (error) {
- expect(error.message).to.equal("Database error");
- expect(error.service).to.equal("checkModule");
- expect(error.method).to.equal("getChecks");
- }
- });
- });
-
- describe("getTeamChecks", function() {
- let checkFindStub, checkCountDocumentsStub, monitorFindStub;
- const mockMonitors = [{ _id: "123" }];
-
- beforeEach(function() {
- monitorFindStub = sinon.stub(Monitor, "find").returns({
- select: sinon.stub().returns(mockMonitors),
- });
- checkCountDocumentsStub = sinon.stub(Check, "countDocuments").returns(2);
- checkFindStub = sinon.stub(Check, "find").returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns({
- select: sinon.stub().returns([{ id: 1 }, { id: 2 }]),
- }),
- }),
- }),
- });
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return checks with basic monitorId query", async function() {
- const req = {
- params: { teamId: "test123" },
- query: {},
- };
-
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it("should handle pagination correctly", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { limit: 1, page: 2, rowsPerPage: 10 },
- };
-
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it("should handle dateRange filter", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { dateRange: "week" },
- };
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it('should handle "all" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "all" },
- };
-
- await getChecks(req);
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it('should handle "down" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "down" },
- };
-
- await getChecks(req);
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it('should handle "resolve" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "resolve" },
- };
-
- await getChecks(req);
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it('should handle "unknown" filter', async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { filter: "unknown" },
- };
-
- await getChecks(req);
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it("should handle ascending sort order", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: { sortOrder: "asc" },
- };
-
- await getChecks(req);
- const result = await getTeamChecks(req);
- expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] });
- });
-
- it("should handle error case", async function() {
- const req = {
- params: { monitorId: "test123" },
- query: {},
- };
-
- checkFindStub.throws(new Error("Database error"));
-
- try {
- await getTeamChecks(req);
- } catch (error) {
- expect(error.message).to.equal("Database error");
- expect(error.service).to.equal("checkModule");
- expect(error.method).to.equal("getTeamChecks");
- }
- });
- });
-
- describe("deleteChecks", function() {
- let checkDeleteManyStub;
-
- beforeEach(function() {
- checkDeleteManyStub = sinon.stub(Check, "deleteMany").resolves({ deletedCount: 1 });
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return a value if a check is deleted", async function() {
- const result = await deleteChecks("123");
- expect(result).to.equal(1);
- });
-
- it("should handle an error", async function() {
- checkDeleteManyStub.throws(new Error("Database error"));
- try {
- await deleteChecks("123");
- } catch (error) {
- expect(error.message).to.equal("Database error");
- expect(error.method).to.equal("deleteChecks");
- }
- });
- });
-
- describe("deleteChecksByTeamId", function() {
- let mockMonitors = [{ _id: 123, save: () => this }];
- let monitorFindStub, monitorSaveStub, checkDeleteManyStub;
-
- beforeEach(function() {
- monitorSaveStub = sinon.stub(Monitor.prototype, "save");
- monitorFindStub = sinon.stub(Monitor, "find").returns(mockMonitors);
- checkDeleteManyStub = sinon.stub(Check, "deleteMany").resolves({ deletedCount: 1 });
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return a deleted count", async function() {
- const result = await deleteChecksByTeamId("123");
- expect(result).to.equal(1);
- });
-
- it("should handle errors", async function() {
- const err = new Error("DB Error");
- monitorFindStub.throws(err);
- try {
- const result = await deleteChecksByTeamId("123");
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("updateChecksTTL", function() {
- let userUpdateManyStub;
- let loggerStub;
-
- beforeEach(function() {
- loggerStub = sinon.stub(logger, "error");
- userUpdateManyStub = sinon.stub(User, "updateMany");
- Check.collection = { dropIndex: sinon.stub(), createIndex: sinon.stub() };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return undefined", async function() {
- const result = await updateChecksTTL("123", 10);
- expect(result).to.be.undefined;
- });
-
- it("should log an error if dropIndex throws an error", async function() {
- const err = new Error("Drop Index Error");
- Check.collection.dropIndex.throws(err);
- await updateChecksTTL("123", 10);
- expect(loggerStub.calledOnce).to.be.true;
- expect(loggerStub.firstCall.args[0].message).to.equal(err.message);
- });
-
- it("should throw an error if createIndex throws an error", async function() {
- const err = new Error("Create Index Error");
- Check.collection.createIndex.throws(err);
- try {
- await updateChecksTTL("123", 10);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should throw an error if User.updateMany throws an error", async function() {
- const err = new Error("Update Many Error");
- userUpdateManyStub.throws(err);
- try {
- await updateChecksTTL("123", 10);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-});
diff --git a/Server/tests/db/hardwareCheckModule.test.js b/Server/tests/db/hardwareCheckModule.test.js
deleted file mode 100644
index c0a642b6b..000000000
--- a/Server/tests/db/hardwareCheckModule.test.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import sinon from "sinon";
-import HardwareCheck from "../../db/models/HardwareCheck.js";
-import { createHardwareCheck } from "../../db/mongo/modules/hardwareCheckModule.js";
-import Monitor from "../../db/models/Monitor.js";
-import logger from "../../utils/logger.js";
-
-const mockHardwareCheck = {
- data: {
- cpu: {
- physical_core: 4,
- logical_core: 8,
- frequency: 4800,
- current_frequency: 1411,
- temperature: [45, 50, 46, 47, 45, 50, 46, 47],
- free_percent: 0.8552990910595134,
- usage_percent: 0.14470090894048657,
- },
- memory: {
- total_bytes: 16467628032,
- available_bytes: 7895044096,
- used_bytes: 6599561216,
- usage_percent: 0.4008,
- },
- disk: [
- {
- read_speed_bytes: null,
- write_speed_bytes: null,
- total_bytes: 931258499072,
- free_bytes: 737097256960,
- usage_percent: 0.1661,
- },
- ],
- host: {
- os: "linux",
- platform: "ubuntu",
- kernel_version: "6.8.0-48-generic",
- },
- },
- errors: [
- {
- metric: ["cpu.temperature"],
- err: "unable to read CPU temperature",
- },
- ],
-};
-
-const mockMonitor = {
- _id: "123",
- uptimePercentage: 1,
- status: true,
- save: () => this,
-};
-
-describe("HardwareCheckModule", function() {
- let hardwareCheckSaveStub,
- hardwareCheckCountDocumentsStub,
- monitorFindByIdStub,
- loggerStub;
-
- beforeEach(function() {
- loggerStub = sinon.stub(logger, "error");
- hardwareCheckSaveStub = sinon.stub(HardwareCheck.prototype, "save");
- monitorFindByIdStub = sinon.stub(Monitor, "findById");
- hardwareCheckCountDocumentsStub = sinon.stub(HardwareCheck, "countDocuments");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createHardwareCheck", function() {
- it("should return a hardware check", async function() {
- hardwareCheckSaveStub.resolves(mockHardwareCheck);
- monitorFindByIdStub.resolves(mockMonitor);
- hardwareCheckCountDocumentsStub.resolves(1);
- const hardwareCheck = await createHardwareCheck({ status: true });
- expect(hardwareCheck).to.exist;
- expect(hardwareCheck).to.deep.equal(mockHardwareCheck);
- });
-
- it("should return a hardware check for a check with status false", async function() {
- hardwareCheckSaveStub.resolves(mockHardwareCheck);
- monitorFindByIdStub.resolves(mockMonitor);
- hardwareCheckCountDocumentsStub.resolves(1);
- const hardwareCheck = await createHardwareCheck({ status: false });
- expect(hardwareCheck).to.exist;
- expect(hardwareCheck).to.deep.equal(mockHardwareCheck);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- monitorFindByIdStub.resolves(mockMonitor);
- hardwareCheckSaveStub.rejects(err);
- try {
- await createHardwareCheck({});
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should log an error if a monitor is not found", async function() {
- monitorFindByIdStub.resolves(null);
- const res = await createHardwareCheck({});
- expect(loggerStub.calledOnce).to.be.true;
- expect(res).to.be.null;
- });
-
- it("should handle a monitor with undefined uptimePercentage", async function() {
- monitorFindByIdStub.resolves({ ...mockMonitor, uptimePercentage: undefined });
- hardwareCheckSaveStub.resolves(mockHardwareCheck);
- hardwareCheckCountDocumentsStub.resolves(1);
- const res = await createHardwareCheck({});
- expect(res).to.exist;
- });
-
- it("should handle a monitor with undefined uptimePercentage and true status", async function() {
- monitorFindByIdStub.resolves({
- ...mockMonitor,
- uptimePercentage: undefined,
- });
- hardwareCheckSaveStub.resolves(mockHardwareCheck);
- hardwareCheckCountDocumentsStub.resolves(1);
- const res = await createHardwareCheck({ status: true });
- expect(res).to.exist;
- });
-
- it("should handle a monitor with undefined uptimePercentage and false status", async function() {
- monitorFindByIdStub.resolves({
- ...mockMonitor,
- uptimePercentage: undefined,
- });
- hardwareCheckSaveStub.resolves(mockHardwareCheck);
- hardwareCheckCountDocumentsStub.resolves(1);
- const res = await createHardwareCheck({ status: false });
- expect(res).to.exist;
- });
- });
-});
diff --git a/Server/tests/db/inviteModule.test.js b/Server/tests/db/inviteModule.test.js
deleted file mode 100644
index 164840826..000000000
--- a/Server/tests/db/inviteModule.test.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import sinon from "sinon";
-import InviteToken from "../../db/models/InviteToken.js";
-import {
- requestInviteToken,
- getInviteToken,
- getInviteTokenAndDelete,
-} from "../../db/mongo/modules/inviteModule.js";
-import { errorMessages } from "../../utils/messages.js";
-
-describe("Invite Module", function() {
- const mockUserData = {
- email: "test@test.com",
- teamId: "123",
- role: ["admin"],
- token: "123",
- };
- const mockInviteToken = { _id: 123, time: 123 };
- let inviteTokenDeleteManyStub,
- inviteTokenSaveStub,
- inviteTokenFindOneStub,
- inviteTokenFindOneAndDeleteStub;
-
- beforeEach(function() {
- inviteTokenDeleteManyStub = sinon.stub(InviteToken, "deleteMany");
- inviteTokenSaveStub = sinon.stub(InviteToken.prototype, "save");
- inviteTokenFindOneStub = sinon.stub(InviteToken, "findOne");
- inviteTokenFindOneAndDeleteStub = sinon.stub(InviteToken, "findOneAndDelete");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("requestInviteToken", function() {
- it("should return a new invite token", async function() {
- inviteTokenDeleteManyStub.resolves();
- inviteTokenSaveStub.resolves();
- const inviteToken = await requestInviteToken(mockUserData);
- expect(inviteToken.email).to.equal(mockUserData.email);
- expect(inviteToken.role).to.deep.equal(mockUserData.role);
- expect(inviteToken.token).to.exist;
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- inviteTokenDeleteManyStub.rejects(err);
- try {
- await requestInviteToken(mockUserData);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getInviteToken", function() {
- it("should return an invite token", async function() {
- inviteTokenFindOneStub.resolves(mockInviteToken);
- const inviteToken = await getInviteToken(mockUserData.token);
- expect(inviteToken).to.deep.equal(mockInviteToken);
- });
-
- it("should handle a token not found", async function() {
- inviteTokenFindOneStub.resolves(null);
- try {
- await getInviteToken(mockUserData.token);
- } catch (error) {
- expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND);
- }
- });
-
- it("should handle DB errors", async function() {
- const err = new Error("test error");
- inviteTokenFindOneStub.rejects(err);
- try {
- await getInviteToken(mockUserData.token);
- } catch (error) {
- expect(error).to.deep.equal(err);
- expect(error.method).to.equal("getInviteToken");
- }
- });
- });
-
- describe("getInviteTokenAndDelete", function() {
- it("should return a deleted invite", async function() {
- inviteTokenFindOneAndDeleteStub.resolves(mockInviteToken);
- const deletedInvite = await getInviteTokenAndDelete(mockUserData.token);
- expect(deletedInvite).to.deep.equal(mockInviteToken);
- });
-
- it("should handle a token not found", async function() {
- inviteTokenFindOneAndDeleteStub.resolves(null);
- try {
- await getInviteTokenAndDelete(mockUserData.token);
- } catch (error) {
- expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND);
- }
- });
-
- it("should handle DB errors", async function() {
- const err = new Error("test error");
- inviteTokenFindOneAndDeleteStub.rejects(err);
- try {
- await getInviteTokenAndDelete(mockUserData.token);
- } catch (error) {
- expect(error).to.deep.equal(err);
- expect(error.method).to.equal("getInviteTokenAndDelete");
- }
- });
- });
-});
diff --git a/Server/tests/db/maintenanceWindowModule.test.js b/Server/tests/db/maintenanceWindowModule.test.js
deleted file mode 100644
index d0abc7fa7..000000000
--- a/Server/tests/db/maintenanceWindowModule.test.js
+++ /dev/null
@@ -1,283 +0,0 @@
-import sinon from "sinon";
-import MaintenanceWindow from "../../db/models/MaintenanceWindow.js";
-import {
- createMaintenanceWindow,
- getMaintenanceWindowById,
- getMaintenanceWindowsByTeamId,
- getMaintenanceWindowsByMonitorId,
- deleteMaintenanceWindowById,
- deleteMaintenanceWindowByMonitorId,
- deleteMaintenanceWindowByUserId,
- editMaintenanceWindowById,
-} from "../../db/mongo/modules/maintenanceWindowModule.js";
-
-describe("MaintenanceWindow Module", function() {
- const mockMaintenanceWindow = {
- monitorId: "123",
- active: true,
- oneTime: true,
- start: 1,
- end: 20000,
- };
-
- let mockMaintenanceWindows = [mockMaintenanceWindow];
- let maintenanceWindowSaveStub,
- maintenanceWindowFindByIdStub,
- maintenanceWindowCountDocumentsStub,
- maintenanceWindowFindStub,
- maintenanceWindowFindByIdAndDeleteStub,
- maintenanceWindowDeleteManyStub,
- maintenanceWindowFindByIdAndUpdateStub;
-
- beforeEach(function() {
- maintenanceWindowSaveStub = sinon.stub(MaintenanceWindow.prototype, "save");
- maintenanceWindowFindByIdStub = sinon.stub(MaintenanceWindow, "findById");
- maintenanceWindowCountDocumentsStub = sinon.stub(MaintenanceWindow, "countDocuments");
- maintenanceWindowFindStub = sinon.stub(MaintenanceWindow, "find").returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns(mockMaintenanceWindows),
- }),
- }),
- });
- maintenanceWindowFindByIdAndDeleteStub = sinon.stub(
- MaintenanceWindow,
- "findByIdAndDelete"
- );
- maintenanceWindowDeleteManyStub = sinon.stub(MaintenanceWindow, "deleteMany");
- maintenanceWindowFindByIdAndUpdateStub = sinon.stub(
- MaintenanceWindow,
- "findByIdAndUpdate"
- );
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createMaintenanceWindow", function() {
- it("should save a new maintenance window", async function() {
- maintenanceWindowSaveStub.resolves(mockMaintenanceWindow);
- const result = await createMaintenanceWindow(mockMaintenanceWindow);
- expect(result).to.deep.equal(mockMaintenanceWindow);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowSaveStub.rejects(err);
- try {
- await createMaintenanceWindow(mockMaintenanceWindow);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getMaintenanceWindowById", function() {
- it("should return a maintenance window", async function() {
- maintenanceWindowFindByIdStub.resolves(mockMaintenanceWindow);
- const result = await getMaintenanceWindowById(mockMaintenanceWindow.id);
- expect(result).to.deep.equal(mockMaintenanceWindow);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowFindByIdStub.rejects(err);
- try {
- await getMaintenanceWindowById(mockMaintenanceWindow.id);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getMaintenanceWindowsByTeamId", function() {
- let query;
-
- beforeEach(function() {
- query = {
- active: true,
- page: 1,
- rowsPerPage: 10,
- field: "name",
- order: "asc",
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return a list of maintenance windows and count", async function() {
- maintenanceWindowCountDocumentsStub.resolves(1);
- const result = await getMaintenanceWindowsByTeamId(
- mockMaintenanceWindow.teamId,
- query
- );
- expect(result).to.deep.equal({
- maintenanceWindows: mockMaintenanceWindows,
- maintenanceWindowCount: 1,
- });
- });
-
- it("should return a list of maintenance windows and count with empty query", async function() {
- query = undefined;
- maintenanceWindowCountDocumentsStub.resolves(1);
- const result = await getMaintenanceWindowsByTeamId(
- mockMaintenanceWindow.teamId,
- query
- );
- expect(result).to.deep.equal({
- maintenanceWindows: mockMaintenanceWindows,
- maintenanceWindowCount: 1,
- });
- });
-
- it("should return a list of maintenance windows and count with no pagination provided", async function() {
- query.page = undefined;
- query.rowsPerPage = undefined;
- maintenanceWindowCountDocumentsStub.resolves(1);
- const result = await getMaintenanceWindowsByTeamId(
- mockMaintenanceWindow.teamId,
- query
- );
- expect(result).to.deep.equal({
- maintenanceWindows: mockMaintenanceWindows,
- maintenanceWindowCount: 1,
- });
- });
-
- it("should return a list of maintenance windows and count with field and desc order", async function() {
- query.order = "desc";
- maintenanceWindowCountDocumentsStub.resolves(1);
- const result = await getMaintenanceWindowsByTeamId(
- mockMaintenanceWindow.teamId,
- query
- );
- expect(result).to.deep.equal({
- maintenanceWindows: mockMaintenanceWindows,
- maintenanceWindowCount: 1,
- });
- });
-
- it("should return a list of maintenance windows and count no field", async function() {
- query.field = undefined;
- maintenanceWindowCountDocumentsStub.resolves(1);
- const result = await getMaintenanceWindowsByTeamId(
- mockMaintenanceWindow.teamId,
- query
- );
- expect(result).to.deep.equal({
- maintenanceWindows: mockMaintenanceWindows,
- maintenanceWindowCount: 1,
- });
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowCountDocumentsStub.rejects(err);
- try {
- await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getMaintenanceWindowsByMonitorId", function() {
- it("should return a list of maintenance windows", async function() {
- maintenanceWindowFindStub.resolves(mockMaintenanceWindows);
- const result = await getMaintenanceWindowsByMonitorId(
- mockMaintenanceWindow.monitorId
- );
- expect(result).to.deep.equal(mockMaintenanceWindows);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowFindStub.rejects(err);
- try {
- await getMaintenanceWindowsByMonitorId(mockMaintenanceWindow.monitorId);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteMaintenanceWindowById", function() {
- it("should delete a maintenance window", async function() {
- maintenanceWindowFindByIdAndDeleteStub.resolves(mockMaintenanceWindow);
- const result = await deleteMaintenanceWindowById(mockMaintenanceWindow.id);
- expect(result).to.deep.equal(mockMaintenanceWindow);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowFindByIdAndDeleteStub.rejects(err);
- try {
- await deleteMaintenanceWindowById(mockMaintenanceWindow.id);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteMaintenanceWindowByMonitorId", function() {
- it("should return the number of documents deleted", async function() {
- maintenanceWindowDeleteManyStub.resolves({ deletedCount: 1 });
- const result = await deleteMaintenanceWindowByMonitorId(
- mockMaintenanceWindow.monitorId
- );
- expect(result).to.deep.equal({ deletedCount: 1 });
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowDeleteManyStub.rejects(err);
- try {
- await deleteMaintenanceWindowByMonitorId(mockMaintenanceWindow.monitorId);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteMaintenanceWindowByUserId", function() {
- it("should return the number of documents deleted", async function() {
- maintenanceWindowDeleteManyStub.resolves({ deletedCount: 1 });
- const result = await deleteMaintenanceWindowByUserId(mockMaintenanceWindow.userId);
- expect(result).to.deep.equal({ deletedCount: 1 });
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowDeleteManyStub.rejects(err);
- try {
- await deleteMaintenanceWindowByUserId(mockMaintenanceWindow.userId);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("editMaintenanceWindowById", function() {
- it("should return the updated maintenance window", async function() {
- maintenanceWindowFindByIdAndUpdateStub.resolves(mockMaintenanceWindow);
- const result = await editMaintenanceWindowById(
- mockMaintenanceWindow.id,
- mockMaintenanceWindow
- );
- expect(result).to.deep.equal(mockMaintenanceWindow);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- maintenanceWindowFindByIdAndUpdateStub.rejects(err);
- try {
- await editMaintenanceWindowById(mockMaintenanceWindow.id, mockMaintenanceWindow);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-});
diff --git a/Server/tests/db/monitorModule.test.js b/Server/tests/db/monitorModule.test.js
deleted file mode 100644
index 50782494a..000000000
--- a/Server/tests/db/monitorModule.test.js
+++ /dev/null
@@ -1,1931 +0,0 @@
-import sinon from "sinon";
-import Monitor from "../../db/models/Monitor.js";
-import Check from "../../db/models/Check.js";
-import PageSpeedCheck from "../../db/models/PageSpeedCheck.js";
-import HardwareCheck from "../../db/models/HardwareCheck.js";
-import Notification from "../../db/models/Notification.js";
-
-import { errorMessages } from "../../utils/messages.js";
-import {
- getAllMonitors,
- getAllMonitorsWithUptimeStats,
- getMonitorStatsById,
- getMonitorById,
- getMonitorsAndSummaryByTeamId,
- getMonitorsByTeamId,
- createMonitor,
- deleteMonitor,
- deleteAllMonitors,
- deleteMonitorsByUserId,
- editMonitor,
- addDemoMonitors,
- calculateUptimeDuration,
- getLastChecked,
- getLatestResponseTime,
- getAverageResponseTime,
- getUptimePercentage,
- getIncidents,
- getMonitorChecks,
- processChecksForDisplay,
- groupChecksByTime,
- calculateGroupStats,
-} from "../../db/mongo/modules/monitorModule.js";
-
-describe("monitorModule", function() {
- let monitorFindStub,
- monitorFindByIdStub,
- monitorFindByIdAndUpdateStub,
- monitorFindByIdAndDeleteStub,
- monitorDeleteManyStub,
- monitorCountStub,
- monitorInsertManyStub,
- checkFindStub,
- pageSpeedCheckFindStub,
- hardwareCheckFindStub;
-
- beforeEach(function() {
- monitorFindStub = sinon.stub(Monitor, "find");
- monitorFindByIdStub = sinon.stub(Monitor, "findById");
- monitorFindByIdAndUpdateStub = sinon.stub(Monitor, "findByIdAndUpdate");
- monitorFindByIdAndDeleteStub = sinon.stub(Monitor, "findByIdAndDelete");
- monitorDeleteManyStub = sinon.stub(Monitor, "deleteMany");
- monitorCountStub = sinon.stub(Monitor, "countDocuments");
-
- monitorInsertManyStub = sinon.stub(Monitor, "insertMany");
- checkFindStub = sinon.stub(Check, "find").returns({
- sort: sinon.stub(),
- });
- pageSpeedCheckFindStub = sinon.stub(PageSpeedCheck, "find").returns({
- sort: sinon.stub(),
- });
- hardwareCheckFindStub = sinon.stub(HardwareCheck, "find").returns({
- sort: sinon.stub(),
- });
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("getAllMonitors", function() {
- it("should return all monitors", async function() {
- const mockMonitors = [
- { _id: "1", name: "Monitor 1", url: "test1.com" },
- { _id: "2", name: "Monitor 2", url: "test2.com" },
- ];
- monitorFindStub.returns(mockMonitors);
- const result = await getAllMonitors();
-
- expect(result).to.deep.equal(mockMonitors);
- expect(monitorFindStub.calledOnce).to.be.true;
- expect(monitorFindStub.firstCall.args).to.deep.equal([]);
- });
-
- it("should handle empty results", async function() {
- monitorFindStub.returns([]);
- const result = await getAllMonitors();
- expect(result).to.be.an("array").that.is.empty;
- });
-
- it("should throw error when database fails", async function() {
- // Arrange
- const error = new Error("Database error");
- error.service = "MonitorModule";
- error.method = "getAllMonitors";
- monitorFindStub.rejects(error);
- // Act & Assert
- try {
- await getAllMonitors();
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err).to.equal(error);
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("getAllMonitors");
- }
- });
- });
-
- describe("getAllMonitorsWithUptimeStats", function() {
- it("should return monitors with uptime stats for different time periods", async function() {
- // Mock data
- const mockMonitors = [
- {
- _id: "monitor1",
- type: "http",
- toObject: () => ({
- _id: "monitor1",
- type: "http",
- name: "Test Monitor",
- }),
- },
- ];
-
- const mockChecks = [
- { status: true },
- { status: true },
- { status: false },
- { status: true },
- ];
-
- monitorFindStub.resolves(mockMonitors);
- checkFindStub.resolves(mockChecks);
-
- const result = await getAllMonitorsWithUptimeStats();
-
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(1);
-
- const monitor = result[0];
- expect(monitor).to.have.property("_id", "monitor1");
- expect(monitor).to.have.property("name", "Test Monitor");
-
- // Check uptime percentages exist for all time periods
- expect(monitor).to.have.property("1");
- expect(monitor).to.have.property("7");
- expect(monitor).to.have.property("30");
- expect(monitor).to.have.property("90");
-
- // Verify uptime percentage calculation (3 successful out of 4 = 75%)
- expect(monitor["1"]).to.equal(75);
- expect(monitor["7"]).to.equal(75);
- expect(monitor["30"]).to.equal(75);
- expect(monitor["90"]).to.equal(75);
- });
-
- it("should return monitors with stats for pagespeed type", async function() {
- // Mock data
- const mockMonitors = [
- {
- _id: "monitor1",
- type: "pagespeed",
- toObject: () => ({
- _id: "monitor1",
- type: "pagespeed",
- name: "Test Monitor",
- }),
- },
- ];
-
- const mockChecks = [
- { status: true },
- { status: true },
- { status: false },
- { status: true },
- ];
-
- monitorFindStub.resolves(mockMonitors);
- pageSpeedCheckFindStub.resolves(mockChecks);
-
- const result = await getAllMonitorsWithUptimeStats();
-
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(1);
-
- const monitor = result[0];
- expect(monitor).to.have.property("_id", "monitor1");
- expect(monitor).to.have.property("name", "Test Monitor");
-
- // Check uptime percentages exist for all time periods
- expect(monitor).to.have.property("1");
- expect(monitor).to.have.property("7");
- expect(monitor).to.have.property("30");
- expect(monitor).to.have.property("90");
-
- // Verify uptime percentage calculation (3 successful out of 4 = 75%)
- expect(monitor["1"]).to.equal(75);
- expect(monitor["7"]).to.equal(75);
- expect(monitor["30"]).to.equal(75);
- expect(monitor["90"]).to.equal(75);
- });
-
- it("should return monitors with stats for hardware type", async function() {
- // Mock data
- const mockMonitors = [
- {
- _id: "monitor1",
- type: "hardware",
- toObject: () => ({
- _id: "monitor1",
- type: "hardware",
- name: "Test Monitor",
- }),
- },
- ];
-
- const mockChecks = [
- { status: true },
- { status: true },
- { status: false },
- { status: true },
- ];
-
- monitorFindStub.resolves(mockMonitors);
- hardwareCheckFindStub.resolves(mockChecks);
-
- const result = await getAllMonitorsWithUptimeStats();
-
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(1);
-
- const monitor = result[0];
- expect(monitor).to.have.property("_id", "monitor1");
- expect(monitor).to.have.property("name", "Test Monitor");
-
- // Check uptime percentages exist for all time periods
- expect(monitor).to.have.property("1");
- expect(monitor).to.have.property("7");
- expect(monitor).to.have.property("30");
- expect(monitor).to.have.property("90");
-
- // Verify uptime percentage calculation (3 successful out of 4 = 75%)
- expect(monitor["1"]).to.equal(75);
- expect(monitor["7"]).to.equal(75);
- expect(monitor["30"]).to.equal(75);
- expect(monitor["90"]).to.equal(75);
- });
-
- it("should handle errors appropriately", async function() {
- // Setup stub to throw error
- monitorFindStub.rejects(new Error("Database error"));
-
- try {
- await getAllMonitorsWithUptimeStats();
- } catch (error) {
- expect(error).to.be.an("error");
- expect(error.message).to.equal("Database error");
- expect(error.service).to.equal("monitorModule");
- expect(error.method).to.equal("getAllMonitorsWithUptimeStats");
- }
- });
-
- it("should handle empty monitor list", async function() {
- monitorFindStub.resolves([]);
-
- const result = await getAllMonitorsWithUptimeStats();
-
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(0);
- });
-
- it("should handle monitor with no checks", async function() {
- const mockMonitors = [
- {
- _id: "monitor1",
- type: "http",
- toObject: () => ({
- _id: "monitor1",
- type: "http",
- name: "Test Monitor",
- }),
- },
- ];
-
- monitorFindStub.resolves(mockMonitors);
- checkFindStub.resolves([]);
-
- const result = await getAllMonitorsWithUptimeStats();
-
- expect(result[0]).to.have.property("1", 0);
- expect(result[0]).to.have.property("7", 0);
- expect(result[0]).to.have.property("30", 0);
- expect(result[0]).to.have.property("90", 0);
- });
- });
-
- describe("calculateUptimeDuration", function() {
- let clock;
- const NOW = new Date("2024-01-01T12:00:00Z").getTime();
-
- beforeEach(function() {
- // Fix the current time
- clock = sinon.useFakeTimers(NOW);
- });
-
- afterEach(function() {
- clock.restore();
- });
-
- it("should return 0 when checks array is empty", function() {
- expect(calculateUptimeDuration([])).to.equal(0);
- });
-
- it("should return 0 when checks array is null", function() {
- expect(calculateUptimeDuration(null)).to.equal(0);
- });
-
- it("should calculate uptime from last down check to most recent check", function() {
- const checks = [
- { status: true, createdAt: "2024-01-01T11:00:00Z" }, // Most recent
- { status: true, createdAt: "2024-01-01T10:00:00Z" },
- { status: false, createdAt: "2024-01-01T09:00:00Z" }, // Last down
- { status: true, createdAt: "2024-01-01T08:00:00Z" },
- ];
-
- // Expected: 2 hours (from 09:00 to 11:00) = 7200000ms
- expect(calculateUptimeDuration(checks)).to.equal(7200000);
- });
-
- it("should calculate uptime from first check when no down checks exist", function() {
- const checks = [
- { status: true, createdAt: "2024-01-01T11:00:00Z" },
- { status: true, createdAt: "2024-01-01T10:00:00Z" },
- { status: true, createdAt: "2024-01-01T09:00:00Z" },
- ];
-
- // Expected: Current time (12:00) - First check (09:00) = 3 hours = 10800000ms
- expect(calculateUptimeDuration(checks)).to.equal(10800000);
- });
- });
-
- describe("getLastChecked", function() {
- let clock;
- const NOW = new Date("2024-01-01T12:00:00Z").getTime();
-
- beforeEach(function() {
- // Fix the current time
- clock = sinon.useFakeTimers(NOW);
- });
-
- afterEach(function() {
- clock.restore();
- });
-
- it("should return 0 when checks array is empty", function() {
- expect(getLastChecked([])).to.equal(0);
- });
-
- it("should return 0 when checks array is null", function() {
- expect(getLastChecked(null)).to.equal(0);
- });
-
- it("should return time difference between now and most recent check", function() {
- const checks = [
- { createdAt: "2024-01-01T11:30:00Z" }, // 30 minutes ago
- { createdAt: "2024-01-01T11:00:00Z" },
- { createdAt: "2024-01-01T10:30:00Z" },
- ];
-
- // Expected: 30 minutes = 1800000ms
- expect(getLastChecked(checks)).to.equal(1800000);
- });
-
- it("should handle checks from different days", function() {
- const checks = [
- { createdAt: "2023-12-31T12:00:00Z" }, // 24 hours ago
- { createdAt: "2023-12-30T12:00:00Z" },
- ];
-
- // Expected: 24 hours = 86400000ms
- expect(getLastChecked(checks)).to.equal(86400000);
- });
- });
-
- describe("getLatestResponseTime", function() {
- it("should return 0 when checks array is empty", function() {
- expect(getLatestResponseTime([])).to.equal(0);
- });
-
- it("should return 0 when checks array is null", function() {
- expect(getLatestResponseTime(null)).to.equal(0);
- });
-
- it("should return response time from most recent check", function() {
- const checks = [
- { responseTime: 150, createdAt: "2024-01-01T11:30:00Z" }, // Most recent
- { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" },
- { responseTime: 250, createdAt: "2024-01-01T10:30:00Z" },
- ];
-
- expect(getLatestResponseTime(checks)).to.equal(150);
- });
-
- it("should handle missing responseTime in checks", function() {
- const checks = [
- { createdAt: "2024-01-01T11:30:00Z" },
- { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" },
- ];
-
- expect(getLatestResponseTime(checks)).to.equal(0);
- });
- });
-
- describe("getAverageResponseTime", function() {
- it("should return 0 when checks array is empty", function() {
- expect(getAverageResponseTime([])).to.equal(0);
- });
-
- it("should return 0 when checks array is null", function() {
- expect(getAverageResponseTime(null)).to.equal(0);
- });
-
- it("should calculate average response time from all checks", function() {
- const checks = [
- { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" },
- { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" },
- { responseTime: 300, createdAt: "2024-01-01T10:30:00Z" },
- ];
-
- // Average: (100 + 200 + 300) / 3 = 200
- expect(getAverageResponseTime(checks)).to.equal(200);
- });
-
- it("should handle missing responseTime in some checks", function() {
- const checks = [
- { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" },
- { createdAt: "2024-01-01T11:00:00Z" },
- { responseTime: 300, createdAt: "2024-01-01T10:30:00Z" },
- ];
-
- // Average: (100 + 300) / 2 = 200
- expect(getAverageResponseTime(checks)).to.equal(200);
- });
-
- it("should return 0 when no checks have responseTime", function() {
- const checks = [
- { createdAt: "2024-01-01T11:30:00Z" },
- { createdAt: "2024-01-01T11:00:00Z" },
- ];
-
- expect(getAverageResponseTime(checks)).to.equal(0);
- });
- });
-
- describe("getUptimePercentage", function() {
- it("should return 0 when checks array is empty", function() {
- expect(getUptimePercentage([])).to.equal(0);
- });
-
- it("should return 0 when checks array is null", function() {
- expect(getUptimePercentage(null)).to.equal(0);
- });
-
- it("should return 100 when all checks are up", function() {
- const checks = [{ status: true }, { status: true }, { status: true }];
- expect(getUptimePercentage(checks)).to.equal(100);
- });
-
- it("should return 0 when all checks are down", function() {
- const checks = [{ status: false }, { status: false }, { status: false }];
- expect(getUptimePercentage(checks)).to.equal(0);
- });
-
- it("should calculate correct percentage for mixed status checks", function() {
- const checks = [
- { status: true },
- { status: false },
- { status: true },
- { status: true },
- ];
- // 3 up out of 4 total = 75%
- expect(getUptimePercentage(checks)).to.equal(75);
- });
-
- it("should handle undefined status values", function() {
- const checks = [{ status: true }, { status: undefined }, { status: true }];
- // 2 up out of 3 total โ 66.67%
- expect(getUptimePercentage(checks)).to.equal((2 / 3) * 100);
- });
- });
-
- describe("getIncidents", function() {
- it("should return 0 when checks array is empty", function() {
- expect(getIncidents([])).to.equal(0);
- });
-
- it("should return 0 when checks array is null", function() {
- expect(getIncidents(null)).to.equal(0);
- });
-
- it("should return 0 when all checks are up", function() {
- const checks = [{ status: true }, { status: true }, { status: true }];
- expect(getIncidents(checks)).to.equal(0);
- });
-
- it("should count all incidents when all checks are down", function() {
- const checks = [{ status: false }, { status: false }, { status: false }];
- expect(getIncidents(checks)).to.equal(3);
- });
-
- it("should count correct number of incidents for mixed status checks", function() {
- const checks = [
- { status: true },
- { status: false },
- { status: true },
- { status: false },
- { status: true },
- ];
- expect(getIncidents(checks)).to.equal(2);
- });
-
- it("should handle undefined status values", function() {
- const checks = [
- { status: true },
- { status: undefined },
- { status: false },
- { status: false },
- ];
- // Only counts explicit false values
- expect(getIncidents(checks)).to.equal(2);
- });
- });
-
- describe("getMonitorChecks", function() {
- let mockModel;
-
- beforeEach(function() {
- // Create a mock model with chainable methods
- const mockChecks = [
- { monitorId: "123", createdAt: new Date("2024-01-01") },
- { monitorId: "123", createdAt: new Date("2024-01-02") },
- ];
-
- mockModel = {
- find: sinon.stub().returns({
- sort: sinon.stub().returns(mockChecks),
- }),
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return all checks and date-ranged checks", async function() {
- // Arrange
- const monitorId = "123";
- const dateRange = {
- start: new Date("2024-01-01"),
- end: new Date("2024-01-02"),
- };
- const sortOrder = -1;
-
- // Act
- const result = await getMonitorChecks(monitorId, mockModel, dateRange, sortOrder);
-
- // Assert
- expect(result).to.have.keys(["checksAll", "checksForDateRange"]);
-
- // Verify find was called with correct parameters
- expect(mockModel.find.firstCall.args[0]).to.deep.equal({ monitorId });
- expect(mockModel.find.secondCall.args[0]).to.deep.equal({
- monitorId,
- createdAt: { $gte: dateRange.start, $lte: dateRange.end },
- });
-
- // Verify sort was called with correct parameters
- const sortCalls = mockModel.find().sort.getCalls();
- sortCalls.forEach((call) => {
- expect(call.args[0]).to.deep.equal({ createdAt: sortOrder });
- });
- });
-
- it("should handle empty results", async function() {
- // Arrange
- const emptyModel = {
- find: sinon.stub().returns({
- sort: sinon.stub().returns([]),
- }),
- };
-
- // Act
- const result = await getMonitorChecks(
- "123",
- emptyModel,
- {
- start: new Date(),
- end: new Date(),
- },
- -1
- );
-
- // Assert
- expect(result.checksAll).to.be.an("array").that.is.empty;
- expect(result.checksForDateRange).to.be.an("array").that.is.empty;
- });
-
- it("should maintain sort order", async function() {
- // Arrange
- const sortedChecks = [
- { monitorId: "123", createdAt: new Date("2024-01-02") },
- { monitorId: "123", createdAt: new Date("2024-01-01") },
- ];
-
- const sortedModel = {
- find: sinon.stub().returns({
- sort: sinon.stub().returns(sortedChecks),
- }),
- };
-
- // Act
- const result = await getMonitorChecks(
- "123",
- sortedModel,
- {
- start: new Date("2024-01-01"),
- end: new Date("2024-01-02"),
- },
- -1
- );
-
- // Assert
- expect(result.checksAll[0].createdAt).to.be.greaterThan(
- result.checksAll[1].createdAt
- );
- expect(result.checksForDateRange[0].createdAt).to.be.greaterThan(
- result.checksForDateRange[1].createdAt
- );
- });
- });
-
- describe("processChecksForDisplay", function() {
- let normalizeStub;
-
- beforeEach(function() {
- normalizeStub = sinon.stub();
- });
-
- it("should return original checks when numToDisplay is not provided", function() {
- const checks = [1, 2, 3, 4, 5];
- const result = processChecksForDisplay(normalizeStub, checks);
- expect(result).to.deep.equal(checks);
- });
-
- it("should return original checks when numToDisplay is greater than checks length", function() {
- const checks = [1, 2, 3];
- const result = processChecksForDisplay(normalizeStub, checks, 5);
- expect(result).to.deep.equal(checks);
- });
-
- it("should filter checks based on numToDisplay", function() {
- const checks = [1, 2, 3, 4, 5, 6];
- const result = processChecksForDisplay(normalizeStub, checks, 3);
- // Should return [1, 3, 5] as n = ceil(6/3) = 2
- expect(result).to.deep.equal([1, 3, 5]);
- });
-
- it("should handle empty checks array", function() {
- const checks = [];
- const result = processChecksForDisplay(normalizeStub, checks, 3);
- expect(result).to.be.an("array").that.is.empty;
- });
-
- it("should call normalizeData when normalize is true", function() {
- const checks = [1, 2, 3];
- normalizeStub.returns([10, 20, 30]);
-
- const result = processChecksForDisplay(normalizeStub, checks, null, true);
-
- expect(normalizeStub.args[0]).to.deep.equal([checks, 1, 100]);
- expect(result).to.deep.equal([10, 20, 30]);
- });
-
- it("should handle both filtering and normalization", function() {
- const checks = [1, 2, 3, 4, 5, 6];
- normalizeStub.returns([10, 30, 50]);
-
- const result = processChecksForDisplay(normalizeStub, checks, 3, true);
-
- expect(normalizeStub.args[0][0]).to.deep.equal([1, 3, 5]);
- expect(result).to.deep.equal([10, 30, 50]);
- });
- });
-
- describe("groupChecksByTime", function() {
- const mockChecks = [
- { createdAt: "2024-01-15T10:30:45Z" },
- { createdAt: "2024-01-15T10:45:15Z" },
- { createdAt: "2024-01-15T11:15:00Z" },
- { createdAt: "2024-01-16T10:30:00Z" },
- ];
-
- it("should group checks by hour when dateRange is 'day'", function() {
- const result = groupChecksByTime(mockChecks, "day");
-
- // Get timestamps for 10:00 and 11:00 on Jan 15
- const time1 = new Date("2024-01-15T10:00:00Z").getTime();
- const time2 = new Date("2024-01-15T11:00:00Z").getTime();
- const time3 = new Date("2024-01-16T10:00:00Z").getTime();
-
- expect(Object.keys(result)).to.have.lengthOf(3);
-
- expect(result[time1].checks).to.have.lengthOf(2);
- expect(result[time2].checks).to.have.lengthOf(1);
- expect(result[time3].checks).to.have.lengthOf(1);
- });
-
- it("should group checks by day when dateRange is not 'day'", function() {
- const result = groupChecksByTime(mockChecks, "week");
-
- expect(Object.keys(result)).to.have.lengthOf(2);
- expect(result["2024-01-15"].checks).to.have.lengthOf(3);
- expect(result["2024-01-16"].checks).to.have.lengthOf(1);
- });
-
- it("should handle empty checks array", function() {
- const result = groupChecksByTime([], "day");
- expect(result).to.deep.equal({});
- });
-
- it("should handle single check", function() {
- const singleCheck = [{ createdAt: "2024-01-15T10:30:45Z" }];
- const result = groupChecksByTime(singleCheck, "day");
-
- const expectedTime = new Date("2024-01-15T10:00:00Z").getTime();
- expect(Object.keys(result)).to.have.lengthOf(1);
- expect(result[expectedTime].checks).to.have.lengthOf(1);
- });
-
- it("should skip invalid dates and process valid ones", function() {
- const checksWithInvalidDate = [
- { createdAt: "invalid-date" },
- { createdAt: "2024-01-15T10:30:45Z" },
- { createdAt: null },
- { createdAt: undefined },
- { createdAt: "" },
- ];
-
- const result = groupChecksByTime(checksWithInvalidDate, "day");
-
- const expectedTime = new Date("2024-01-15T10:00:00Z").getTime();
- expect(Object.keys(result)).to.have.lengthOf(1);
- expect(result[expectedTime].checks).to.have.lengthOf(1);
- expect(result[expectedTime].checks[0].createdAt).to.equal("2024-01-15T10:30:45Z");
- });
-
- it("should handle checks in same time group", function() {
- const checksInSameHour = [
- { createdAt: "2024-01-15T10:15:00Z" },
- { createdAt: "2024-01-15T10:45:00Z" },
- ];
-
- const result = groupChecksByTime(checksInSameHour, "day");
-
- const expectedTime = new Date("2024-01-15T10:00:00Z").getTime();
- expect(Object.keys(result)).to.have.lengthOf(1);
- expect(result[expectedTime].checks).to.have.lengthOf(2);
- });
- });
-
- describe("calculateGroupStats", function() {
- // Mock getUptimePercentage function
- let uptimePercentageStub;
-
- beforeEach(function() {
- uptimePercentageStub = sinon.stub();
- uptimePercentageStub.returns(95); // Default return value
- });
-
- it("should calculate stats correctly for a group of checks", function() {
- const mockGroup = {
- time: "2024-01-15",
- checks: [
- { status: true, responseTime: 100 },
- { status: false, responseTime: 200 },
- { status: true, responseTime: 300 },
- ],
- };
-
- const result = calculateGroupStats(mockGroup, uptimePercentageStub);
-
- expect(result).to.deep.equal({
- time: "2024-01-15",
- uptimePercentage: (2 / 3) * 100,
- totalChecks: 3,
- totalIncidents: 1,
- avgResponseTime: 200, // (100 + 200 + 300) / 3
- });
- });
-
- it("should handle empty checks array", function() {
- const mockGroup = {
- time: "2024-01-15",
- checks: [],
- };
-
- const result = calculateGroupStats(mockGroup, uptimePercentageStub);
-
- expect(result).to.deep.equal({
- time: "2024-01-15",
- uptimePercentage: 0,
- totalChecks: 0,
- totalIncidents: 0,
- avgResponseTime: 0,
- });
- });
-
- it("should handle missing responseTime values", function() {
- const mockGroup = {
- time: "2024-01-15",
- checks: [
- { status: true },
- { status: false, responseTime: 200 },
- { status: true, responseTime: undefined },
- ],
- };
-
- const result = calculateGroupStats(mockGroup, uptimePercentageStub);
-
- expect(result).to.deep.equal({
- time: "2024-01-15",
- uptimePercentage: (2 / 3) * 100,
- totalChecks: 3,
- totalIncidents: 1,
- avgResponseTime: 200, // 200 / 1
- });
- });
-
- it("should handle all checks with status false", function() {
- const mockGroup = {
- time: "2024-01-15",
- checks: [
- { status: false, responseTime: 100 },
- { status: false, responseTime: 200 },
- { status: false, responseTime: 300 },
- ],
- };
-
- const result = calculateGroupStats(mockGroup, uptimePercentageStub);
-
- expect(result).to.deep.equal({
- time: "2024-01-15",
- uptimePercentage: 0,
- totalChecks: 3,
- totalIncidents: 3,
- avgResponseTime: 200,
- });
- });
-
- it("should handle all checks with status true", function() {
- const mockGroup = {
- time: "2024-01-15",
- checks: [
- { status: true, responseTime: 100 },
- { status: true, responseTime: 200 },
- { status: true, responseTime: 300 },
- ],
- };
-
- const result = calculateGroupStats(mockGroup, uptimePercentageStub);
-
- expect(result).to.deep.equal({
- time: "2024-01-15",
- uptimePercentage: 100,
- totalChecks: 3,
- totalIncidents: 0,
- avgResponseTime: 200,
- });
- });
- });
-
- describe("getMonitorStatsById", function() {
- const now = new Date();
- const oneHourAgo = new Date(now - 3600000);
- const twoHoursAgo = new Date(now - 7200000);
-
- const mockMonitor = {
- _id: "monitor123",
- type: "http",
- name: "Test Monitor",
- url: "https://test.com",
- toObject: () => ({
- _id: "monitor123",
- type: "http",
- name: "Test Monitor",
- url: "https://test.com",
- }),
- };
-
- const mockMonitorPing = {
- _id: "monitor123",
- type: "ping",
- name: "Test Monitor",
- url: "https://test.com",
- toObject: () => ({
- _id: "monitor123",
- type: "http",
- name: "Test Monitor",
- url: "https://test.com",
- }),
- };
- const mockMonitorDocker = {
- _id: "monitor123",
- type: "docker",
- name: "Test Monitor",
- url: "https://test.com",
- toObject: () => ({
- _id: "monitor123",
- type: "http",
- name: "Test Monitor",
- url: "https://test.com",
- }),
- };
-
- const checkDocs = [
- {
- monitorId: "monitor123",
- status: true,
- responseTime: 100,
- createdAt: new Date("2024-01-01T12:00:00Z"),
- toObject: function () {
- return {
- monitorId: this.monitorId,
- status: this.status,
- responseTime: this.responseTime,
- createdAt: this.createdAt,
- };
- },
- },
- {
- monitorId: "monitor123",
- status: true,
- responseTime: 150,
- createdAt: new Date("2024-01-01T11:00:00Z"),
- toObject: function () {
- return {
- monitorId: this.monitorId,
- status: this.status,
- responseTime: this.responseTime,
- createdAt: this.createdAt,
- };
- },
- },
- {
- monitorId: "monitor123",
- status: false,
- responseTime: 200,
- createdAt: new Date("2024-01-01T10:00:00Z"),
- toObject: function () {
- return {
- monitorId: this.monitorId,
- status: this.status,
- responseTime: this.responseTime,
- createdAt: this.createdAt,
- };
- },
- },
- ];
- const req = {
- params: { monitorId: "monitor123" },
- query: {
- dateRange: "day",
- sortOrder: "desc",
- numToDisplay: 10,
- normalize: true,
- },
- };
-
- beforeEach(function() {
- checkFindStub.returns({
- sort: () => checkDocs,
- });
- monitorFindByIdStub.returns(mockMonitor);
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return monitor stats with calculated values, sort order desc", async function() {
- req.query.sortOrder = "desc";
- const result = await getMonitorStatsById(req);
- expect(result).to.include.keys([
- "_id",
- "type",
- "name",
- "url",
- "uptimeDuration",
- "lastChecked",
- "latestResponseTime",
- "periodIncidents",
- "periodTotalChecks",
- "periodAvgResponseTime",
- "periodUptime",
- "aggregateData",
- ]);
- expect(result.latestResponseTime).to.equal(100);
- expect(result.periodTotalChecks).to.equal(3);
- expect(result.periodIncidents).to.equal(1);
- expect(result.periodUptime).to.be.a("number");
- expect(result.aggregateData).to.be.an("array");
- });
-
- it("should return monitor stats with calculated values, ping type", async function() {
- monitorFindByIdStub.returns(mockMonitorPing);
- req.query.sortOrder = "desc";
- const result = await getMonitorStatsById(req);
- expect(result).to.include.keys([
- "_id",
- "type",
- "name",
- "url",
- "uptimeDuration",
- "lastChecked",
- "latestResponseTime",
- "periodIncidents",
- "periodTotalChecks",
- "periodAvgResponseTime",
- "periodUptime",
- "aggregateData",
- ]);
- expect(result.latestResponseTime).to.equal(100);
- expect(result.periodTotalChecks).to.equal(3);
- expect(result.periodIncidents).to.equal(1);
- expect(result.periodUptime).to.be.a("number");
- expect(result.aggregateData).to.be.an("array");
- });
-
- it("should return monitor stats with calculated values, docker type", async function() {
- monitorFindByIdStub.returns(mockMonitorDocker);
- req.query.sortOrder = "desc";
- const result = await getMonitorStatsById(req);
- expect(result).to.include.keys([
- "_id",
- "type",
- "name",
- "url",
- "uptimeDuration",
- "lastChecked",
- "latestResponseTime",
- "periodIncidents",
- "periodTotalChecks",
- "periodAvgResponseTime",
- "periodUptime",
- "aggregateData",
- ]);
- expect(result.latestResponseTime).to.equal(100);
- expect(result.periodTotalChecks).to.equal(3);
- expect(result.periodIncidents).to.equal(1);
- expect(result.periodUptime).to.be.a("number");
- expect(result.aggregateData).to.be.an("array");
- });
-
- it("should return monitor stats with calculated values", async function() {
- req.query.sortOrder = "asc";
- const result = await getMonitorStatsById(req);
- expect(result).to.include.keys([
- "_id",
- "type",
- "name",
- "url",
- "uptimeDuration",
- "lastChecked",
- "latestResponseTime",
- "periodIncidents",
- "periodTotalChecks",
- "periodAvgResponseTime",
- "periodUptime",
- "aggregateData",
- ]);
- expect(result.latestResponseTime).to.equal(100);
- expect(result.periodTotalChecks).to.equal(3);
- expect(result.periodIncidents).to.equal(1);
- expect(result.periodUptime).to.be.a("number");
- expect(result.aggregateData).to.be.an("array");
- });
-
- it("should throw error when monitor is not found", async function() {
- monitorFindByIdStub.returns(Promise.resolve(null));
-
- const req = {
- params: { monitorId: "nonexistent" },
- };
-
- try {
- await getMonitorStatsById(req);
- expect.fail("Should have thrown an error");
- } catch (error) {
- expect(error).to.be.an("Error");
- expect(error.service).to.equal("monitorModule");
- expect(error.method).to.equal("getMonitorStatsById");
- }
- });
- });
-
- describe("getMonitorById", function() {
- let notificationFindStub;
- let monitorSaveStub;
-
- beforeEach(function() {
- // Create stubs
- notificationFindStub = sinon.stub(Notification, "find");
- monitorSaveStub = sinon.stub().resolves();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return monitor with notifications when found", async function() {
- // Arrange
- const monitorId = "123";
- const mockMonitor = {
- _id: monitorId,
- name: "Test Monitor",
- save: monitorSaveStub,
- };
- const mockNotifications = [
- { _id: "notif1", message: "Test notification 1" },
- { _id: "notif2", message: "Test notification 2" },
- ];
-
- monitorFindByIdStub.resolves(mockMonitor);
- notificationFindStub.resolves(mockNotifications);
-
- const result = await getMonitorById(monitorId);
- expect(result._id).to.equal(monitorId);
- expect(result.name).to.equal("Test Monitor");
- expect(monitorFindByIdStub.calledWith(monitorId)).to.be.true;
- expect(notificationFindStub.calledWith({ monitorId })).to.be.true;
- expect(monitorSaveStub.calledOnce).to.be.true;
- });
-
- it("should throw 404 error when monitor not found", async function() {
- // Arrange
- const monitorId = "nonexistent";
- monitorFindByIdStub.resolves(null);
-
- // Act & Assert
- try {
- await getMonitorById(monitorId);
- expect.fail("Should have thrown an error");
- } catch (error) {
- expect(error.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
- expect(error.status).to.equal(404);
- expect(error.service).to.equal("monitorModule");
- expect(error.method).to.equal("getMonitorById");
- }
- });
-
- it("should handle database errors properly", async function() {
- // Arrange
- const monitorId = "123";
- const dbError = new Error("Database connection failed");
- monitorFindByIdStub.rejects(dbError);
-
- // Act & Assert
- try {
- await getMonitorById(monitorId);
- expect.fail("Should have thrown an error");
- } catch (error) {
- expect(error.service).to.equal("monitorModule");
- expect(error.method).to.equal("getMonitorById");
- expect(error.message).to.equal("Database connection failed");
- }
- });
-
- it("should handle notification fetch errors", async function() {
- // Arrange
- const monitorId = "123";
- const mockMonitor = {
- _id: monitorId,
- name: "Test Monitor",
- save: monitorSaveStub,
- };
- const notificationError = new Error("Notification fetch failed");
-
- monitorFindByIdStub.resolves(mockMonitor);
- notificationFindStub.rejects(notificationError);
-
- // Act & Assert
- try {
- await getMonitorById(monitorId);
- expect.fail("Should have thrown an error");
- } catch (error) {
- expect(error.service).to.equal("monitorModule");
- expect(error.method).to.equal("getMonitorById");
- expect(error.message).to.equal("Notification fetch failed");
- }
- });
-
- it("should handle monitor save errors", async function() {
- // Arrange
- const monitorId = "123";
- const mockMonitor = {
- _id: monitorId,
- name: "Test Monitor",
- save: sinon.stub().rejects(new Error("Save failed")),
- };
- const mockNotifications = [];
-
- monitorFindByIdStub.resolves(mockMonitor);
- notificationFindStub.resolves(mockNotifications);
-
- // Act & Assert
- try {
- await getMonitorById(monitorId);
- expect.fail("Should have thrown an error");
- } catch (error) {
- expect(error.service).to.equal("monitorModule");
- expect(error.method).to.equal("getMonitorById");
- expect(error.message).to.equal("Save failed");
- }
- });
- });
-
- describe("getMonitorsAndSummaryByTeamId", function() {
- it("should return monitors and correct summary counts", async function() {
- // Arrange
- const teamId = "team123";
- const type = "http";
- const mockMonitors = [
- { teamId, type, status: true, isActive: true }, // up
- { teamId, type, status: false, isActive: true }, // down
- { teamId, type, status: null, isActive: false }, // paused
- { teamId, type, status: true, isActive: true }, // up
- ];
- monitorFindStub.resolves(mockMonitors);
-
- // Act
- const result = await getMonitorsAndSummaryByTeamId(teamId, type);
-
- // Assert
- expect(result.monitors).to.have.lengthOf(4);
- expect(result.monitorCounts).to.deep.equal({
- up: 2,
- down: 1,
- paused: 1,
- total: 4,
- });
- expect(monitorFindStub.calledOnceWith({ teamId, type })).to.be.true;
- });
-
- it("should return empty results for non-existent team", async function() {
- // Arrange
- monitorFindStub.resolves([]);
-
- // Act
- const result = await getMonitorsAndSummaryByTeamId("nonexistent", "http");
-
- // Assert
- expect(result.monitors).to.have.lengthOf(0);
- expect(result.monitorCounts).to.deep.equal({
- up: 0,
- down: 0,
- paused: 0,
- total: 0,
- });
- });
-
- it("should handle database errors", async function() {
- // Arrange
- const error = new Error("Database error");
- error.service = "MonitorModule";
- error.method = "getMonitorsAndSummaryByTeamId";
- monitorFindStub.rejects(error);
-
- // Act & Assert
- try {
- await getMonitorsAndSummaryByTeamId("team123", "http");
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err).to.equal(error);
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("getMonitorsAndSummaryByTeamId");
- }
- });
- });
-
- describe("getMonitorsByTeamId", function() {
- beforeEach(function() {
- // Chain stubs for Monitor.find().skip().limit().sort()
-
- // Stub for CHECK_MODEL_LOOKUP model find
- checkFindStub.returns({
- sort: sinon.stub().returns({
- limit: sinon.stub().returns([]),
- }),
- });
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should return monitors with basic query parameters", async function() {
- const mockMonitors = [
- { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) },
- { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) },
- ];
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns(mockMonitors),
- }),
- }),
- });
-
- const req = {
- params: { teamId: "team123" },
- query: {
- type: "http",
- page: 0,
- rowsPerPage: 10,
- field: "name",
- status: false,
- checkOrder: "desc",
- },
- };
-
- monitorCountStub.resolves(2);
-
- const result = await getMonitorsByTeamId(req);
-
- expect(result).to.have.property("monitors");
- expect(result).to.have.property("monitorCount", 2);
- });
-
- it("should return monitors with basic query parameters", async function() {
- const mockMonitors = [
- { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) },
- { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) },
- ];
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns(mockMonitors),
- }),
- }),
- });
-
- const req = {
- params: { teamId: "team123" },
- query: {
- type: "http",
- page: 0,
- rowsPerPage: 10,
- field: "name",
- status: true,
- checkOrder: "asc",
- },
- };
-
- monitorCountStub.resolves(2);
-
- const result = await getMonitorsByTeamId(req);
-
- expect(result).to.have.property("monitors");
- expect(result).to.have.property("monitorCount", 2);
- });
-
- it("should handle type filter with array input", async function() {
- const req = {
- params: { teamId: "team123" },
- query: {
- type: ["http", "ping"],
- },
- };
-
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns([]),
- }),
- }),
- });
- monitorCountStub.resolves(0);
-
- await getMonitorsByTeamId(req);
-
- expect(Monitor.find.firstCall.args[0]).to.deep.equal({
- teamId: "team123",
- type: { $in: ["http", "ping"] },
- });
- });
-
- it("should handle text search filter", async function() {
- const req = {
- params: { teamId: "team123" },
- query: {
- filter: "search",
- },
- };
-
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns([]),
- }),
- }),
- });
- monitorCountStub.resolves(0);
-
- await getMonitorsByTeamId(req);
-
- expect(Monitor.find.firstCall.args[0]).to.deep.equal({
- teamId: "team123",
- $or: [
- { name: { $regex: "search", $options: "i" } },
- { url: { $regex: "search", $options: "i" } },
- ],
- });
- });
-
- it("should handle pagination parameters", async function() {
- const req = {
- params: { teamId: "team123" },
- query: {
- page: 2,
- rowsPerPage: 5,
- },
- };
-
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns([]),
- }),
- }),
- });
- monitorCountStub.resolves(0);
-
- const result = await getMonitorsByTeamId(req);
- expect(result).to.deep.equal({
- monitors: [],
- monitorCount: 0,
- });
- });
-
- it("should handle sorting parameters", async function() {
- const req = {
- params: { teamId: "team123" },
- query: {
- field: "name",
- order: "asc",
- },
- };
-
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns([]),
- }),
- }),
- });
- monitorCountStub.resolves(0);
-
- await getMonitorsByTeamId(req);
-
- const result = await getMonitorsByTeamId(req);
- expect(result).to.deep.equal({
- monitors: [],
- monitorCount: 0,
- });
- });
-
- it("should return early when limit is -1", async function() {
- // Arrange
- const req = {
- params: { teamId: "team123" },
- query: {
- limit: "-1",
- },
- };
-
- const mockMonitors = [
- { _id: "1", type: "http" },
- { _id: "2", type: "ping" },
- ];
-
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns(mockMonitors),
- }),
- }),
- });
-
- monitorCountStub.resolves(2);
-
- // Act
- const result = await getMonitorsByTeamId(req);
-
- // Assert
- expect(result).to.deep.equal({
- monitors: mockMonitors,
- monitorCount: 2,
- });
- });
-
- it("should normalize checks when normalize parameter is provided", async function() {
- const req = {
- params: { teamId: "team123" },
- query: { normalize: "true" },
- };
- monitorCountStub.resolves(2);
-
- const mockMonitors = [
- { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) },
- { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) },
- ];
-
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().returns(mockMonitors),
- }),
- }),
- });
-
- const result = await getMonitorsByTeamId(req);
- expect(result.monitorCount).to.equal(2);
- expect(result.monitors).to.have.lengthOf(2);
- });
-
- it("should handle database errors", async function() {
- const req = {
- params: { teamId: "team123" },
- query: {},
- };
-
- const error = new Error("Database error");
- monitorFindStub.returns({
- skip: sinon.stub().returns({
- limit: sinon.stub().returns({
- sort: sinon.stub().throws(error),
- }),
- }),
- });
-
- try {
- await getMonitorsByTeamId(req);
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("getMonitorsByTeamId");
- expect(err.message).to.equal("Database error");
- }
- });
- });
-
- describe("createMonitor", function() {
- it("should create a monitor without notifications", async function() {
- let monitorSaveStub = sinon.stub(Monitor.prototype, "save").resolves();
-
- const req = {
- body: {
- name: "Test Monitor",
- url: "http://test.com",
- type: "http",
- notifications: ["someNotification"],
- },
- };
-
- const expectedMonitor = {
- name: "Test Monitor",
- url: "http://test.com",
- type: "http",
- notifications: undefined,
- save: monitorSaveStub,
- };
-
- const result = await createMonitor(req);
- expect(result.name).to.equal(expectedMonitor.name);
- expect(result.url).to.equal(expectedMonitor.url);
- });
-
- it("should handle database errors", async function() {
- const req = {
- body: {
- name: "Test Monitor",
- },
- };
-
- try {
- await createMonitor(req);
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("createMonitor");
- }
- });
- });
-
- describe("deleteMonitor", function() {
- it("should delete a monitor successfully", async function() {
- const monitorId = "123456789";
- const mockMonitor = {
- _id: monitorId,
- name: "Test Monitor",
- url: "http://test.com",
- };
-
- const req = {
- params: { monitorId },
- };
-
- monitorFindByIdAndDeleteStub.resolves(mockMonitor);
-
- const result = await deleteMonitor(req);
-
- expect(result).to.deep.equal(mockMonitor);
- sinon.assert.calledWith(monitorFindByIdAndDeleteStub, monitorId);
- });
-
- it("should throw error when monitor not found", async function() {
- const monitorId = "nonexistent123";
- const req = {
- params: { monitorId },
- };
-
- monitorFindByIdAndDeleteStub.resolves(null);
-
- try {
- await deleteMonitor(req);
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("deleteMonitor");
- }
- });
-
- it("should handle database errors", async function() {
- const monitorId = "123456789";
- const req = {
- params: { monitorId },
- };
-
- const dbError = new Error("Database connection error");
- monitorFindByIdAndDeleteStub.rejects(dbError);
-
- try {
- await deleteMonitor(req);
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err.message).to.equal("Database connection error");
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("deleteMonitor");
- }
- });
- });
-
- describe("deleteAllMonitors", function() {
- it("should delete all monitors for a team successfully", async function() {
- const teamId = "team123";
- const mockMonitors = [
- { _id: "1", name: "Monitor 1", teamId },
- { _id: "2", name: "Monitor 2", teamId },
- ];
-
- monitorFindStub.resolves(mockMonitors);
- monitorDeleteManyStub.resolves({ deletedCount: 2 });
-
- const result = await deleteAllMonitors(teamId);
-
- expect(result).to.deep.equal({
- monitors: mockMonitors,
- deletedCount: 2,
- });
- sinon.assert.calledWith(monitorFindStub, { teamId });
- sinon.assert.calledWith(monitorDeleteManyStub, { teamId });
- });
-
- it("should return empty array when no monitors found", async function() {
- const teamId = "emptyTeam";
-
- monitorFindStub.resolves([]);
- monitorDeleteManyStub.resolves({ deletedCount: 0 });
-
- const result = await deleteAllMonitors(teamId);
-
- expect(result).to.deep.equal({
- monitors: [],
- deletedCount: 0,
- });
- sinon.assert.calledWith(monitorFindStub, { teamId });
- sinon.assert.calledWith(monitorDeleteManyStub, { teamId });
- });
-
- it("should handle database errors", async function() {
- const teamId = "team123";
- const dbError = new Error("Database connection error");
- monitorFindStub.rejects(dbError);
-
- try {
- await deleteAllMonitors(teamId);
- } catch (err) {
- expect(err.message).to.equal("Database connection error");
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("deleteAllMonitors");
- }
- });
-
- it("should handle deleteMany errors", async function() {
- const teamId = "team123";
- monitorFindStub.resolves([{ _id: "1", name: "Monitor 1" }]);
- monitorDeleteManyStub.rejects(new Error("Delete operation failed"));
-
- try {
- await deleteAllMonitors(teamId);
- } catch (err) {
- expect(err.message).to.equal("Delete operation failed");
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("deleteAllMonitors");
- }
- });
- });
-
- describe("deleteMonitorsByUserId", function() {
- beforeEach(function() {});
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should delete all monitors for a user successfully", async function() {
- // Arrange
- const userId = "user123";
- const mockResult = {
- deletedCount: 3,
- acknowledged: true,
- };
-
- monitorDeleteManyStub.resolves(mockResult);
-
- // Act
- const result = await deleteMonitorsByUserId(userId);
-
- // Assert
- expect(result).to.deep.equal(mockResult);
- sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId });
- });
-
- it("should return zero deletedCount when no monitors found", async function() {
- // Arrange
- const userId = "nonexistentUser";
- const mockResult = {
- deletedCount: 0,
- acknowledged: true,
- };
-
- monitorDeleteManyStub.resolves(mockResult);
-
- // Act
- const result = await deleteMonitorsByUserId(userId);
-
- // Assert
- expect(result.deletedCount).to.equal(0);
- sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId });
- });
-
- it("should handle database errors", async function() {
- // Arrange
- const userId = "user123";
- const dbError = new Error("Database connection error");
- monitorDeleteManyStub.rejects(dbError);
-
- // Act & Assert
- try {
- await deleteMonitorsByUserId(userId);
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err.message).to.equal("Database connection error");
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("deleteMonitorsByUserId");
- }
- });
- });
-
- describe("editMonitor", function() {
- it("should edit a monitor successfully", async function() {
- // Arrange
- const candidateId = "monitor123";
- const candidateMonitor = {
- name: "Updated Monitor",
- url: "http://updated.com",
- type: "http",
- notifications: ["someNotification"],
- };
-
- const expectedUpdateData = {
- name: "Updated Monitor",
- url: "http://updated.com",
- type: "http",
- notifications: undefined,
- };
-
- const mockUpdatedMonitor = {
- _id: candidateId,
- ...expectedUpdateData,
- };
-
- monitorFindByIdAndUpdateStub.resolves(mockUpdatedMonitor);
-
- // Act
- const result = await editMonitor(candidateId, candidateMonitor);
-
- // Assert
- expect(result).to.deep.equal(mockUpdatedMonitor);
- sinon.assert.calledWith(
- monitorFindByIdAndUpdateStub,
- candidateId,
- expectedUpdateData,
- {
- new: true,
- }
- );
- });
-
- it("should return null when monitor not found", async function() {
- // Arrange
- const candidateId = "nonexistent123";
- const candidateMonitor = {
- name: "Updated Monitor",
- };
-
- monitorFindByIdAndUpdateStub.resolves(null);
-
- // Act
- const result = await editMonitor(candidateId, candidateMonitor);
-
- // Assert
- expect(result).to.be.null;
- sinon.assert.calledWith(
- monitorFindByIdAndUpdateStub,
- candidateId,
- { name: "Updated Monitor", notifications: undefined },
- { new: true }
- );
- });
-
- it("should remove notifications from update data", async function() {
- // Arrange
- const candidateId = "monitor123";
- const candidateMonitor = {
- name: "Updated Monitor",
- notifications: ["notification1", "notification2"],
- };
-
- const expectedUpdateData = {
- name: "Updated Monitor",
- notifications: undefined,
- };
-
- monitorFindByIdAndUpdateStub.resolves({
- _id: candidateId,
- ...expectedUpdateData,
- });
-
- // Act
- await editMonitor(candidateId, candidateMonitor);
-
- // Assert
- sinon.assert.calledWith(
- monitorFindByIdAndUpdateStub,
- candidateId,
- expectedUpdateData,
- {
- new: true,
- }
- );
- });
-
- it("should handle database errors", async function() {
- // Arrange
- const candidateId = "monitor123";
- const candidateMonitor = {
- name: "Updated Monitor",
- };
-
- const dbError = new Error("Database connection error");
- monitorFindByIdAndUpdateStub.rejects(dbError);
-
- // Act & Assert
- try {
- await editMonitor(candidateId, candidateMonitor);
- expect.fail("Should have thrown an error");
- } catch (err) {
- expect(err.message).to.equal("Database connection error");
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("editMonitor");
- }
- });
- });
-
- describe("addDemoMonitors", function() {
- it("should add demo monitors successfully", async function() {
- // Arrange
- const userId = "user123";
- const teamId = "team123";
- monitorInsertManyStub.resolves([{ _id: "123" }]);
- const result = await addDemoMonitors(userId, teamId);
- expect(result).to.deep.equal([{ _id: "123" }]);
- });
-
- it("should handle database errors", async function() {
- const userId = "user123";
- const teamId = "team123";
-
- const dbError = new Error("Database connection error");
- monitorInsertManyStub.rejects(dbError);
-
- try {
- await addDemoMonitors(userId, teamId);
- } catch (err) {
- expect(err.message).to.equal("Database connection error");
- expect(err.service).to.equal("monitorModule");
- expect(err.method).to.equal("addDemoMonitors");
- }
- });
- });
-});
diff --git a/Server/tests/db/notificationModule.test.js b/Server/tests/db/notificationModule.test.js
deleted file mode 100644
index 5c77018c2..000000000
--- a/Server/tests/db/notificationModule.test.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import sinon from "sinon";
-import Notification from "../../db/models/Notification.js";
-import {
- createNotification,
- getNotificationsByMonitorId,
- deleteNotificationsByMonitorId,
-} from "../../db/mongo/modules/notificationModule.js";
-
-describe("notificationModule", function() {
- const mockNotification = {
- monitorId: "123",
- };
- const mockNotifications = [mockNotification];
- let notificationSaveStub, notificationFindStub, notificationDeleteManyStub;
-
- beforeEach(function() {
- notificationSaveStub = sinon.stub(Notification.prototype, "save").resolves();
- notificationFindStub = sinon.stub(Notification, "find").resolves();
- notificationDeleteManyStub = sinon.stub(Notification, "deleteMany").resolves();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createNotification", function() {
- it("should create a new notification", async function() {
- const notificationData = { _id: "123", name: "test" };
- notificationSaveStub.resolves(notificationData);
- const res = await createNotification(notificationData);
- expect(res).to.deep.equal(notificationData);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- notificationSaveStub.rejects(err);
- try {
- await createNotification(mockNotification);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getNotificationsByMonitorId", function() {
- it("should return notifications by monitor ID", async function() {
- notificationFindStub.resolves(mockNotifications);
- const res = await getNotificationsByMonitorId(mockNotification.monitorId);
- expect(res).to.deep.equal(mockNotifications);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- notificationFindStub.rejects(err);
- try {
- await getNotificationsByMonitorId(mockNotification.monitorId);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteNotificationsByMonitorId", function() {
- it("should delete notifications by monitor ID", async function() {
- notificationDeleteManyStub.resolves({ deletedCount: mockNotifications.length });
- const res = await deleteNotificationsByMonitorId(mockNotification.monitorId);
- expect(res).to.deep.equal(mockNotifications.length);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- notificationDeleteManyStub.rejects(err);
- try {
- await deleteNotificationsByMonitorId(mockNotification.monitorId);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-});
diff --git a/Server/tests/db/pageSpeedCheckModule.test.js b/Server/tests/db/pageSpeedCheckModule.test.js
deleted file mode 100644
index 3bacc4a3b..000000000
--- a/Server/tests/db/pageSpeedCheckModule.test.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import sinon from "sinon";
-import PageSpeedCheck from "../../db/models/PageSpeedCheck.js";
-import {
- createPageSpeedCheck,
- deletePageSpeedChecksByMonitorId,
-} from "../../db/mongo/modules/pageSpeedCheckModule.js";
-
-const mockPageSpeedCheck = {
- monitorId: "monitorId",
- bestPractices: 1,
- seo: 1,
- performance: 1,
-};
-
-const mockDeletedResult = { deletedCount: 1 };
-
-describe("pageSpeedCheckModule", function() {
- let pageSpeedCheckSaveStub, pageSpeedCheckDeleteManyStub;
-
- beforeEach(function() {
- pageSpeedCheckSaveStub = sinon.stub(PageSpeedCheck.prototype, "save");
- pageSpeedCheckDeleteManyStub = sinon.stub(PageSpeedCheck, "deleteMany");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createPageSpeedCheck", function() {
- it("should return a page speed check", async function() {
- pageSpeedCheckSaveStub.resolves(mockPageSpeedCheck);
- const pageSpeedCheck = await createPageSpeedCheck(mockPageSpeedCheck);
- expect(pageSpeedCheck).to.deep.equal(mockPageSpeedCheck);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- pageSpeedCheckSaveStub.rejects(err);
- try {
- await expect(createPageSpeedCheck(mockPageSpeedCheck));
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deletePageSpeedChecksByMonitorId", function() {
- it("should return the number of deleted checks", async function() {
- pageSpeedCheckDeleteManyStub.resolves(mockDeletedResult);
- const result = await deletePageSpeedChecksByMonitorId("monitorId");
- expect(result).to.deep.equal(mockDeletedResult.deletedCount);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- pageSpeedCheckDeleteManyStub.rejects(err);
- try {
- await expect(deletePageSpeedChecksByMonitorId("monitorId"));
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-});
diff --git a/Server/tests/db/recoveryModule.test.js b/Server/tests/db/recoveryModule.test.js
deleted file mode 100644
index 9d2978dd3..000000000
--- a/Server/tests/db/recoveryModule.test.js
+++ /dev/null
@@ -1,179 +0,0 @@
-import sinon from "sinon";
-import RecoveryToken from "../../db/models/RecoveryToken.js";
-import User from "../../db/models/User.js";
-import {
- requestRecoveryToken,
- validateRecoveryToken,
- resetPassword,
-} from "../../db/mongo/modules/recoveryModule.js";
-import { errorMessages } from "../../utils/messages.js";
-
-const mockRecoveryToken = {
- email: "test@test.com",
- token: "1234567890",
-};
-
-const mockUser = {
- email: "test@test.com",
- password: "oldPassword",
-};
-
-const mockUserWithoutPassword = {
- email: "test@test.com",
-};
-
-// Create a query builder that logs
-const createQueryChain = (finalResult, comparePasswordResult = false) => ({
- select: () => ({
- select: async () => {
- if (finalResult === mockUser) {
- // Return a new object with all required methods
- return {
- email: "test@test.com",
- password: "oldPassword",
- comparePassword: sinon.stub().resolves(comparePasswordResult),
- save: sinon.stub().resolves(),
- };
- }
- return finalResult;
- },
- }),
- // Add methods to the top level too
- comparePassword: sinon.stub().resolves(comparePasswordResult),
- save: sinon.stub().resolves(),
-});
-
-describe("recoveryModule", function() {
- let deleteManyStub,
- saveStub,
- findOneStub,
- userCompareStub,
- userSaveStub,
- userFindOneStub;
- let req, res;
-
- beforeEach(function() {
- req = {
- body: { email: "test@test.com" },
- };
- deleteManyStub = sinon.stub(RecoveryToken, "deleteMany");
- saveStub = sinon.stub(RecoveryToken.prototype, "save");
- findOneStub = sinon.stub(RecoveryToken, "findOne");
- userCompareStub = sinon.stub(User.prototype, "comparePassword");
- userSaveStub = sinon.stub(User.prototype, "save");
- userFindOneStub = sinon.stub().resolves();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("requestRecoveryToken", function() {
- it("should return a recovery token", async function() {
- deleteManyStub.resolves();
- saveStub.resolves(mockRecoveryToken);
- const result = await requestRecoveryToken(req, res);
- expect(result.email).to.equal(mockRecoveryToken.email);
- });
-
- it("should handle an error", async function() {
- const err = new Error("Test error");
- deleteManyStub.rejects(err);
- try {
- await requestRecoveryToken(req, res);
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("validateRecoveryToken", function() {
- it("should return a recovery token if found", async function() {
- findOneStub.resolves(mockRecoveryToken);
- const result = await validateRecoveryToken(req, res);
- expect(result).to.deep.equal(mockRecoveryToken);
- });
-
- it("should thrown an error if a token is not found", async function() {
- findOneStub.resolves(null);
- try {
- await validateRecoveryToken(req, res);
- } catch (error) {
- expect(error).to.exist;
- expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND);
- }
- });
-
- it("should handle DB errors", async function() {
- const err = new Error("Test error");
- findOneStub.rejects(err);
- try {
- await validateRecoveryToken(req, res);
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("resetPassword", function() {
- beforeEach(function() {
- req.body = {
- password: "test",
- newPassword: "test1",
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should thrown an error if a recovery token is not found", async function() {
- findOneStub.resolves(null);
- try {
- await resetPassword(req, res);
- } catch (error) {
- expect(error).to.exist;
- expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND);
- }
- });
-
- it("should throw an error if a user is not found", async function() {
- findOneStub.resolves(mockRecoveryToken);
- userFindOneStub = sinon.stub(User, "findOne").resolves(null);
- try {
- await resetPassword(req, res);
- } catch (error) {
- expect(error).to.exist;
- expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND);
- }
- });
-
- it("should throw an error if the passwords match", async function() {
- findOneStub.resolves(mockRecoveryToken);
- saveStub.resolves();
- userFindOneStub = sinon
- .stub(User, "findOne")
- .returns(createQueryChain(mockUser, true));
- try {
- await resetPassword(req, res);
- } catch (error) {
- expect(error).to.exist;
- expect(error.message).to.equal(errorMessages.DB_RESET_PASSWORD_BAD_MATCH);
- }
- });
-
- it("should return a user without password if successful", async function() {
- findOneStub.resolves(mockRecoveryToken);
- saveStub.resolves();
- userFindOneStub = sinon
- .stub(User, "findOne")
- .returns(createQueryChain(mockUser)) // First call will resolve to mockUser
- .onSecondCall()
- .returns(createQueryChain(mockUserWithoutPassword));
- const result = await resetPassword(req, res);
- expect(result).to.deep.equal(mockUserWithoutPassword);
- });
- });
-});
diff --git a/Server/tests/db/settingsModule.test.js b/Server/tests/db/settingsModule.test.js
deleted file mode 100644
index b518ba61d..000000000
--- a/Server/tests/db/settingsModule.test.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import sinon from "sinon";
-import {
- getAppSettings,
- updateAppSettings,
-} from "../../db/mongo/modules/settingsModule.js";
-import AppSettings from "../../db/models/AppSettings.js";
-
-const mockAppSettings = {
- appName: "Test App",
-};
-
-describe("SettingsModule", function() {
- let appSettingsFindOneStub, appSettingsFindOneAndUpdateStub;
-
- beforeEach(function() {
- appSettingsFindOneStub = sinon.stub(AppSettings, "findOne");
- appSettingsFindOneAndUpdateStub = sinon.stub(AppSettings, "findOneAndUpdate");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("getAppSettings", function() {
- it("should return app settings", async function() {
- appSettingsFindOneStub.resolves(mockAppSettings);
- const result = await getAppSettings();
- expect(result).to.deep.equal(mockAppSettings);
- });
-
- it("should handle an error", async function() {
- const err = new Error("Test error");
- appSettingsFindOneStub.throws(err);
- try {
- await getAppSettings();
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("updateAppSettings", function() {
- it("should update app settings", async function() {
- appSettingsFindOneAndUpdateStub.resolves(mockAppSettings);
- const result = await updateAppSettings(mockAppSettings);
- expect(result).to.deep.equal(mockAppSettings);
- });
-
- it("should handle an error", async function() {
- const err = new Error("Test error");
- appSettingsFindOneAndUpdateStub.throws(err);
- try {
- await updateAppSettings(mockAppSettings);
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
- });
-});
diff --git a/Server/tests/db/statusPageModule.test.js b/Server/tests/db/statusPageModule.test.js
deleted file mode 100644
index 3d5d5e9d0..000000000
--- a/Server/tests/db/statusPageModule.test.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import sinon from "sinon";
-import {
- createStatusPage,
- getStatusPageByUrl,
-} from "../../db/mongo/modules/statusPageModule.js";
-import StatusPage from "../../db/models/StatusPage.js";
-import { errorMessages } from "../../utils/messages.js";
-
-describe("statusPageModule", function() {
- let statusPageFindOneStub, statusPageSaveStub, statusPageFindStub;
-
- beforeEach(function() {
- statusPageSaveStub = sinon.stub(StatusPage.prototype, "save");
- statusPageFindOneStub = sinon.stub(StatusPage, "findOne");
- statusPageFindStub = sinon.stub(StatusPage, "find");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createStatusPage", function() {
- it("should throw an error if a non-unique url is provided", async function() {
- statusPageFindOneStub.resolves(true);
- try {
- await createStatusPage({ url: "test" });
- } catch (error) {
- expect(error.status).to.equal(400);
- expect(error.message).to.equal(errorMessages.STATUS_PAGE_URL_NOT_UNIQUE);
- }
- });
-
- it("should handle duplicate URL errors", async function() {
- const err = new Error("test");
- err.code = 11000;
- statusPageSaveStub.rejects(err);
- try {
- await createStatusPage({ url: "test" });
- } catch (error) {
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should return a status page if a unique url is provided", async function() {
- statusPageFindOneStub.resolves(null);
- statusPageFindStub.resolves([]);
- const mockStatusPage = { url: "test" };
- const statusPage = await createStatusPage(mockStatusPage);
- expect(statusPage).to.exist;
- expect(statusPage.url).to.equal(mockStatusPage.url);
- });
- });
-
- describe("getStatusPageByUrl", function() {
- it("should throw an error if a status page is not found", async function() {
- statusPageFindOneStub.resolves(null);
- try {
- await getStatusPageByUrl("test");
- } catch (error) {
- expect(error.status).to.equal(404);
- expect(error.message).to.equal(errorMessages.STATUS_PAGE_NOT_FOUND);
- }
- });
-
- it("should return a status page if a status page is found", async function() {
- const mockStatusPage = { url: "test" };
- statusPageFindOneStub.resolves(mockStatusPage);
- const statusPage = await getStatusPageByUrl(mockStatusPage.url);
- expect(statusPage).to.exist;
- expect(statusPage).to.deep.equal(mockStatusPage);
- });
- });
-});
diff --git a/Server/tests/db/userModule.test.js b/Server/tests/db/userModule.test.js
deleted file mode 100644
index b52ba1880..000000000
--- a/Server/tests/db/userModule.test.js
+++ /dev/null
@@ -1,304 +0,0 @@
-import sinon from "sinon";
-import UserModel from "../../db/models/User.js";
-import TeamModel from "../../db/models/Team.js";
-import {
- insertUser,
- getUserByEmail,
- updateUser,
- deleteUser,
- deleteTeam,
- deleteAllOtherUsers,
- getAllUsers,
- logoutUser,
-} from "../../db/mongo/modules/userModule.js";
-import { errorMessages } from "../../utils/messages.js";
-
-const mockUser = {
- email: "test@test.com",
- password: "password",
- role: ["user"],
-};
-const mockSuperUser = {
- email: "test@test.com",
- password: "password",
- role: ["superadmin"],
-};
-const imageFile = {
- image: 1,
-};
-
-describe("userModule", function() {
- let teamSaveStub,
- teamFindByIdAndDeleteStub,
- userSaveStub,
- userFindStub,
- userFindOneStub,
- userFindByIdAndUpdateStub,
- userFindByIdAndDeleteStub,
- userDeleteManyStub,
- userUpdateOneStub,
- generateAvatarImageStub,
- parseBooleanStub;
-
- beforeEach(function() {
- teamSaveStub = sinon.stub(TeamModel.prototype, "save");
- teamFindByIdAndDeleteStub = sinon.stub(TeamModel, "findByIdAndDelete");
- userSaveStub = sinon.stub(UserModel.prototype, "save");
- userFindStub = sinon.stub(UserModel, "find");
- userFindOneStub = sinon.stub(UserModel, "findOne");
- userFindByIdAndUpdateStub = sinon.stub(UserModel, "findByIdAndUpdate");
- userFindByIdAndDeleteStub = sinon.stub(UserModel, "findByIdAndDelete");
- userDeleteManyStub = sinon.stub(UserModel, "deleteMany");
- userUpdateOneStub = sinon.stub(UserModel, "updateOne");
- generateAvatarImageStub = sinon.stub().resolves({ image: 2 });
- parseBooleanStub = sinon.stub().returns(true);
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("insertUser", function() {
- it("should insert a regular user", async function() {
- userSaveStub.resolves(mockUser);
- userFindOneStub.returns({
- select: sinon.stub().returns({
- select: sinon.stub().resolves(mockUser),
- }),
- });
- const result = await insertUser(mockUser, imageFile, generateAvatarImageStub);
- expect(result).to.deep.equal(mockUser);
- });
-
- it("should insert a superadmin user", async function() {
- userSaveStub.resolves(mockSuperUser);
- userFindOneStub.returns({
- select: sinon.stub().returns({
- select: sinon.stub().resolves(mockSuperUser),
- }),
- });
- const result = await insertUser(mockSuperUser, imageFile, generateAvatarImageStub);
- expect(result).to.deep.equal(mockSuperUser);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- userSaveStub.rejects(err);
- try {
- await insertUser(mockUser, imageFile, generateAvatarImageStub);
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
-
- it("should handle a duplicate key error", async function() {
- const err = new Error("test error");
- err.code = 11000;
- userSaveStub.rejects(err);
- try {
- await insertUser(mockUser, imageFile, generateAvatarImageStub);
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getUserByEmail", function() {
- it("should return a user", async function() {
- userFindOneStub.returns({
- select: sinon.stub().resolves(mockUser),
- });
- const result = await getUserByEmail(mockUser.email);
- expect(result).to.deep.equal(mockUser);
- });
- });
-
- describe("getUserByEmail", function() {
- it("throw an error if a user is not found", async function() {
- userFindOneStub.returns({
- select: sinon.stub().resolves(null),
- });
- try {
- await getUserByEmail(mockUser.email);
- } catch (error) {
- expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND);
- }
- });
- });
-
- describe("updateUser", function() {
- let req, res;
-
- beforeEach(function() {
- req = {
- params: {
- userId: "testId",
- },
- body: {
- deleteProfileImage: "false",
- email: "test@test.com",
- },
- file: {
- buffer: "test",
- mimetype: "test",
- },
- };
- res = {};
- });
-
- afterEach(function() {});
-
- it("should update a user", async function() {
- parseBooleanStub.returns(false);
- userFindByIdAndUpdateStub.returns({
- select: sinon.stub().returns({
- select: sinon.stub().resolves(mockUser),
- }),
- });
- const result = await updateUser(
- req,
- res,
- parseBooleanStub,
- generateAvatarImageStub
- );
- expect(result).to.deep.equal(mockUser);
- });
-
- it("should delete a user profile image", async function() {
- req.body.deleteProfileImage = "true";
- userFindByIdAndUpdateStub.returns({
- select: sinon.stub().returns({
- select: sinon.stub().resolves(mockUser),
- }),
- });
- const result = await updateUser(
- req,
- res,
- parseBooleanStub,
- generateAvatarImageStub
- );
- expect(result).to.deep.equal(mockUser);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- userFindByIdAndUpdateStub.throws(err);
- try {
- await updateUser(req, res, parseBooleanStub, generateAvatarImageStub);
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteUser", function() {
- it("should return a deleted user", async function() {
- userFindByIdAndDeleteStub.resolves(mockUser);
- const result = await deleteUser("testId");
- expect(result).to.deep.equal(mockUser);
- });
-
- it("should throw an error if a user is not found", async function() {
- try {
- await deleteUser("testId");
- } catch (error) {
- expect(error).to.exist;
- expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND);
- }
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- userFindByIdAndDeleteStub.throws(err);
- try {
- await deleteUser("testId");
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteTeam", function() {
- it("should return true if team deleted", async function() {
- teamFindByIdAndDeleteStub.resolves();
- const result = await deleteTeam("testId");
- expect(result).to.equal(true);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- teamFindByIdAndDeleteStub.throws(err);
- try {
- await deleteTeam("testId");
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("deleteAllOtherUsers", function() {
- it("should return true if all other users deleted", async function() {
- userDeleteManyStub.resolves(true);
- const result = await deleteAllOtherUsers();
- expect(result).to.equal(true);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- userDeleteManyStub.throws(err);
- try {
- await deleteAllOtherUsers();
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("getAllUsers", function() {
- it("should return all users", async function() {
- userFindStub.returns({
- select: sinon.stub().returns({
- select: sinon.stub().resolves([mockUser]),
- }),
- });
- const result = await getAllUsers();
- expect(result).to.deep.equal([mockUser]);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- userFindStub.throws(err);
- try {
- await getAllUsers();
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-
- describe("logoutUser", function() {
- it("should return true if user logged out", async function() {
- userUpdateOneStub.resolves(true);
- const result = await logoutUser("testId");
- expect(result).to.equal(true);
- });
-
- it("should handle an error", async function() {
- const err = new Error("test error");
- userUpdateOneStub.throws(err);
- try {
- await logoutUser("testId");
- } catch (error) {
- expect(error).to.exist;
- expect(error).to.deep.equal(err);
- }
- });
- });
-});
diff --git a/Server/tests/services/emailService.test.js b/Server/tests/services/emailService.test.js
deleted file mode 100644
index 2584aa1bc..000000000
--- a/Server/tests/services/emailService.test.js
+++ /dev/null
@@ -1,212 +0,0 @@
-import sinon from "sinon";
-import EmailService from "../../service/emailService.js";
-
-describe("EmailService - Constructor", function() {
- let settingsServiceMock;
- let fsMock;
- let pathMock;
- let compileMock;
- let mjml2htmlMock;
- let nodemailerMock;
- let loggerMock;
-
- beforeEach(function() {
- settingsServiceMock = {
- getSettings: sinon.stub().returns({
- systemEmailHost: "smtp.example.com",
- systemEmailPort: 465,
- systemEmailAddress: "test@example.com",
- systemEmailPassword: "password",
- }),
- };
-
- fsMock = {
- readFileSync: sinon.stub().returns(" "),
- };
-
- pathMock = {
- join: sinon.stub().callsFake((...args) => args.join("/")),
- };
-
- compileMock = sinon.stub().returns(() => " ");
-
- mjml2htmlMock = sinon.stub().returns({ html: "" });
-
- nodemailerMock = {
- createTransport: sinon.stub().returns({
- sendMail: sinon.stub().resolves({ messageId: "12345" }),
- }),
- };
-
- loggerMock = {
- error: sinon.stub(),
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should initialize template loaders and email transporter", function() {
- const emailService = new EmailService(
- settingsServiceMock,
- fsMock,
- pathMock,
- compileMock,
- mjml2htmlMock,
- nodemailerMock,
- loggerMock
- );
-
- // Verify that the settingsService is assigned correctly
- expect(emailService.settingsService).to.equal(settingsServiceMock);
-
- // Verify that the template loaders are initialized
- expect(emailService.templateLookup.welcomeEmailTemplate).to.be.a("function");
- expect(emailService.templateLookup.employeeActivationTemplate).to.be.a("function");
-
- // Verify that the email transporter is initialized
- expect(nodemailerMock.createTransport.calledOnce).to.be.true;
- const emailConfig = nodemailerMock.createTransport.getCall(0).args[0];
- expect(emailConfig).to.deep.equal({
- host: "smtp.example.com",
- port: 465,
- secure: true,
- auth: {
- user: "test@example.com",
- pass: "password",
- },
- });
- });
-
- it("should have undefined templates if FS fails", function() {
- fsMock = {
- readFileSync: sinon.stub().throws(new Error("File read error")),
- };
- const emailService = new EmailService(
- settingsServiceMock,
- fsMock,
- pathMock,
- compileMock,
- mjml2htmlMock,
- nodemailerMock,
- loggerMock
- );
- expect(loggerMock.error.called).to.be.true;
- expect(loggerMock.error.firstCall.args[0].message).to.equal("File read error");
- });
-});
-
-describe("EmailService - buildAndSendEmail", function() {
- let settingsServiceMock;
- let fsMock;
- let pathMock;
- let compileMock;
- let mjml2htmlMock;
- let nodemailerMock;
- let loggerMock;
- let emailService;
-
- beforeEach(function() {
- settingsServiceMock = {
- getSettings: sinon.stub().returns({
- systemEmailHost: "smtp.example.com",
- systemEmailPort: 465,
- systemEmailAddress: "test@example.com",
- systemEmailPassword: "password",
- }),
- };
-
- fsMock = {
- readFileSync: sinon.stub().returns(" "),
- };
-
- pathMock = {
- join: sinon.stub().callsFake((...args) => args.join("/")),
- };
-
- compileMock = sinon.stub().returns(() => " ");
-
- mjml2htmlMock = sinon.stub().returns({ html: "" });
-
- nodemailerMock = {
- createTransport: sinon.stub().returns({
- sendMail: sinon.stub().resolves({ messageId: "12345" }),
- }),
- };
-
- loggerMock = {
- error: sinon.stub(),
- };
-
- emailService = new EmailService(
- settingsServiceMock,
- fsMock,
- pathMock,
- compileMock,
- mjml2htmlMock,
- nodemailerMock,
- loggerMock
- );
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should build and send email successfully", async function() {
- const messageId = await emailService.buildAndSendEmail(
- "welcomeEmailTemplate",
- {},
- "recipient@example.com",
- "Welcome"
- );
-
- expect(messageId).to.equal("12345");
- expect(nodemailerMock.createTransport().sendMail.calledOnce).to.be.true;
- });
-
- it("should log error if building HTML fails", async function() {
- mjml2htmlMock.throws(new Error("MJML error"));
-
- const messageId = await emailService.buildAndSendEmail(
- "welcomeEmailTemplate",
- {},
- "recipient@example.com",
- "Welcome"
- );
- expect(loggerMock.error.calledOnce).to.be.true;
- expect(loggerMock.error.getCall(0).args[0].message).to.equal("MJML error");
- });
-
- it("should log error if sending email fails", async function() {
- nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error"));
- await emailService.buildAndSendEmail(
- "welcomeEmailTemplate",
- {},
- "recipient@example.com",
- "Welcome"
- );
- expect(loggerMock.error.calledOnce).to.be.true;
- expect(loggerMock.error.getCall(0).args[0].message).to.equal("SMTP error");
- });
-
- it("should log error if both building HTML and sending email fail", async function() {
- mjml2htmlMock.throws(new Error("MJML error"));
- nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error"));
-
- const messageId = await emailService.buildAndSendEmail(
- "welcomeEmailTemplate",
- {},
- "recipient@example.com",
- "Welcome"
- );
-
- expect(messageId).to.be.undefined;
- expect(loggerMock.error.calledTwice).to.be.true;
- expect(loggerMock.error.getCall(0).args[0].message).to.equal("MJML error");
- expect(loggerMock.error.getCall(1).args[0].message).to.equal("SMTP error");
- });
-
- it("should log an error if buildHtml fails", async function() {});
-});
diff --git a/Server/tests/services/jobQueue.test.js b/Server/tests/services/jobQueue.test.js
deleted file mode 100644
index a7778a56d..000000000
--- a/Server/tests/services/jobQueue.test.js
+++ /dev/null
@@ -1,834 +0,0 @@
-import sinon from "sinon";
-import JobQueue from "../../service/jobQueue.js";
-import { log } from "console";
-
-class QueueStub {
- constructor(queueName, options) {
- this.queueName = queueName;
- this.options = options;
- this.workers = [];
- this.jobs = [];
- }
-
- // Add any methods that are expected to be called on the Queue instance
- add(job) {
- this.jobs.push(job);
- }
-
- removeRepeatable(id) {
- const removedJob = this.jobs.find((job) => job.data._id === id);
- this.jobs = this.jobs.filter((job) => job.data._id !== id);
- if (removedJob) {
- return true;
- }
- return false;
- }
-
- getRepeatableJobs() {
- return this.jobs;
- }
- async getJobs() {
- return this.jobs;
- }
-
- async pause() {
- return true;
- }
-
- async obliterate() {
- return true;
- }
-}
-
-class WorkerStub {
- constructor(QUEUE_NAME, workerTask) {
- this.queueName = QUEUE_NAME;
- this.workerTask = async () => workerTask({ data: { _id: 1 } });
- }
-
- async close() {
- return true;
- }
-}
-
-describe("JobQueue", function() {
- let settingsService,
- logger,
- db,
- networkService,
- statusService,
- notificationService,
- jobQueue;
-
- beforeEach(async function() {
- settingsService = { getSettings: sinon.stub() };
- statusService = { updateStatus: sinon.stub() };
- notificationService = { handleNotifications: sinon.stub() };
-
- logger = { error: sinon.stub(), info: sinon.stub() };
- db = {
- getAllMonitors: sinon.stub().returns([]),
- getMaintenanceWindowsByMonitorId: sinon.stub().returns([]),
- };
- networkService = { getStatus: sinon.stub() };
- jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("createJobQueue", function() {
- it("should create a new JobQueue and add jobs for active monitors", async function() {
- db.getAllMonitors.returns([
- { id: 1, isActive: true },
- { id: 2, isActive: true },
- ]);
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- // There should be double the jobs, as one is meant to be instantly executed
- // And one is meant to be enqueued
- expect(jobQueue.queue.jobs.length).to.equal(4);
- });
-
- it("should reject with an error if an error occurs", async function() {
- db.getAllMonitors.throws("Error");
- try {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- } catch (error) {
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("createJobQueue");
- }
- });
-
- it("should reject with an error if an error occurs, should not overwrite error data", async function() {
- const error = new Error("Error");
- error.service = "otherService";
- error.method = "otherMethod";
- db.getAllMonitors.throws(error);
-
- try {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- } catch (error) {
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-
- describe("Constructor", function() {
- it("should construct a new JobQueue with default port and host if not provided", async function() {
- settingsService.getSettings.returns({});
-
- expect(jobQueue.connection.host).to.equal("127.0.0.1");
- expect(jobQueue.connection.port).to.equal(6379);
- });
-
- it("should construct a new JobQueue with provided port and host", async function() {
- settingsService.getSettings.returns({ redisHost: "localhost", redisPort: 1234 });
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- expect(jobQueue.connection.host).to.equal("localhost");
- expect(jobQueue.connection.port).to.equal(1234);
- });
- });
-
- describe("isMaintenanceWindow", function() {
- it("should throw an error if error occurs", async function() {
- db.getMaintenanceWindowsByMonitorId.throws("Error");
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- try {
- jobQueue.isInMaintenanceWindow(1);
- } catch (error) {
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("createWorker");
- }
- });
-
- it("should return true if in maintenance window with no repeat", async function() {
- db.getMaintenanceWindowsByMonitorId.returns([
- {
- active: true,
- start: new Date(Date.now() - 1000).toISOString(),
- end: new Date(Date.now() + 1000).toISOString(),
- repeat: 0,
- },
- ]);
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- const inWindow = await jobQueue.isInMaintenanceWindow(1);
- expect(inWindow).to.be.true;
- });
-
- it("should return true if in maintenance window with repeat", async function() {
- db.getMaintenanceWindowsByMonitorId.returns([
- {
- active: true,
- start: new Date(Date.now() - 10000).toISOString(),
- end: new Date(Date.now() - 5000).toISOString(),
- repeat: 1000,
- },
- ]);
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- const inWindow = await jobQueue.isInMaintenanceWindow(1);
- expect(inWindow).to.be.true;
- });
-
- it("should return false if in end < start", async function() {
- db.getMaintenanceWindowsByMonitorId.returns([
- {
- active: true,
- start: new Date(Date.now() - 5000).toISOString(),
- end: new Date(Date.now() - 10000).toISOString(),
- repeat: 1000,
- },
- ]);
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- const inWindow = await jobQueue.isInMaintenanceWindow(1);
- expect(inWindow).to.be.false;
- });
-
- it("should return false if not in maintenance window", async function() {
- db.getMaintenanceWindowsByMonitorId.returns([
- {
- active: false,
- start: new Date(Date.now() - 5000).toISOString(),
- end: new Date(Date.now() - 10000).toISOString(),
- repeat: 1000,
- },
- ]);
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- const inWindow = await jobQueue.isInMaintenanceWindow(1);
- expect(inWindow).to.be.false;
- });
- });
-
- describe("createJobHandler", function() {
- it("resolve to an error if an error is thrown within", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.isInMaintenanceWindow = sinon.stub().throws("Error");
- try {
- const handler = jobQueue.createJobHandler();
- await handler({ data: { _id: 1 } });
- } catch (error) {
- expect(error.service).to.equal("JobQueue");
- expect(error.details).to.equal(`Error processing job 1: Error`);
- }
- });
-
- it("should log info if job is in maintenance window", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.isInMaintenanceWindow = sinon.stub().returns(true);
- const handler = jobQueue.createJobHandler();
- await handler({ data: { _id: 1 } });
- expect(logger.info.calledOnce).to.be.true;
- expect(logger.info.firstCall.args[0].message).to.equal(
- "Monitor 1 is in maintenance window"
- );
- });
-
- it("should return if status has not changed", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.isInMaintenanceWindow = sinon.stub().returns(false);
- statusService.updateStatus = sinon.stub().returns({ statusChanged: false });
- const handler = jobQueue.createJobHandler();
- await handler({ data: { _id: 1 } });
- expect(jobQueue.notificationService.handleNotifications.notCalled).to.be.true;
- });
-
- it("should return if status has changed, but prevStatus was undefined (monitor paused)", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.isInMaintenanceWindow = sinon.stub().returns(false);
- statusService.updateStatus = sinon
- .stub()
- .returns({ statusChanged: true, prevStatus: undefined });
- const handler = jobQueue.createJobHandler();
- await handler({ data: { _id: 1 } });
- expect(jobQueue.notificationService.handleNotifications.notCalled).to.be.true;
- });
-
- it("should call notification service if status changed and monitor was not paused", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.isInMaintenanceWindow = sinon.stub().returns(false);
- statusService.updateStatus = sinon
- .stub()
- .returns({ statusChanged: true, prevStatus: false });
- const handler = jobQueue.createJobHandler();
- await handler({ data: { _id: 1 } });
- expect(jobQueue.notificationService.handleNotifications.calledOnce).to.be.true;
- });
- });
-
- describe("getWorkerStats", function() {
- it("should throw an error if getRepeatable Jobs fails", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.queue.getRepeatableJobs = async () => {
- throw new Error("Error");
- };
- try {
- const stats = await jobQueue.getWorkerStats();
- } catch (error) {
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("getWorkerStats");
- }
- });
-
- it("should throw an error if getRepeatable Jobs fails but respect existing error data", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.queue.getRepeatableJobs = async () => {
- const error = new Error("Existing Error");
- error.service = "otherService";
- error.method = "otherMethod";
- throw error;
- };
- try {
- await jobQueue.getWorkerStats();
- } catch (error) {
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-
- describe("scaleWorkers", function() {
- it("should scale workers to 5 if no workers", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- expect(jobQueue.workers.length).to.equal(5);
- });
-
- it("should scale workers up", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- jobQueue.scaleWorkers({
- load: 100,
- jobs: Array.from({ length: 100 }, (_, i) => i + 1),
- });
- expect(jobQueue.workers.length).to.equal(20);
- });
-
- it("should scale workers down, even with error of worker.close fails", async function() {
- WorkerStub.prototype.close = async () => {
- throw new Error("Error");
- };
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- await jobQueue.scaleWorkers({
- load: 100,
- jobs: Array.from({ length: 100 }, (_, i) => i + 1),
- });
-
- const res = await jobQueue.scaleWorkers({
- load: 0,
- jobs: [],
- });
- expect(jobQueue.workers.length).to.equal(5);
- });
-
- it("should scale workers down", async function() {
- WorkerStub.prototype.close = async () => {
- return true;
- };
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- await jobQueue.scaleWorkers({
- load: 40,
- jobs: Array.from({ length: 40 }, (_, i) => i + 1),
- });
-
- const res = await jobQueue.scaleWorkers({
- load: 0,
- jobs: [],
- });
- expect(jobQueue.workers.length).to.equal(5);
- });
-
- it("should return false if scaling doesn't happen", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- const res = await jobQueue.scaleWorkers({ load: 5 });
- expect(jobQueue.workers.length).to.equal(5);
- expect(res).to.be.false;
- });
- });
-
- describe("getJobs", function() {
- it("should return jobs", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- const jobs = await jobQueue.getJobs();
- expect(jobs.length).to.equal(0);
- });
-
- it("should throw an error if getRepeatableJobs fails", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- try {
- jobQueue.queue.getRepeatableJobs = async () => {
- throw new Error("error");
- };
-
- await jobQueue.getJobs(true);
- } catch (error) {
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("getJobs");
- }
- });
-
- it("should throw an error if getRepeatableJobs fails but respect existing error data", async function() {
- const jobQueue = await JobQueue.createJobQueue(
- db,
- networkService,
- statusService,
- notificationService,
- settingsService,
- logger,
- QueueStub,
- WorkerStub
- );
- try {
- jobQueue.queue.getRepeatableJobs = async () => {
- const error = new Error("Existing error");
- error.service = "otherService";
- error.method = "otherMethod";
- throw error;
- };
-
- await jobQueue.getJobs(true);
- } catch (error) {
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-
- describe("getJobStats", function() {
- it("should return job stats for no jobs", async function() {
- const jobStats = await jobQueue.getJobStats();
- expect(jobStats).to.deep.equal({ jobs: [], workers: 5 });
- });
-
- it("should return job stats for jobs", async function() {
- jobQueue.queue.getJobs = async () => {
- return [{ data: { url: "test" }, getState: async () => "completed" }];
- };
- const jobStats = await jobQueue.getJobStats();
- expect(jobStats).to.deep.equal({
- jobs: [{ url: "test", state: "completed" }],
- workers: 5,
- });
- });
-
- it("should reject with an error if mapping jobs fails", async function() {
- jobQueue.queue.getJobs = async () => {
- return [
- {
- data: { url: "test" },
- getState: async () => {
- throw new Error("Mapping Error");
- },
- },
- ];
- };
- try {
- await jobQueue.getJobStats();
- } catch (error) {
- expect(error.message).to.equal("Mapping Error");
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("getJobStats");
- }
- });
-
- it("should reject with an error if mapping jobs fails but respect existing error data", async function() {
- jobQueue.queue.getJobs = async () => {
- return [
- {
- data: { url: "test" },
- getState: async () => {
- const error = new Error("Mapping Error");
- error.service = "otherService";
- error.method = "otherMethod";
- throw error;
- },
- },
- ];
- };
- try {
- await jobQueue.getJobStats();
- } catch (error) {
- expect(error.message).to.equal("Mapping Error");
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-
- describe("addJob", function() {
- it("should add a job to the queue", async function() {
- jobQueue.addJob("test", { url: "test" });
- expect(jobQueue.queue.jobs.length).to.equal(1);
- });
-
- it("should reject with an error if adding fails", async function() {
- jobQueue.queue.add = async () => {
- throw new Error("Error adding job");
- };
- try {
- await jobQueue.addJob("test", { url: "test" });
- } catch (error) {
- expect(error.message).to.equal("Error adding job");
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("addJob");
- }
- });
-
- it("should reject with an error if adding fails but respect existing error data", async function() {
- jobQueue.queue.add = async () => {
- const error = new Error("Error adding job");
- error.service = "otherService";
- error.method = "otherMethod";
- throw error;
- };
- try {
- await jobQueue.addJob("test", { url: "test" });
- } catch (error) {
- expect(error.message).to.equal("Error adding job");
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-
- describe("deleteJob", function() {
- it("should delete a job from the queue", async function() {
- jobQueue.getWorkerStats = sinon.stub().returns({ load: 1, jobs: [{}] });
- jobQueue.scaleWorkers = sinon.stub();
- const monitor = { _id: 1 };
- const job = { data: monitor };
- jobQueue.queue.jobs = [job];
- await jobQueue.deleteJob(monitor);
- // expect(jobQueue.queue.jobs.length).to.equal(0);
- // expect(logger.info.calledOnce).to.be.true;
- // expect(jobQueue.getWorkerStats.calledOnce).to.be.true;
- // expect(jobQueue.scaleWorkers.calledOnce).to.be.true;
- });
-
- it("should log an error if job is not found", async function() {
- jobQueue.getWorkerStats = sinon.stub().returns({ load: 1, jobs: [{}] });
- jobQueue.scaleWorkers = sinon.stub();
- const monitor = { _id: 1 };
- const job = { data: monitor };
- jobQueue.queue.jobs = [job];
- await jobQueue.deleteJob({ id_: 2 });
- expect(logger.error.calledOnce).to.be.true;
- });
-
- it("should reject with an error if removeRepeatable fails", async function() {
- jobQueue.queue.removeRepeatable = async () => {
- const error = new Error("removeRepeatable error");
- throw error;
- };
-
- try {
- await jobQueue.deleteJob({ _id: 1 });
- } catch (error) {
- expect(error.message).to.equal("removeRepeatable error");
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("deleteJob");
- }
- });
-
- it("should reject with an error if removeRepeatable fails but respect existing error data", async function() {
- jobQueue.queue.removeRepeatable = async () => {
- const error = new Error("removeRepeatable error");
- error.service = "otherService";
- error.method = "otherMethod";
- throw error;
- };
-
- try {
- await jobQueue.deleteJob({ _id: 1 });
- } catch (error) {
- expect(error.message).to.equal("removeRepeatable error");
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-
- describe("getMetrics", function() {
- it("should return metrics for the job queue", async function() {
- jobQueue.queue.getWaitingCount = async () => 1;
- jobQueue.queue.getActiveCount = async () => 2;
- jobQueue.queue.getCompletedCount = async () => 3;
- jobQueue.queue.getFailedCount = async () => 4;
- jobQueue.queue.getDelayedCount = async () => 5;
- jobQueue.queue.getRepeatableJobs = async () => [1, 2, 3];
- const metrics = await jobQueue.getMetrics();
- expect(metrics).to.deep.equal({
- waiting: 1,
- active: 2,
- completed: 3,
- failed: 4,
- delayed: 5,
- repeatableJobs: 3,
- });
- });
-
- it("should log an error if metrics operations fail", async function() {
- jobQueue.queue.getWaitingCount = async () => {
- throw new Error("Error");
- };
- await jobQueue.getMetrics();
- expect(logger.error.calledOnce).to.be.true;
- expect(logger.error.firstCall.args[0].message).to.equal("Error");
- });
- });
-
- describe("obliterate", function() {
- it("should return true if obliteration is successful", async function() {
- jobQueue.queue.pause = async () => true;
- jobQueue.getJobs = async () => [{ key: 1, id: 1 }];
- jobQueue.queue.removeRepeatableByKey = async () => true;
- jobQueue.queue.remove = async () => true;
- jobQueue.queue.obliterate = async () => true;
- const obliteration = await jobQueue.obliterate();
- expect(obliteration).to.be.true;
- });
-
- it("should throw an error if obliteration fails", async function() {
- jobQueue.getMetrics = async () => {
- throw new Error("Error");
- };
-
- try {
- await jobQueue.obliterate();
- } catch (error) {
- expect(error.service).to.equal("JobQueue");
- expect(error.method).to.equal("obliterate");
- }
- });
-
- it("should throw an error if obliteration fails but respect existing error data", async function() {
- jobQueue.getMetrics = async () => {
- const error = new Error("Error");
- error.service = "otherService";
- error.method = "otherMethod";
- throw error;
- };
-
- try {
- await jobQueue.obliterate();
- } catch (error) {
- expect(error.service).to.equal("otherService");
- expect(error.method).to.equal("otherMethod");
- }
- });
- });
-});
diff --git a/Server/tests/services/networkService.test.js b/Server/tests/services/networkService.test.js
deleted file mode 100644
index a5a59c938..000000000
--- a/Server/tests/services/networkService.test.js
+++ /dev/null
@@ -1,455 +0,0 @@
-import sinon from "sinon";
-import NetworkService from "../../service/networkService.js";
-import { expect } from "chai";
-import http from "http";
-import { errorMessages } from "../../utils/messages.js";
-describe("Network Service", function() {
- let axios, ping, Docker, logger, networkService;
-
- beforeEach(function() {
- axios = {
- get: sinon.stub().resolves({
- data: { foo: "bar" },
- status: 200,
- }),
- };
- Docker = class {
- listContainers = sinon.stub().resolves([
- {
- Names: ["http://test.com"],
- Id: "http://test.com",
- },
- ]);
- getContainer = sinon.stub().returns({
- inspect: sinon.stub().resolves({ State: { Status: "running" } }),
- });
- };
- ping = {
- promise: {
- probe: sinon
- .stub()
- .resolves({ response: { alive: true }, responseTime: 100, alive: true }),
- },
- };
- logger = { error: sinon.stub() };
- networkService = new NetworkService(axios, ping, logger, http, Docker);
- });
-
- describe("constructor", function() {
- it("should create a new NetworkService instance", function() {
- const networkService = new NetworkService();
- expect(networkService).to.be.an.instanceOf(NetworkService);
- });
- });
-
- describe("timeRequest", function() {
- it("should time an asynchronous operation", async function() {
- const operation = sinon.stub().resolves("success");
- const { response, responseTime } = await networkService.timeRequest(operation);
- expect(response).to.equal("success");
- expect(responseTime).to.be.a("number");
- });
-
- it("should handle errors if operation throws error", async function() {
- const error = new Error("Test error");
- const operation = sinon.stub().throws(error);
- const { response, responseTime } = await networkService.timeRequest(operation);
- expect(response).to.be.null;
- expect(responseTime).to.be.a("number");
- expect(error.message).to.equal("Test error");
- });
- });
-
- describe("requestPing", function() {
- it("should return a response object if ping successful", async function() {
- const pingResult = await networkService.requestPing({
- data: { url: "http://test.com", _id: "123" },
- });
- expect(pingResult.monitorId).to.equal("123");
- expect(pingResult.type).to.equal("ping");
- expect(pingResult.responseTime).to.be.a("number");
- expect(pingResult.status).to.be.true;
- });
-
- it("should return a response object if ping unsuccessful", async function() {
- const error = new Error("Test error");
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const pingResult = await networkService.requestPing({
- data: { url: "http://test.com", _id: "123" },
- });
- expect(pingResult.monitorId).to.equal("123");
- expect(pingResult.type).to.equal("ping");
- expect(pingResult.responseTime).to.be.a("number");
- expect(pingResult.status).to.be.false;
- expect(pingResult.code).to.equal(networkService.PING_ERROR);
- });
-
- it("should throw an error if ping cannot resolve", async function() {
- const error = new Error("test error");
- networkService.timeRequest = sinon.stub().throws(error);
- try {
- await networkService.requestPing({
- data: { url: "http://test.com", _id: "123" },
- });
- } catch (error) {
- expect(error).to.exist;
- expect(error.method).to.equal("requestPing");
- }
- });
- });
-
- describe("requestHttp", function() {
- it("should return a response object if http successful", async function() {
- const job = { data: { url: "http://test.com", _id: "123", type: "http" } };
- const httpResult = await networkService.requestHttp(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("http");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.true;
- });
-
- it("should return a response object if http unsuccessful", async function() {
- const error = new Error("Test error");
- error.response = { status: 404 };
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const job = { data: { url: "http://test.com", _id: "123", type: "http" } };
- const httpResult = await networkService.requestHttp(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("http");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.false;
- expect(httpResult.code).to.equal(404);
- });
-
- it("should return a response object if http unsuccessful with unknown code", async function() {
- const error = new Error("Test error");
- error.response = {};
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const job = { data: { url: "http://test.com", _id: "123", type: "http" } };
- const httpResult = await networkService.requestHttp(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("http");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.false;
- expect(httpResult.code).to.equal(networkService.NETWORK_ERROR);
- });
-
- it("should throw an error if an error occurs", async function() {
- const error = new Error("test error");
- networkService.timeRequest = sinon.stub().throws(error);
- try {
- await networkService.requestHttp({
- data: { url: "http://test.com", _id: "123" },
- });
- } catch (error) {
- expect(error).to.exist;
- expect(error.method).to.equal("requestHttp");
- }
- });
- });
-
- describe("requestPagespeed", function() {
- it("should return a response object if pagespeed successful", async function() {
- const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } };
- const pagespeedResult = await networkService.requestPagespeed(job);
- expect(pagespeedResult.monitorId).to.equal("123");
- expect(pagespeedResult.type).to.equal("pagespeed");
- expect(pagespeedResult.responseTime).to.be.a("number");
- expect(pagespeedResult.status).to.be.true;
- });
-
- it("should return a response object if pagespeed unsuccessful", async function() {
- const error = new Error("Test error");
- error.response = { status: 404 };
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } };
- const pagespeedResult = await networkService.requestPagespeed(job);
- expect(pagespeedResult.monitorId).to.equal("123");
- expect(pagespeedResult.type).to.equal("pagespeed");
- expect(pagespeedResult.responseTime).to.be.a("number");
- expect(pagespeedResult.status).to.be.false;
- expect(pagespeedResult.code).to.equal(404);
- });
-
- it("should return a response object if pagespeed unsuccessful with an unknown code", async function() {
- const error = new Error("Test error");
- error.response = {};
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } };
- const pagespeedResult = await networkService.requestPagespeed(job);
- expect(pagespeedResult.monitorId).to.equal("123");
- expect(pagespeedResult.type).to.equal("pagespeed");
- expect(pagespeedResult.responseTime).to.be.a("number");
- expect(pagespeedResult.status).to.be.false;
- expect(pagespeedResult.code).to.equal(networkService.NETWORK_ERROR);
- });
-
- it("should throw an error if pagespeed cannot resolve", async function() {
- const error = new Error("test error");
- networkService.timeRequest = sinon.stub().throws(error);
- try {
- await networkService.requestPagespeed({
- data: { url: "http://test.com", _id: "123" },
- });
- } catch (error) {
- expect(error).to.exist;
- expect(error.method).to.equal("requestPagespeed");
- }
- });
- });
-
- describe("requestHardware", function() {
- it("should return a response object if hardware successful", async function() {
- const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } };
- const httpResult = await networkService.requestHardware(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("hardware");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.true;
- });
-
- it("should return a response object if hardware successful and job has a secret", async function() {
- const job = {
- data: {
- url: "http://test.com",
- _id: "123",
- type: "hardware",
- secret: "my_secret",
- },
- };
- const httpResult = await networkService.requestHardware(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("hardware");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.true;
- });
-
- it("should return a response object if hardware unsuccessful", async function() {
- const error = new Error("Test error");
- error.response = { status: 404 };
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } };
- const httpResult = await networkService.requestHardware(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("hardware");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.false;
- expect(httpResult.code).to.equal(404);
- });
-
- it("should return a response object if hardware unsuccessful with unknown code", async function() {
- const error = new Error("Test error");
- error.response = {};
- networkService.timeRequest = sinon
- .stub()
- .resolves({ response: null, responseTime: 1, error });
- const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } };
- const httpResult = await networkService.requestHardware(job);
- expect(httpResult.monitorId).to.equal("123");
- expect(httpResult.type).to.equal("hardware");
- expect(httpResult.responseTime).to.be.a("number");
- expect(httpResult.status).to.be.false;
- expect(httpResult.code).to.equal(networkService.NETWORK_ERROR);
- });
-
- it("should throw an error if hardware cannot resolve", async function() {
- const error = new Error("test error");
- networkService.timeRequest = sinon.stub().throws(error);
- try {
- await networkService.requestHardware({
- data: { url: "http://test.com", _id: "123" },
- });
- } catch (error) {
- expect(error).to.exist;
- expect(error.method).to.equal("requestHardware");
- }
- });
- });
-
- describe("requestDocker", function() {
- it("should return a response object if docker successful", async function() {
- const job = { data: { url: "http://test.com", _id: "123", type: "docker" } };
- const dockerResult = await networkService.requestDocker(job);
- expect(dockerResult.monitorId).to.equal("123");
- expect(dockerResult.type).to.equal("docker");
- expect(dockerResult.responseTime).to.be.a("number");
- expect(dockerResult.status).to.be.true;
- });
-
- it("should return a response object with status false if container not running", async function() {
- Docker = class {
- listContainers = sinon.stub().resolves([
- {
- Names: ["/my_container"],
- Id: "abc123",
- },
- ]);
- getContainer = sinon.stub().returns({
- inspect: sinon.stub().resolves({ State: { Status: "stopped" } }),
- });
- };
- networkService = new NetworkService(axios, ping, logger, http, Docker);
- const job = { data: { url: "abc123", _id: "123", type: "docker" } };
- const dockerResult = await networkService.requestDocker(job);
- expect(dockerResult.status).to.be.false;
- expect(dockerResult.code).to.equal(200);
- });
-
- it("should handle an error when fetching the container", async function() {
- Docker = class {
- listContainers = sinon.stub().resolves([
- {
- Names: ["/my_container"],
- Id: "abc123",
- },
- ]);
- getContainer = sinon.stub().returns({
- inspect: sinon.stub().throws(new Error("test error")),
- });
- };
- networkService = new NetworkService(axios, ping, logger, http, Docker);
- const job = { data: { url: "abc123", _id: "123", type: "docker" } };
- const dockerResult = await networkService.requestDocker(job);
- expect(dockerResult.status).to.be.false;
- expect(dockerResult.code).to.equal(networkService.NETWORK_ERROR);
- });
-
- it("should throw an error if operations fail", async function() {
- Docker = class {
- listContainers = sinon.stub().resolves([
- {
- Names: ["/my_container"],
- Id: "abc123",
- },
- ]);
- getContainer = sinon.stub().throws(new Error("test error"));
- };
- networkService = new NetworkService(axios, ping, logger, http, Docker);
- const job = { data: { url: "abc123", _id: "123", type: "docker" } };
- try {
- await networkService.requestDocker(job);
- } catch (error) {
- expect(error.message).to.equal("test error");
- }
- });
-
- it("should throw an error if no matching images found", async function() {
- Docker = class {
- listContainers = sinon.stub().resolves([]);
- getContainer = sinon.stub().throws(new Error("test error"));
- };
- networkService = new NetworkService(axios, ping, logger, http, Docker);
- const job = { data: { url: "abc123", _id: "123", type: "docker" } };
- try {
- await networkService.requestDocker(job);
- } catch (error) {
- expect(error.message).to.equal(errorMessages.DOCKER_NOT_FOUND);
- }
- });
- });
-
- describe("getStatus", function() {
- beforeEach(function() {
- networkService.requestPing = sinon.stub();
- networkService.requestHttp = sinon.stub();
- networkService.requestPagespeed = sinon.stub();
- networkService.requestHardware = sinon.stub();
- networkService.requestDocker = sinon.stub();
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should call requestPing if type is ping", async function() {
- await networkService.getStatus({ data: { type: "ping" } });
- expect(networkService.requestPing.calledOnce).to.be.true;
- expect(networkService.requestDocker.notCalled).to.be.true;
- expect(networkService.requestHttp.notCalled).to.be.true;
- expect(networkService.requestPagespeed.notCalled).to.be.true;
- });
-
- it("should call requestHttp if type is http", async function() {
- await networkService.getStatus({ data: { type: "http" } });
- expect(networkService.requestPing.notCalled).to.be.true;
- expect(networkService.requestDocker.notCalled).to.be.true;
- expect(networkService.requestHttp.calledOnce).to.be.true;
- expect(networkService.requestPagespeed.notCalled).to.be.true;
- });
-
- it("should call requestPagespeed if type is pagespeed", async function() {
- await networkService.getStatus({ data: { type: "pagespeed" } });
- expect(networkService.requestPing.notCalled).to.be.true;
- expect(networkService.requestDocker.notCalled).to.be.true;
- expect(networkService.requestHttp.notCalled).to.be.true;
- expect(networkService.requestPagespeed.calledOnce).to.be.true;
- });
-
- it("should call requestHardware if type is hardware", async function() {
- await networkService.getStatus({ data: { type: "hardware" } });
- expect(networkService.requestHardware.calledOnce).to.be.true;
- expect(networkService.requestDocker.notCalled).to.be.true;
- expect(networkService.requestPing.notCalled).to.be.true;
- expect(networkService.requestPagespeed.notCalled).to.be.true;
- });
-
- it("should call requestDocker if type is Docker", async function() {
- await networkService.getStatus({ data: { type: "docker" } });
- expect(networkService.requestDocker.calledOnce).to.be.true;
- expect(networkService.requestHardware.notCalled).to.be.true;
- expect(networkService.requestPing.notCalled).to.be.true;
- expect(networkService.requestPagespeed.notCalled).to.be.true;
- });
-
- it("should throw an error if an unknown type is provided", async function() {
- try {
- await networkService.getStatus({ data: { type: "unknown" } });
- } catch (error) {
- expect(error.service).to.equal("NetworkService");
- expect(error.method).to.equal("getStatus");
- expect(error.message).to.equal("Unsupported type: unknown");
- }
- });
-
- it("should throw an error if job type is undefined", async function() {
- try {
- await networkService.getStatus({ data: { type: undefined } });
- } catch (error) {
- expect(error.service).to.equal("NetworkService");
- expect(error.method).to.equal("getStatus");
- expect(error.message).to.equal("Unsupported type: unknown");
- }
- });
-
- it("should throw an error if job is empty", async function() {
- try {
- await networkService.getStatus({});
- } catch (error) {
- expect(error.method).to.equal("getStatus");
- expect(error.message).to.equal("Unsupported type: unknown");
- }
- });
-
- it("should throw an error if job is null", async function() {
- try {
- await networkService.getStatus(null);
- } catch (error) {
- expect(error.service).to.equal("NetworkService");
- expect(error.method).to.equal("getStatus");
- expect(error.message).to.equal("Unsupported type: unknown");
- }
- });
- });
-});
diff --git a/Server/tests/services/notificationService.test.js b/Server/tests/services/notificationService.test.js
deleted file mode 100644
index e6220b20c..000000000
--- a/Server/tests/services/notificationService.test.js
+++ /dev/null
@@ -1,302 +0,0 @@
-import sinon from "sinon";
-import NotificationService from "../../service/notificationService.js";
-import { expect } from "chai";
-
-describe("NotificationService", function() {
- let emailService, db, logger, notificationService;
-
- beforeEach(function() {
- db = {
- getNotificationsByMonitorId: sinon.stub(),
- };
- emailService = {
- buildAndSendEmail: sinon.stub(),
- };
- logger = {
- warn: sinon.stub(),
- };
-
- notificationService = new NotificationService(emailService, db, logger);
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("constructor", function() {
- it("should create a new instance of NotificationService", function() {
- expect(notificationService).to.be.an.instanceOf(NotificationService);
- });
- });
-
- describe("sendEmail", function() {
- it("should send an email notification with Up Template", async function() {
- const networkResponse = {
- monitor: {
- name: "Test Monitor",
- url: "http://test.com",
- },
- status: true,
- prevStatus: false,
- };
- const address = "test@test.com";
- await notificationService.sendEmail(networkResponse, address);
- expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true;
- expect(
- notificationService.emailService.buildAndSendEmail.calledWith(
- "serverIsUpTemplate",
- { monitor: "Test Monitor", url: "http://test.com" }
- )
- );
- });
-
- it("should send an email notification with Down Template", async function() {
- const networkResponse = {
- monitor: {
- name: "Test Monitor",
- url: "http://test.com",
- },
- status: false,
- prevStatus: true,
- };
- const address = "test@test.com";
- await notificationService.sendEmail(networkResponse, address);
- expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true;
- });
-
- it("should send an email notification with Up Template", async function() {
- const networkResponse = {
- monitor: {
- name: "Test Monitor",
- url: "http://test.com",
- },
- status: true,
- prevStatus: false,
- };
- const address = "test@test.com";
- await notificationService.sendEmail(networkResponse, address);
- expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true;
- });
- });
-
- describe("handleNotifications", function() {
- it("should handle notifications based on the network response", async function() {
- notificationService.sendEmail = sinon.stub();
- const res = await notificationService.handleNotifications({
- monitor: {
- type: "email",
- address: "www.google.com",
- },
- });
- expect(res).to.be.true;
- });
-
- it("should handle hardware notifications", async function() {
- notificationService.sendEmail = sinon.stub();
- const res = await notificationService.handleNotifications({
- monitor: {
- type: "hardware",
- address: "www.google.com",
- },
- });
- expect(res).to.be.true;
- });
-
- it("should handle an error when getting notifications", async function() {
- const testError = new Error("Test Error");
- notificationService.db.getNotificationsByMonitorId.rejects(testError);
- await notificationService.handleNotifications({ monitorId: "123" });
- expect(notificationService.logger.warn.calledOnce).to.be.true;
- });
- });
-
- describe("sendHardwareEmail", function() {
- let networkResponse, address, alerts;
-
- beforeEach(function() {
- networkResponse = {
- monitor: {
- name: "Test Monitor",
- url: "http://test.com",
- },
- status: true,
- prevStatus: false,
- };
- address = "test@test.com";
- alerts = ["test"];
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should send an email notification with Hardware Template", async function() {
- emailService.buildAndSendEmail.resolves(true);
- const res = await notificationService.sendHardwareEmail(
- networkResponse,
- address,
- alerts
- );
- expect(res).to.be.true;
- });
-
- it("should return false if no alerts are provided", async function() {
- alerts = [];
- emailService.buildAndSendEmail.resolves(true);
- const res = await notificationService.sendHardwareEmail(
- networkResponse,
- address,
- alerts
- );
- expect(res).to.be.false;
- });
- });
-
- describe("handleStatusNotifications", function() {
- let networkResponse;
-
- beforeEach(function() {
- networkResponse = {
- monitor: {
- name: "Test Monitor",
- url: "http://test.com",
- },
- statusChanged: true,
- status: true,
- prevStatus: false,
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- it("should handle status notifications", async function() {
- db.getNotificationsByMonitorId.resolves([
- { type: "email", address: "test@test.com" },
- ]);
- const res = await notificationService.handleStatusNotifications(networkResponse);
- expect(res).to.be.true;
- });
-
- it("should return false if status hasn't changed", async function() {
- networkResponse.statusChanged = false;
- const res = await notificationService.handleStatusNotifications(networkResponse);
- expect(res).to.be.false;
- });
-
- it("should return false if prevStatus is undefined", async function() {
- networkResponse.prevStatus = undefined;
- const res = await notificationService.handleStatusNotifications(networkResponse);
- expect(res).to.be.false;
- });
-
- it("should handle an error", async function() {
- const testError = new Error("Test Error");
- db.getNotificationsByMonitorId.rejects(testError);
- try {
- await notificationService.handleStatusNotifications(networkResponse);
- } catch (error) {
- expect(error).to.be.an.instanceOf(Error);
- expect(error.message).to.equal("Test Error");
- }
- });
- });
-
- describe("handleHardwareNotifications", function() {
- let networkResponse;
-
- beforeEach(function() {
- networkResponse = {
- monitor: {
- name: "Test Monitor",
- url: "http://test.com",
- thresholds: {
- usage_cpu: 1,
- usage_memory: 1,
- usage_disk: 1,
- },
- },
- payload: {
- data: {
- cpu: {
- usage_percent: 0.655,
- },
- memory: {
- usage_percent: 0.783,
- },
- disk: [
- {
- name: "/dev/sda1",
- usage_percent: 0.452,
- },
- {
- name: "/dev/sdb1",
- usage_percent: 0.627,
- },
- ],
- },
- },
- };
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("it should return false if no thresholds are set", function() {
- it("should return false if no thresholds are set", async function() {
- networkResponse.monitor.thresholds = undefined;
- const res =
- await notificationService.handleHardwareNotifications(networkResponse);
- expect(res).to.be.false;
- });
-
- it("should return false if metrics are null", async function() {
- networkResponse.payload.data = null;
- const res =
- await notificationService.handleHardwareNotifications(networkResponse);
- expect(res).to.be.false;
- });
-
- it("should return true if request is well formed and thresholds > 0", async function() {
- db.getNotificationsByMonitorId.resolves([
- {
- type: "email",
- address: "test@test.com",
- alertThreshold: 1,
- cpuAlertThreshold: 1,
- memoryAlertThreshold: 1,
- diskAlertThreshold: 1,
- save: sinon.stub().resolves(),
- },
- ]);
- const res =
- await notificationService.handleHardwareNotifications(networkResponse);
- expect(res).to.be.true;
- });
-
- it("should return true if thresholds are exceeded", async function() {
- db.getNotificationsByMonitorId.resolves([
- {
- type: "email",
- address: "test@test.com",
- alertThreshold: 1,
- cpuAlertThreshold: 1,
- memoryAlertThreshold: 1,
- diskAlertThreshold: 1,
- save: sinon.stub().resolves(),
- },
- ]);
- networkResponse.monitor.thresholds = {
- usage_cpu: 0.01,
- usage_memory: 0.01,
- usage_disk: 0.01,
- };
- const res =
- await notificationService.handleHardwareNotifications(networkResponse);
- expect(res).to.be.true;
- });
- });
- });
-});
diff --git a/Server/tests/services/settingsService.test.js b/Server/tests/services/settingsService.test.js
deleted file mode 100644
index 94f7737bc..000000000
--- a/Server/tests/services/settingsService.test.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import sinon from "sinon";
-import SettingsService from "../../service/settingsService.js";
-import { expect } from "chai";
-import NetworkService from "../../service/networkService.js";
-const SERVICE_NAME = "SettingsService";
-
-describe("SettingsService", function() {
- let sandbox, mockAppSettings;
-
- beforeEach(function () {
- sandbox = sinon.createSandbox();
- sandbox.stub(process.env, "CLIENT_HOST").value("http://localhost");
- sandbox.stub(process.env, "JWT_SECRET").value("secret");
- sandbox.stub(process.env, "REFRESH_TOKEN_SECRET").value("refreshSecret");
- sandbox.stub(process.env, "DB_TYPE").value("postgres");
- sandbox
- .stub(process.env, "DB_CONNECTION_STRING")
- .value("postgres://user:pass@localhost/db");
- sandbox.stub(process.env, "REDIS_HOST").value("localhost");
- sandbox.stub(process.env, "REDIS_PORT").value("6379");
- sandbox.stub(process.env, "TOKEN_TTL").value("3600");
- sandbox.stub(process.env, "REFRESH_TOKEN_TTL").value("86400");
- sandbox.stub(process.env, "PAGESPEED_API_KEY").value("apiKey");
- sandbox.stub(process.env, "SYSTEM_EMAIL_HOST").value("smtp.mailtrap.io");
- sandbox.stub(process.env, "SYSTEM_EMAIL_PORT").value("2525");
- sandbox.stub(process.env, "SYSTEM_EMAIL_ADDRESS").value("test@example.com");
- sandbox.stub(process.env, "SYSTEM_EMAIL_PASSWORD").value("password");
- });
-
- mockAppSettings = {
- settingOne: 123,
- settingTwo: 456,
- };
-
- afterEach(function () {
- sandbox.restore();
- sinon.restore();
- });
-
- describe("constructor", function() {
- it("should construct a new SettingsService", function() {
- const settingsService = new SettingsService(mockAppSettings);
- expect(settingsService.appSettings).to.equal(mockAppSettings);
- });
- });
-
- describe("loadSettings", function() {
- it("should load settings from DB when environment variables are not set", async function() {
- const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" };
- const appSettings = { findOne: sinon.stub().returns(dbSettings) };
- const settingsService = new SettingsService(appSettings);
- settingsService.settings = {};
- const result = await settingsService.loadSettings();
- expect(result).to.deep.equal(dbSettings);
- });
-
- it("should throw an error if settings are not found", async function () {
- const appSettings = { findOne: sinon.stub().returns(null) };
- const settingsService = new SettingsService(appSettings);
- settingsService.settings = null;
-
- try {
- await settingsService.loadSettings();
- } catch (error) {
- expect(error.message).to.equal("Settings not found");
- expect(error.service).to.equal(SERVICE_NAME);
- expect(error.method).to.equal("loadSettings");
- }
- });
-
- it("should add its method and service name to error if not present", async function() {
- const appSettings = { findOne: sinon.stub().throws(new Error("Test error")) };
- const settingsService = new SettingsService(appSettings);
- try {
- await settingsService.loadSettings();
- } catch (error) {
- expect(error.message).to.equal("Test error");
- expect(error.service).to.equal(SERVICE_NAME);
- expect(error.method).to.equal("loadSettings");
- }
- });
-
- it("should not add its method and service name to error if present", async function() {
- const error = new Error("Test error");
- error.method = "otherMethod";
- error.service = "OTHER_SERVICE";
- const appSettings = { findOne: sinon.stub().throws(error) };
- const settingsService = new SettingsService(appSettings);
- try {
- await settingsService.loadSettings();
- } catch (error) {
- expect(error.message).to.equal("Test error");
- expect(error.service).to.equal("OTHER_SERVICE");
- expect(error.method).to.equal("otherMethod");
- }
- });
-
- it("should merge DB settings with environment variables", async function () {
- const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" };
- const appSettings = { findOne: sinon.stub().returns(dbSettings) };
- const settingsService = new SettingsService(appSettings);
- const result = await settingsService.loadSettings();
- expect(result).to.deep.equal(settingsService.settings);
- expect(settingsService.settings.logLevel).to.equal("debug");
- expect(settingsService.settings.apiBaseUrl).to.equal("http://localhost");
- });
- });
-
- describe("reloadSettings", function() {
- it("should call loadSettings", async function() {
- const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" };
- const appSettings = { findOne: sinon.stub().returns(dbSettings) };
- const settingsService = new SettingsService(appSettings);
- settingsService.settings = {};
- const result = await settingsService.reloadSettings();
- expect(result).to.deep.equal(dbSettings);
- });
- });
-
- describe("getSettings", function() {
- it("should return the current settings", function() {
- const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" };
- const appSettings = { findOne: sinon.stub().returns(dbSettings) };
- const settingsService = new SettingsService(appSettings);
- settingsService.settings = dbSettings;
- const result = settingsService.getSettings();
- expect(result).to.deep.equal(dbSettings);
- });
-
- it("should throw an error if settings have not been loaded", function() {
- const appSettings = { findOne: sinon.stub().returns(null) };
- const settingsService = new SettingsService(appSettings);
- settingsService.settings = null;
-
- try {
- settingsService.getSettings();
- } catch (error) {
- expect(error.message).to.equal("Settings have not been loaded");
- }
- });
- });
-});
diff --git a/Server/tests/services/statusService.test.js b/Server/tests/services/statusService.test.js
deleted file mode 100644
index f7473e37e..000000000
--- a/Server/tests/services/statusService.test.js
+++ /dev/null
@@ -1,259 +0,0 @@
-import sinon from "sinon";
-import StatusService from "../../service/statusService.js";
-import { afterEach, describe } from "node:test";
-
-describe("StatusService", () => {
- let db, logger, statusService;
-
- beforeEach(function() {
- db = {
- getMonitorById: sinon.stub(),
- createCheck: sinon.stub(),
- createPagespeedCheck: sinon.stub(),
- };
- logger = {
- info: sinon.stub(),
- error: sinon.stub(),
- };
- statusService = new StatusService(db, logger);
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- describe("constructor", () => {
- it("should create an instance of StatusService", function() {
- expect(statusService).to.be.an.instanceOf(StatusService);
- });
- });
-
- describe("getStatusString", () => {
- it("should return 'up' if status is true", function() {
- expect(statusService.getStatusString(true)).to.equal("up");
- });
-
- it("should return 'down' if status is false", function() {
- expect(statusService.getStatusString(false)).to.equal("down");
- });
-
- it("should return 'unknown' if status is undefined or null", function() {
- expect(statusService.getStatusString(undefined)).to.equal("unknown");
- });
- });
-
- describe("updateStatus", () => {
- beforeEach(function() {
- // statusService.insertCheck = sinon.stub().resolves;
- });
-
- afterEach(() => {
- sinon.restore();
- });
-
- it("should throw an error if an error occurs", async function() {
- const error = new Error("Test error");
- statusService.db.getMonitorById = sinon.stub().throws(error);
- try {
- await statusService.updateStatus({ monitorId: "test", status: true });
- } catch (error) {
- expect(error.message).to.equal("Test error");
- }
- // expect(statusService.insertCheck.calledOnce).to.be.true;
- });
-
- it("should return {statusChanged: false} if status hasn't changed", async function() {
- statusService.db.getMonitorById = sinon.stub().returns({ status: true });
- const result = await statusService.updateStatus({
- monitorId: "test",
- status: true,
- });
- expect(result).to.deep.equal({ statusChanged: false });
- // expect(statusService.insertCheck.calledOnce).to.be.true;
- });
-
- it("should return {statusChanged: true} if status has changed from down to up", async function() {
- statusService.db.getMonitorById = sinon
- .stub()
- .returns({ status: false, save: sinon.stub() });
- const result = await statusService.updateStatus({
- monitorId: "test",
- status: true,
- });
- expect(result.statusChanged).to.be.true;
- expect(result.monitor.status).to.be.true;
- expect(result.prevStatus).to.be.false;
- // expect(statusService.insertCheck.calledOnce).to.be.true;
- });
-
- it("should return {statusChanged: true} if status has changed from up to down", async function() {
- statusService.db.getMonitorById = sinon
- .stub()
- .returns({ status: true, save: sinon.stub() });
- const result = await statusService.updateStatus({
- monitorId: "test",
- status: false,
- });
- expect(result.statusChanged).to.be.true;
- expect(result.monitor.status).to.be.false;
- expect(result.prevStatus).to.be.true;
- // expect(statusService.insertCheck.calledOnce).to.be.true;
- });
- });
-
- describe("buildCheck", () => {
- it("should build a check object", function() {
- const check = statusService.buildCheck({
- monitorId: "test",
- type: "test",
- status: true,
- responseTime: 100,
- code: 200,
- message: "Test message",
- payload: { test: "test" },
- });
- expect(check.monitorId).to.equal("test");
- expect(check.status).to.be.true;
- expect(check.statusCode).to.equal(200);
- expect(check.responseTime).to.equal(100);
- expect(check.message).to.equal("Test message");
- });
-
- it("should build a check object for pagespeed type", function() {
- const check = statusService.buildCheck({
- monitorId: "test",
- type: "pagespeed",
- status: true,
- responseTime: 100,
- code: 200,
- message: "Test message",
- payload: {
- lighthouseResult: {
- categories: {
- accessibility: { score: 1 },
- "best-practices": { score: 1 },
- performance: { score: 1 },
- seo: { score: 1 },
- },
- audits: {
- "cumulative-layout-shift": { score: 1 },
- "speed-index": { score: 1 },
- "first-contentful-paint": { score: 1 },
- "largest-contentful-paint": { score: 1 },
- "total-blocking-time": { score: 1 },
- },
- },
- },
- });
- expect(check.monitorId).to.equal("test");
- expect(check.status).to.be.true;
- expect(check.statusCode).to.equal(200);
- expect(check.responseTime).to.equal(100);
- expect(check.message).to.equal("Test message");
- expect(check.accessibility).to.equal(100);
- expect(check.bestPractices).to.equal(100);
- expect(check.performance).to.equal(100);
- expect(check.seo).to.equal(100);
- expect(check.audits).to.deep.equal({
- cls: { score: 1 },
- si: { score: 1 },
- fcp: { score: 1 },
- lcp: { score: 1 },
- tbt: { score: 1 },
- });
- });
-
- it("should build a check object for pagespeed type with missing data", function() {
- const check = statusService.buildCheck({
- monitorId: "test",
- type: "pagespeed",
- status: true,
- responseTime: 100,
- code: 200,
- message: "Test message",
- payload: {
- lighthouseResult: {
- categories: {},
- audits: {},
- },
- },
- });
- expect(check.monitorId).to.equal("test");
- expect(check.status).to.be.true;
- expect(check.statusCode).to.equal(200);
- expect(check.responseTime).to.equal(100);
- expect(check.message).to.equal("Test message");
- expect(check.accessibility).to.equal(0);
- expect(check.bestPractices).to.equal(0);
- expect(check.performance).to.equal(0);
- expect(check.seo).to.equal(0);
- expect(check.audits).to.deep.equal({
- cls: 0,
- si: 0,
- fcp: 0,
- lcp: 0,
- tbt: 0,
- });
- });
-
- it("should build a check for hardware type", function() {
- const check = statusService.buildCheck({
- monitorId: "test",
- type: "hardware",
- status: true,
- responseTime: 100,
- code: 200,
- message: "Test message",
- payload: { data: { cpu: "cpu", memory: "memory", disk: "disk", host: "host" } },
- });
- expect(check.monitorId).to.equal("test");
- expect(check.status).to.be.true;
- expect(check.statusCode).to.equal(200);
- expect(check.responseTime).to.equal(100);
- expect(check.message).to.equal("Test message");
- expect(check.cpu).to.equal("cpu");
- expect(check.memory).to.equal("memory");
- expect(check.disk).to.equal("disk");
- expect(check.host).to.equal("host");
- });
-
- it("should build a check for hardware type with missing data", function() {
- const check = statusService.buildCheck({
- monitorId: "test",
- type: "hardware",
- status: true,
- responseTime: 100,
- code: 200,
- message: "Test message",
- payload: {},
- });
- expect(check.monitorId).to.equal("test");
- expect(check.status).to.be.true;
- expect(check.statusCode).to.equal(200);
- expect(check.responseTime).to.equal(100);
- expect(check.message).to.equal("Test message");
- expect(check.cpu).to.deep.equal({});
- expect(check.memory).to.deep.equal({});
- expect(check.disk).to.deep.equal({});
- expect(check.host).to.deep.equal({});
- });
- });
-
- describe("insertCheck", () => {
- it("should log an error if one is thrown", async function() {
- const testError = new Error("Test error");
- statusService.db.createCheck = sinon.stub().throws(testError);
- try {
- await statusService.insertCheck({ monitorId: "test" });
- } catch (error) {
- expect(error.message).to.equal(testError.message);
- }
- expect(statusService.logger.error.calledOnce).to.be.true;
- });
-
- it("should insert a check into the database", async function() {
- await statusService.insertCheck({ monitorId: "test", type: "http" });
- expect(statusService.db.createCheck.calledOnce).to.be.true;
- });
- });
-});
diff --git a/Server/tests/utils/dataUtils.test.js b/Server/tests/utils/dataUtils.test.js
deleted file mode 100644
index 0b73a5f95..000000000
--- a/Server/tests/utils/dataUtils.test.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import { NormalizeData, calculatePercentile } from "../../utils/dataUtils.js";
-import sinon from "sinon";
-
-describe("NormalizeData", function() {
- it("should normalize response times when checks length is greater than 1", function() {
- const checks = [
- { responseTime: 20, _doc: { id: 1 } },
- { responseTime: 40, _doc: { id: 2 } },
- { responseTime: 60, _doc: { id: 3 } },
- ];
- const rangeMin = 1;
- const rangeMax = 100;
-
- const result = NormalizeData(checks, rangeMin, rangeMax);
-
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(3);
- result.forEach((check) => {
- expect(check).to.have.property("responseTime").that.is.a("number");
- expect(check).to.have.property("originalResponseTime").that.is.a("number");
- });
- });
-
- it("should return checks with original response times when checks length is 1", function() {
- const checks = [{ responseTime: 20, _doc: { id: 1 } }];
- const rangeMin = 1;
- const rangeMax = 100;
-
- const result = NormalizeData(checks, rangeMin, rangeMax);
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(1);
- expect(result[0]).to.have.property("originalResponseTime", 20);
- });
-
- it("should handle edge cases with extreme response times", function() {
- const checks = [
- { responseTime: 5, _doc: { id: 1 } },
- { responseTime: 95, _doc: { id: 2 } },
- ];
- const rangeMin = 1;
- const rangeMax = 100;
-
- const result = NormalizeData(checks, rangeMin, rangeMax);
-
- expect(result).to.be.an("array");
- expect(result).to.have.lengthOf(2);
- expect(result[0]).to.have.property("responseTime").that.is.at.least(rangeMin);
- expect(result[1]).to.have.property("responseTime").that.is.at.most(rangeMax);
- });
-});
-
-describe("calculatePercentile", function() {
- it("should return the lower value when upper is greater than or equal to the length of the sorted array", function() {
- const checks = [
- { responseTime: 10 },
- { responseTime: 20 },
- { responseTime: 30 },
- { responseTime: 40 },
- { responseTime: 50 },
- ];
-
- const percentile = 100;
- const result = calculatePercentile(checks, percentile);
- const expected = 50;
- expect(result).to.equal(expected);
- });
-});
diff --git a/Server/tests/utils/imageProcessing.test.js b/Server/tests/utils/imageProcessing.test.js
deleted file mode 100644
index 7a779c93d..000000000
--- a/Server/tests/utils/imageProcessing.test.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { expect } from "chai";
-import sinon from "sinon";
-import sharp from "sharp";
-import { GenerateAvatarImage } from "../../utils/imageProcessing.js";
-
-describe("imageProcessing - GenerateAvatarImage", function() {
- it("should resize the image to 64x64 and return a base64 string", async function() {
- const file = {
- buffer: Buffer.from("test image buffer"),
- };
-
- // Stub the sharp function
- const toBufferStub = sinon.stub().resolves(Buffer.from("resized image buffer"));
- const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub });
- const sharpStub = sinon
- .stub(sharp.prototype, "resize")
- .returns({ toBuffer: toBufferStub });
-
- const result = await GenerateAvatarImage(file);
-
- // Verify the result
- const expected = Buffer.from("resized image buffer").toString("base64");
- expect(result).to.equal(expected);
-
- // Verify that the sharp function was called with the correct arguments
- expect(sharpStub.calledOnceWith({ width: 64, height: 64, fit: "cover" })).to.be.true;
- expect(toBufferStub.calledOnce).to.be.true;
-
- // Restore the stubbed functions
- sharpStub.restore();
- });
-
- it("should throw an error if resizing fails", async function() {
- const file = {
- buffer: Buffer.from("test image buffer"),
- };
-
- // Stub the sharp function to throw an error
- const toBufferStub = sinon.stub().rejects(new Error("Resizing failed"));
- const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub });
- const sharpStub = sinon
- .stub(sharp.prototype, "resize")
- .returns({ toBuffer: toBufferStub });
-
- try {
- await GenerateAvatarImage(file);
- // If no error is thrown, fail the test
- expect.fail("Expected error to be thrown");
- } catch (error) {
- // Verify that the error message is correct
- expect(error.message).to.equal("Resizing failed");
- }
-
- // Restore the stubbed functions
- sharpStub.restore();
- });
-});
diff --git a/Server/tests/utils/logger.test.js b/Server/tests/utils/logger.test.js
deleted file mode 100644
index 8030ff8fb..000000000
--- a/Server/tests/utils/logger.test.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import sinon from "sinon";
-import logger from "../../utils/logger.js";
-import { Logger } from "../../utils/logger.js";
-import winston from "winston";
-
-describe("Logger", function() {
- let infoStub, warnStub, errorStub;
-
- beforeEach(function() {
- infoStub = sinon.stub(logger.logger, "info");
- warnStub = sinon.stub(logger.logger, "warn");
- errorStub = sinon.stub(logger.logger, "error");
- });
-
- afterEach(function() {
- sinon.restore();
- });
-
- describe("constructor", function() {
- let createLoggerStub;
-
- beforeEach(function () {
- createLoggerStub = sinon.stub(winston, "createLogger");
- });
-
- afterEach(function () {
- sinon.restore();
- });
-
- it("should convert message to JSON string if it is an object", function () {
- const logMessage = { key: "value" };
- const expectedMessage = JSON.stringify(logMessage, null, 2);
-
- createLoggerStub.callsFake((config) => {
- const consoleTransport = config.transports[0];
- const logEntry = {
- level: "info",
- message: logMessage,
- timestamp: new Date().toISOString(),
- };
- const formattedMessage = consoleTransport.format.transform(logEntry);
- expect(formattedMessage).to.include(expectedMessage);
- return { log: sinon.spy() };
- });
-
- const logger = new Logger();
- logger.logger.info(logMessage);
- });
-
- it("should convert details to JSON string if it is an object", function () {
- const logDetails = { key: "value" };
- const expectedDetails = JSON.stringify(logDetails, null, 2); // Removed .s
-
- createLoggerStub.callsFake((config) => {
- const consoleTransport = config.transports[0];
- const logEntry = {
- level: "info",
- message: "", // Add empty message since it's required
- details: logDetails,
- timestamp: new Date().toISOString(),
- };
- const formattedMessage = consoleTransport.format.transform(logEntry);
- expect(formattedMessage).to.include(expectedDetails);
- return { info: sinon.spy() }; // Changed to return info method
- });
-
- const logger = new Logger();
- logger.logger.info("", { details: logDetails }); // Updated to pass details properly
- });
- });
-
- describe("info", function() {
- it("should log an informational message", function() {
- const config = {
- message: "Info message",
- service: "TestService",
- method: "TestMethod",
- details: { key: "value" },
- };
-
- logger.info(config);
-
- expect(infoStub.calledOnce).to.be.true;
- expect(
- infoStub.calledWith(config.message, {
- service: config.service,
- method: config.method,
- details: config.details,
- })
- ).to.be.true;
- });
- });
-
- describe("warn", function() {
- it("should log a warning message", function() {
- const config = {
- message: "Warning message",
- service: "TestService",
- method: "TestMethod",
- details: { key: "value" },
- };
-
- logger.warn(config);
-
- expect(warnStub.calledOnce).to.be.true;
- expect(
- warnStub.calledWith(config.message, {
- service: config.service,
- method: config.method,
- details: config.details,
- })
- ).to.be.true;
- });
- });
-
- describe("error", function() {
- it("should log an error message", function() {
- const config = {
- message: "Error message",
- service: "TestService",
- method: "TestMethod",
- details: { key: "value" },
- stack: "Error stack trace",
- };
-
- logger.error(config);
-
- expect(errorStub.calledOnce).to.be.true;
- expect(
- errorStub.calledWith(config.message, {
- service: config.service,
- method: config.method,
- details: config.details,
- stack: config.stack,
- })
- ).to.be.true;
- });
- });
-});
diff --git a/Server/tests/utils/messages.test.js b/Server/tests/utils/messages.test.js
deleted file mode 100644
index 7ece332c4..000000000
--- a/Server/tests/utils/messages.test.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { errorMessages, successMessages } from "../../utils/messages.js";
-describe("Messages", function() {
- describe("messages - errorMessages", function() {
- it("should have a DB_FIND_MONITOR_BY_ID function", function() {
- const monitorId = "12345";
- expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)).to.equal(
- `Monitor with id ${monitorId} not found`
- );
- });
-
- it("should have a DB_DELETE_CHECKS function", function() {
- const monitorId = "12345";
- expect(errorMessages.DB_DELETE_CHECKS(monitorId)).to.equal(
- `No checks found for monitor with id ${monitorId}`
- );
- });
- });
-
- describe("messages - successMessages", function() {
- it("should have a MONITOR_GET_BY_USER_ID function", function() {
- const userId = "12345";
- expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal(
- `Got monitor for ${userId} successfully"`
- );
- });
-
- // Add more tests for other success messages as needed
- });
-});
diff --git a/Server/tests/utils/utils.test.js b/Server/tests/utils/utils.test.js
deleted file mode 100644
index f697c9b4a..000000000
--- a/Server/tests/utils/utils.test.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import { ParseBoolean, getTokenFromHeaders } from "../../utils/utils.js";
-
-describe("utils - ParseBoolean", function() {
- it("should return true", function() {
- const result = ParseBoolean("true");
- expect(result).to.be.true;
- });
-
- it("should return false", function() {
- const result = ParseBoolean("false");
- expect(result).to.be.false;
- });
-
- it("should return false", function() {
- const result = ParseBoolean(null);
- expect(result).to.be.false;
- });
-
- it("should return false", function() {
- const result = ParseBoolean(undefined);
- expect(result).to.be.false;
- });
-});
-
-describe("utils - getTokenFromHeaders", function() {
- it("should throw an error if authorization header is missing", function() {
- const headers = {};
- expect(() => getTokenFromHeaders(headers)).to.throw("No auth headers");
- });
-
- it("should throw an error if authorization header does not start with Bearer", function() {
- const headers = { authorization: "Basic abcdef" };
- expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers");
- });
-
- it("should return the token if authorization header is correctly formatted", function() {
- const headers = { authorization: "Bearer abcdef" };
- expect(getTokenFromHeaders(headers)).to.equal("abcdef");
- });
-
- it("should throw an error if authorization header has more than two parts", function() {
- const headers = { authorization: "Bearer abc def" };
- expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers");
- });
-
- it("should throw an error if authorization header has less than two parts", function() {
- const headers = { authorization: "Bearer" };
- expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers");
- });
-});
diff --git a/Server/utils/dataUtils.js b/Server/utils/dataUtils.js
deleted file mode 100644
index ca6ca66df..000000000
--- a/Server/utils/dataUtils.js
+++ /dev/null
@@ -1,92 +0,0 @@
-const calculatePercentile = (arr, percentile) => {
- const sorted = arr.slice().sort((a, b) => a.responseTime - b.responseTime);
- const index = (percentile / 100) * (sorted.length - 1);
- const lower = Math.floor(index);
- const upper = lower + 1;
- const weight = index % 1;
- if (upper >= sorted.length) return sorted[lower].responseTime;
- return sorted[lower].responseTime * (1 - weight) + sorted[upper].responseTime * weight;
-};
-
-const calculatePercentileUptimeDetails = (arr, percentile) => {
- const sorted = arr.slice().sort((a, b) => a.avgResponseTime - b.avgResponseTime);
- const index = (percentile / 100) * (sorted.length - 1);
- const lower = Math.floor(index);
- const upper = lower + 1;
- const weight = index % 1;
- if (upper >= sorted.length) return sorted[lower].avgResponseTime;
- return (
- sorted[lower].avgResponseTime * (1 - weight) + sorted[upper].avgResponseTime * weight
- );
-};
-
-const NormalizeData = (checks, rangeMin, rangeMax) => {
- if (checks.length > 1) {
- // Get the 5th and 95th percentile
- const min = calculatePercentile(checks, 0);
- const max = calculatePercentile(checks, 95);
- const normalizedChecks = checks.map((check) => {
- const originalResponseTime = check.responseTime;
- // Normalize the response time between 1 and 100
- let normalizedResponseTime =
- rangeMin + ((check.responseTime - min) * (rangeMax - rangeMin)) / (max - min);
-
- // Put a floor on the response times so we don't have extreme outliers
- // Better visuals
- normalizedResponseTime = Math.max(
- rangeMin,
- Math.min(rangeMax, normalizedResponseTime)
- );
- return {
- ...check,
- responseTime: normalizedResponseTime,
- originalResponseTime: originalResponseTime,
- };
- });
-
- return normalizedChecks;
- } else {
- return checks.map((check) => {
- return { ...check, originalResponseTime: check.responseTime };
- });
- }
-};
-const NormalizeDataUptimeDetails = (checks, rangeMin, rangeMax) => {
- if (checks.length > 1) {
- // Get the 5th and 95th percentile
- const min = calculatePercentileUptimeDetails(checks, 0);
- const max = calculatePercentileUptimeDetails(checks, 95);
-
- const normalizedChecks = checks.map((check) => {
- const originalResponseTime = check.avgResponseTime;
- // Normalize the response time between 1 and 100
- let normalizedResponseTime =
- rangeMin + ((check.avgResponseTime - min) * (rangeMax - rangeMin)) / (max - min);
-
- // Put a floor on the response times so we don't have extreme outliers
- // Better visuals
- normalizedResponseTime = Math.max(
- rangeMin,
- Math.min(rangeMax, normalizedResponseTime)
- );
- return {
- ...check,
- avgResponseTime: normalizedResponseTime,
- originalAvgResponseTime: originalResponseTime,
- };
- });
-
- return normalizedChecks;
- } else {
- return checks.map((check) => {
- return { ...check, originalResponseTime: check.responseTime };
- });
- }
-};
-
-export {
- calculatePercentile,
- NormalizeData,
- calculatePercentileUptimeDetails,
- NormalizeDataUptimeDetails,
-};
diff --git a/Server/utils/demoMonitors.json b/Server/utils/demoMonitors.json
deleted file mode 100644
index 3efea8d0e..000000000
--- a/Server/utils/demoMonitors.json
+++ /dev/null
@@ -1,22 +0,0 @@
-[
- {
- "name": "Google",
- "url": "https://www.google.com"
- },
- {
- "name": "Facebook",
- "url": "https://www.facebook.com"
- },
- {
- "name": "Yahoo",
- "url": "https://www.yahoo.com"
- },
- {
- "name": "Amazon",
- "url": "https://www.amazon.com"
- },
- {
- "name": "Apple",
- "url": "https://www.apple.com"
- }
-]
diff --git a/Server/utils/demoMonitorsOld.json b/Server/utils/demoMonitorsOld.json
deleted file mode 100644
index ffce7d226..000000000
--- a/Server/utils/demoMonitorsOld.json
+++ /dev/null
@@ -1,1271 +0,0 @@
-[
- {
- "name": "0to255",
- "url": "https://0to255.com"
- },
- {
- "name": "10015.io",
- "url": "https://10015.io"
- },
- {
- "name": "3DIcons",
- "url": "https://3dicons.co"
- },
- {
- "name": "About.me",
- "url": "https://about.me"
- },
- {
- "name": "Alias",
- "url": "https://alias.co"
- },
- {
- "name": "All About Berlin",
- "url": "https://allaboutberlin.com"
- },
- {
- "name": "All Acronyms",
- "url": "https://allacronyms.com"
- },
- {
- "name": "All You Can Read ",
- "url": "https://allyoucanread.com"
- },
- {
- "name": "AllTrails",
- "url": "https://alltrails.com"
- },
- {
- "name": "Anotepad",
- "url": "https://anotepad.com"
- },
- {
- "name": "AnswerSocrates",
- "url": "https://answersocrates.com"
- },
- {
- "name": "AnswerThePublic ",
- "url": "https://answerthepublic.com"
- },
- {
- "name": "Apollo ",
- "url": "https://apollo.io"
- },
- {
- "name": "ArrayList",
- "url": "https://arraylist.org"
- },
- {
- "name": "Ask Difference",
- "url": "https://askdifference.com"
- },
- {
- "name": "Audd.io",
- "url": "https://audd.io"
- },
- {
- "name": "Audiocheck",
- "url": "https://audiocheck.net"
- },
- {
- "name": "Audionautix",
- "url": "https://audionautix.com"
- },
- {
- "name": "Authentic Jobs",
- "url": "https://authenticjobs.com"
- },
- {
- "name": "Behind the Name",
- "url": "https://behindthename.com"
- },
- {
- "name": "Bilim Terimleri",
- "url": "https://terimler.org"
- },
- {
- "name": "BitBof",
- "url": "https://bitbof.com"
- },
- {
- "name": "Blank Page",
- "url": "https://blank.page"
- },
- {
- "name": "Bonanza",
- "url": "https://bonanza.com"
- },
- {
- "name": "BookCrossing",
- "url": "https://bookcrossing.com"
- },
- {
- "name": "Browse AI",
- "url": "https://browse.ai"
- },
- {
- "name": "Bubbl.us",
- "url": "https://bubbl.us"
- },
- {
- "name": "Business Model Toolbox",
- "url": "https://bmtoolbox.net"
- },
- {
- "name": "ByClickDownloader",
- "url": "https://byclickdownloader.com"
- },
- {
- "name": "Calligraphr",
- "url": "https://calligraphr.com"
- },
- {
- "name": "CertificateClaim",
- "url": "https://certificateclaim.com"
- },
- {
- "name": "Chosic",
- "url": "https://chosic.com"
- },
- {
- "name": "ClipDrop",
- "url": "https://clipdrop.co"
- },
- {
- "name": "CloudConvert",
- "url": "https://cloudconvert.com"
- },
- {
- "name": "CodingFont",
- "url": "https://codingfont.com"
- },
- {
- "name": "Color Hunt",
- "url": "https://colorhunt.co"
- },
- {
- "name": "ColorHexa",
- "url": "https://colorhexa.com"
- },
- {
- "name": "Conversion-Tool",
- "url": "https://conversion-tool.com"
- },
- {
- "name": "Cool Startup Jobs",
- "url": "https://coolstartupjobs.com"
- },
- {
- "name": "Coroflot",
- "url": "https://coroflot.com"
- },
- {
- "name": "Corrupt-a-File",
- "url": "https://corrupt-a-file.net"
- },
- {
- "name": "Couchsurfing",
- "url": "https://couchsurfing.com"
- },
- {
- "name": "Countries Been",
- "url": "https://countriesbeen.com"
- },
- {
- "name": "Country Code",
- "url": "https://countrycode.org"
- },
- {
- "name": "Creately",
- "url": "https://creately.com"
- },
- {
- "name": "Creately ",
- "url": "https://creately.com"
- },
- {
- "name": "Crossfade.io",
- "url": "https://crossfade.io"
- },
- {
- "name": "Crunchbase",
- "url": "https://crunchbase.com"
- },
- {
- "name": "CVmkr",
- "url": "https://cvwizard.com"
- },
- {
- "name": "Daily Remote",
- "url": "https://dailyremote.com"
- },
- {
- "name": "David Li",
- "url": "https://david.li"
- },
- {
- "name": "DemandHunt",
- "url": "https://demandhunt.com"
- },
- {
- "name": "Designify",
- "url": "https://designify.com"
- },
- {
- "name": "Diff Checker",
- "url": "https://diffchecker.com"
- },
- {
- "name": "DifferenceBetween.info",
- "url": "https://differencebetween.info"
- },
- {
- "name": "Digital Glossary",
- "url": "https://digital-glossary.com"
- },
- {
- "name": "Dimensions",
- "url": "https://dimensions.com"
- },
- {
- "name": "Discoverify Music",
- "url": "https://discoverifymusic.com"
- },
- {
- "name": "discu.eu",
- "url": "https://discu.eu"
- },
- {
- "name": "Do It Yourself",
- "url": "https://doityourself.com"
- },
- {
- "name": "draw.io",
- "url": "https://drawio.com"
- },
- {
- "name": "Drumeo",
- "url": "https://drumeo.com"
- },
- {
- "name": "Dummies",
- "url": "https://dummies.com"
- },
- {
- "name": "Easel.ly",
- "url": "https://easel.ly"
- },
- {
- "name": "Educalingo",
- "url": "https://educalingo.com"
- },
- {
- "name": "Emoji Combos",
- "url": "https://emojicombos.com"
- },
- {
- "name": "EquityBee",
- "url": "https://equitybee.com"
- },
- {
- "name": "EquityZen",
- "url": "https://equityzen.com"
- },
- {
- "name": "Escape Room Tips",
- "url": "https://escaperoomtips.com"
- },
- {
- "name": "Every Noise",
- "url": "https://everynoise.com"
- },
- {
- "name": "Every Time Zone",
- "url": "https://everytimezone.com"
- },
- {
- "name": "Excalideck",
- "url": "https://excalideck.com"
- },
- {
- "name": "Excalidraw",
- "url": "https://excalidraw.com"
- },
- {
- "name": "Extract pics",
- "url": "https://extract.pics"
- },
- {
- "name": "EZGIF",
- "url": "https://ezgif.com"
- },
- {
- "name": "FactSlides",
- "url": "https://factslides.com"
- },
- {
- "name": "FIGR ",
- "url": "https://figr.app"
- },
- {
- "name": "Fine Dictionary",
- "url": "https://finedictionary.com"
- },
- {
- "name": "Fiverr",
- "url": "https://fiverr.com"
- },
- {
- "name": "Fix It Club",
- "url": "https://fixitclub.com"
- },
- {
- "name": "Flightradar24",
- "url": "https://flightradar24.com"
- },
- {
- "name": "FlowCV ",
- "url": "https://flowcv.com"
- },
- {
- "name": "Font Squirrel",
- "url": "https://fontsquirrel.com"
- },
- {
- "name": "FontAwesome",
- "url": "https://fontawesome.com"
- },
- {
- "name": "Fontello ",
- "url": "https://fontello.com"
- },
- {
- "name": "Form to Chatbot",
- "url": "https://formtochatbot.com"
- },
- {
- "name": "Founder Resources",
- "url": "https://founderresources.io"
- },
- {
- "name": "Franz",
- "url": "https://meetfranz.com"
- },
- {
- "name": "Fraze It",
- "url": "https://fraze.it"
- },
- {
- "name": "Freecycle",
- "url": "https://freecycle.org"
- },
- {
- "name": "FreeType",
- "url": "https://freetype.org"
- },
- {
- "name": "FutureM",
- "url": "https://futureme.org"
- },
- {
- "name": "Generated.Photos",
- "url": "https://generated.photos"
- },
- {
- "name": "Get Human",
- "url": "https://gethuman.com"
- },
- {
- "name": "Go Bento",
- "url": "https://gobento.com"
- },
- {
- "name": "Good CV",
- "url": "https://goodcv.com"
- },
- {
- "name": "Grammar Monster",
- "url": "https://grammar-monster.com"
- },
- {
- "name": "Grammar Book",
- "url": "https://grammarbook.com"
- },
- {
- "name": "Gummy Search",
- "url": "https://gummysearch.com"
- },
- {
- "name": "Gumroad",
- "url": "https://gumroad.com"
- },
- {
- "name": "HealthIcons",
- "url": "https://healthicons.org"
- },
- {
- "name": "HexColor",
- "url": "https://hexcolor.co"
- },
- {
- "name": "Hidden Life Radio",
- "url": "https://hiddenliferadio.com"
- },
- {
- "name": "Hired",
- "url": "https://lhh.com"
- },
- {
- "name": "Honey",
- "url": "https://joinhoney.com"
- },
- {
- "name": "HowStuffWorks",
- "url": "https://howstuffworks.com"
- },
- {
- "name": "HugeIcons Pro",
- "url": "https://hugeicons.com"
- },
- {
- "name": "Humble Bundle",
- "url": "https://humblebundle.com"
- },
- {
- "name": "I Have No TV",
- "url": "https://ihavenotv.com"
- },
- {
- "name": "I Miss the Office",
- "url": "https://imisstheoffice.eu"
- },
- {
- "name": "IcoMoon",
- "url": "https://icomoon.io"
- },
- {
- "name": "Iconfinder",
- "url": "https://iconfinder.com"
- },
- {
- "name": "Icon Packs",
- "url": "https://iconpacks.net"
- },
- {
- "name": "Iconshock",
- "url": "https://iconshock.com"
- },
- {
- "name": "Iconz Design",
- "url": "https://iconz.design"
- },
- {
- "name": "iFixit",
- "url": "https://ifixit.com"
- },
- {
- "name": "IFTTT",
- "url": "https://ifttt.com"
- },
- {
- "name": "Illlustrations",
- "url": "https://illlustrations.co"
- },
- {
- "name": "Illustration Kit",
- "url": "https://illustrationkit.com"
- },
- {
- "name": "IMSDB",
- "url": "https://imsdb.com"
- },
- {
- "name": "Incompetech",
- "url": "https://incompetech.com"
- },
- {
- "name": "Incredibox",
- "url": "https://incredibox.com"
- },
- {
- "name": "InnerBod",
- "url": "https://innerbody.com"
- },
- {
- "name": "Instructables",
- "url": "https://instructables.com"
- },
- {
- "name": "Integromat",
- "url": "https://make.com"
- },
- {
- "name": "Investopedia",
- "url": "https://investopedia.com"
- },
- {
- "name": "Japanese Wiki Corpus",
- "url": "https://japanesewiki.com"
- },
- {
- "name": "Jitter.Video",
- "url": "https://jitter.video"
- },
- {
- "name": "Jobspresso",
- "url": "https://jobspresso.co"
- },
- {
- "name": "JPEG-Optimizer",
- "url": "https://jpeg-optimizer.com"
- },
- {
- "name": "JS Remotely",
- "url": "https://jsremotely.com"
- },
- {
- "name": "JScreenFix",
- "url": "https://jscreenfix.com"
- },
- {
- "name": "JSON Resume",
- "url": "https://jsonresume.io"
- },
- {
- "name": "Just Join",
- "url": "https://justjoin.it"
- },
- {
- "name": "Just the Recipe",
- "url": "https://justtherecipe.com"
- },
- {
- "name": "JustRemote",
- "url": "https://justremote.co"
- },
- {
- "name": "JustWatch",
- "url": "https://justwatch.com"
- },
- {
- "name": "Kanopy",
- "url": "https://kanopy.com"
- },
- {
- "name": "Kassellabs",
- "url": "https://kassellabs.io"
- },
- {
- "name": "Key Differences",
- "url": "https://keydifferences.com"
- },
- {
- "name": "Keybase",
- "url": "https://keybase.io"
- },
- {
- "name": "KeyValues",
- "url": "https://keyvalues.com"
- },
- {
- "name": "KHInsider",
- "url": "https://khinsider.com"
- },
- {
- "name": "Killed by Google",
- "url": "https://killedbygoogle.com"
- },
- {
- "name": "Kimovil",
- "url": "https://kimovil.com"
- },
- {
- "name": "Lalal.ai",
- "url": "https://www.lalal.ai"
- },
- {
- "name": "Learn Anything",
- "url": "https://learn-anything.xyz"
- },
- {
- "name": "LendingTree",
- "url": "https://lendingtree.com"
- },
- {
- "name": "Lightyear.fm",
- "url": "https://lightyear.fm"
- },
- {
- "name": "LittleSis",
- "url": "https://littlesis.org"
- },
- {
- "name": "Looria",
- "url": "https://looria.com"
- },
- {
- "name": "Lucidchart",
- "url": "https://lucidchart.com"
- },
- {
- "name": "Lunar",
- "url": "https://lunar.fyi"
- },
- {
- "name": "Manuals Lib",
- "url": "https://manualslib.com"
- },
- {
- "name": "Map Crunch",
- "url": "https://mapcrunch.com"
- },
- {
- "name": "Masterworks",
- "url": "https://masterworks.com"
- },
- {
- "name": "MediaFire",
- "url": "https://mediafire.com"
- },
- {
- "name": "Mixlr",
- "url": "https://mixlr.com"
- },
- {
- "name": "Moises AI",
- "url": "https://moises.ai"
- },
- {
- "name": "Money",
- "url": "https://money.com"
- },
- {
- "name": "Mountain Project",
- "url": "https://mountainproject.com"
- },
- {
- "name": "Movie Map",
- "url": "https://movie-map.com"
- },
- {
- "name": "Movie Sounds",
- "url": "https://movie-sounds.org"
- },
- {
- "name": "MP3Cut",
- "url": "https://mp3cut.net"
- },
- {
- "name": "Murmel",
- "url": "https://murmel.social"
- },
- {
- "name": "Muscle Wiki",
- "url": "https://musclewiki.com"
- },
- {
- "name": "Music-Map",
- "url": "https://music-map.com"
- },
- {
- "name": "MusicTheory.net",
- "url": "https://musictheory.net"
- },
- {
- "name": "MyFonts",
- "url": "https://myfonts.com"
- },
- {
- "name": "MyFridgeFood",
- "url": "https://myfridgefood.com"
- },
- {
- "name": "Nameberry",
- "url": "https://nameberry.com"
- },
- {
- "name": "Namechk",
- "url": "https://namechk.com"
- },
- {
- "name": "Ncase",
- "url": "https://ncase.me"
- },
- {
- "name": "News in Levels",
- "url": "https://newsinlevels.com"
- },
- {
- "name": "Noisli",
- "url": "https://noisli.com"
- },
- {
- "name": "Notes.io",
- "url": "https://notes.io"
- },
- {
- "name": "Novoresume",
- "url": "https://novoresume.com"
- },
- {
- "name": "Ocoya",
- "url": "https://ocoya.com"
- },
- {
- "name": "Old Computers Museum",
- "url": "https://oldcomputers.net"
- },
- {
- "name": "Online Tone Generator",
- "url": "https://onlinetonegenerator.com"
- },
- {
- "name": "Online-Convert",
- "url": "https://online-convert.com"
- },
- {
- "name": "OnlineConversion",
- "url": "https://onlineconversion.com"
- },
- {
- "name": "Online OCR",
- "url": "https://onlineocr.net"
- },
- {
- "name": "OpenWeatherMap",
- "url": "https://openweathermap.org"
- },
- {
- "name": "OrgPad",
- "url": "https://orgpad.com"
- },
- {
- "name": "Passport Index",
- "url": "https://passportindex.org"
- },
- {
- "name": "PDF Candy",
- "url": "https://pdfcandy.com"
- },
- {
- "name": "PDF2DOC",
- "url": "https://pdf2doc.com"
- },
- {
- "name": "PDFescape",
- "url": "https://pdfescape.com"
- },
- {
- "name": "PfpMaker",
- "url": "https://pfpmaker.com"
- },
- {
- "name": "PIDGI Wiki ",
- "url": "https://pidgi.net"
- },
- {
- "name": "PimEyes",
- "url": "https://pimeyes.com"
- },
- {
- "name": "Pipl ",
- "url": "https://pipl.com"
- },
- {
- "name": "PixelBazaar",
- "url": "https://pixelbazaar.com"
- },
- {
- "name": "PixelPaper",
- "url": "https://pixelpaper.io"
- },
- {
- "name": "Ponly",
- "url": "https://ponly.com"
- },
- {
- "name": "PowerToFly",
- "url": "https://powertofly.com"
- },
- {
- "name": "Pretzel Rocks",
- "url": "https://pretzel.rocks"
- },
- {
- "name": "PrintIt",
- "url": "https://printit.work"
- },
- {
- "name": "Prismatext",
- "url": "https://prismatext.com"
- },
- {
- "name": "Puffin Maps",
- "url": "https://puffinmaps.com"
- },
- {
- "name": "Puzzle Loop ",
- "url": "https://puzzle-loop.com"
- },
- {
- "name": "QuoteMaster",
- "url": "https://quotemaster.org"
- },
- {
- "name": "Radio Garden",
- "url": "https://radio.garden"
- },
- {
- "name": "Radiooooo",
- "url": "https://radiooooo.com"
- },
- {
- "name": "Radiosondy",
- "url": "https://radiosondy.info"
- },
- {
- "name": "Rainy Mood",
- "url": "https://rainymood.com"
- },
- {
- "name": "Random Street View",
- "url": "https://randomstreetview.com"
- },
- {
- "name": "Rap4Ever",
- "url": "https://rap4all.com"
- },
- {
- "name": "RareFilm",
- "url": "https://rarefilm.net"
- },
- {
- "name": "Rattibha",
- "url": "https://rattibha.com"
- },
- {
- "name": "Reddit List ",
- "url": "https://redditlist.com"
- },
- {
- "name": "RedditSearch.io",
- "url": "https://redditsearch.io"
- },
- {
- "name": "Reelgood",
- "url": "https://reelgood.com"
- },
- {
- "name": "Reface",
- "url": "https://reface.ai"
- },
- {
- "name": "Rejected.us",
- "url": "https://rejected.us"
- },
- {
- "name": "Relanote",
- "url": "https://relanote.com"
- },
- {
- "name": "Remote Leaf",
- "url": "https://remoteleaf.com"
- },
- {
- "name": "Remote OK",
- "url": "https://remoteok.com"
- },
- {
- "name": "Remote Starter Kit ",
- "url": "https://remotestarterkit.com"
- },
- {
- "name": "Remote.co",
- "url": "https://remote.co"
- },
- {
- "name": "Remote Base ",
- "url": "https://remotebase.com"
- },
- {
- "name": "Remote Bear",
- "url": "https://remotebear.io"
- },
- {
- "name": "Remove.bg",
- "url": "https://remove.bg"
- },
- {
- "name": "Respresso",
- "url": "https://respresso.io"
- },
- {
- "name": "Reveddit",
- "url": "https://reveddit.com"
- },
- {
- "name": "Rhymer",
- "url": "https://rhymer.com"
- },
- {
- "name": "RhymeZone",
- "url": "https://rhymezone.com"
- },
- {
- "name": "Ribbet",
- "url": "https://ribbet.com"
- },
- {
- "name": "Roadmap.sh",
- "url": "https://roadmap.sh"
- },
- {
- "name": "Roadtrippers",
- "url": "https://roadtrippers.com"
- },
- {
- "name": "RxResu.me",
- "url": "https://rxresu.me"
- },
- {
- "name": "SchemeColor",
- "url": "https://schemecolor.com"
- },
- {
- "name": "Screenshot.Guru",
- "url": "https://screenshot.guru"
- },
- {
- "name": "SeatGuru",
- "url": "https://seatguru.com"
- },
- {
- "name": "Sessions",
- "url": "https://sessions.us"
- },
- {
- "name": "Shottr",
- "url": "https://shottr.cc"
- },
- {
- "name": "Signature Maker",
- "url": "https://signature-maker.net"
- },
- {
- "name": "Skip The Drive",
- "url": "https://skipthedrive.com"
- },
- {
- "name": "Slowby",
- "url": "https://slowby.travel"
- },
- {
- "name": "Small World",
- "url": "https://smallworld.kiwi"
- },
- {
- "name": "SmallPDF",
- "url": "https://smallpdf.com"
- },
- {
- "name": "Social Image Maker",
- "url": "https://socialimagemaker.io"
- },
- {
- "name": "Social Sizes",
- "url": "https://socialsizes.io"
- },
- {
- "name": "SoundLove",
- "url": "https://soundlove.se"
- },
- {
- "name": "Spline",
- "url": "https://spline.design"
- },
- {
- "name": "Starkey Comics",
- "url": "https://starkeycomics.com"
- },
-
- {
- "name": "Statista",
- "url": "https://statista.com"
- },
- {
- "name": "Stolen Camera Finder",
- "url": "https://stolencamerafinder.com"
- },
- {
- "name": "Strobe.Cool",
- "url": "https://strobe.cool"
- },
- {
- "name": "Sumo",
- "url": "https://sumo.app"
- },
- {
- "name": "SuperMeme AI",
- "url": "https://supermeme.ai"
- },
- {
- "name": "Synthesia",
- "url": "https://synthesia.io"
- },
- {
- "name": "TablerIcons",
- "url": "https://tablericons.com"
- },
- {
- "name": "Tango",
- "url": "https://tango.us"
- },
- {
- "name": "TasteDive",
- "url": "https://tastedive.com"
- },
- {
- "name": "TechSpecs",
- "url": "https://techspecs.io"
- },
- {
- "name": "Teoria",
- "url": "https://teoria.com"
- },
- {
- "name": "Text Faces",
- "url": "https://textfac.es"
- },
- {
- "name": "The Balance Money",
- "url": "https://thebalancemoney.com"
- },
- {
- "name": "The Punctuation Guide",
- "url": "https://thepunctuationguide.com"
- },
- {
- "name": "This to That",
- "url": "https://thistothat.com"
- },
- {
- "name": "This vs That",
- "url": "https://thisvsthat.io"
- },
- {
- "name": "ThreadReaderApp ",
- "url": "https://threadreaderapp.com"
- },
- {
- "name": "Thumbly",
- "url": "https://tokee.ai"
- },
- {
- "name": "Tiii.me",
- "url": "https://tiii.me"
- },
- {
- "name": "TikTok Video Downloader",
- "url": "https://ttvdl.com"
- },
- {
- "name": "Time and Date",
- "url": "https://timeanddate.com"
- },
- {
- "name": "Time.is",
- "url": "https://time.is"
- },
- {
- "name": "Title Case",
- "url": "https://titlecase.com"
- },
- {
- "name": "Toaster Central",
- "url": "https://toastercentral.com"
- },
- {
- "name": "Tongue-Twister ",
- "url": "https://tongue-twister.net"
- },
- {
- "name": "TradingView",
- "url": "https://tradingview.com"
- },
- {
- "name": "Transparent Textures",
- "url": "https://transparenttextures.com"
- },
- {
- "name": "Tubi TV",
- "url": "https://tubitv.com"
- },
- {
- "name": "Tunefind",
- "url": "https://tunefind.com"
- },
- {
- "name": "TuneMyMusic",
- "url": "https://tunemymusic.com"
- },
- {
- "name": "Tweepsmap",
- "url": "https://fedica.com"
- },
- {
- "name": "Two Peas and Their Pod",
- "url": "https://twopeasandtheirpod.com"
- },
- {
- "name": "Typatone",
- "url": "https://typatone.com"
- },
- {
- "name": "Under Glass",
- "url": "https://underglass.io"
- },
- {
- "name": "UniCorner",
- "url": "https://unicorner.news"
- },
- {
- "name": "Unita",
- "url": "https://unita.co"
- },
- {
- "name": "UnitConverters",
- "url": "https://unitconverters.net"
- },
- {
- "name": "Unreadit",
- "url": "https://unreadit.com"
- },
- {
- "name": "Unscreen",
- "url": "https://unscreen.com"
- },
- {
- "name": "UnTools ",
- "url": "https://untools.co"
- },
- {
- "name": "Upwork",
- "url": "https://upwork.com"
- },
- {
- "name": "UTF8 Icons",
- "url": "https://utf8icons.com"
- },
- {
- "name": "Vector Magic",
- "url": "https://vectormagic.com"
- },
- {
- "name": "Virtual Vacation",
- "url": "https://virtualvacation.us"
- },
- {
- "name": "Virtual Vocations",
- "url": "https://virtualvocations.com"
- },
- {
- "name": "Visiwig",
- "url": "https://visiwig.com"
- },
- {
- "name": "Visual CV",
- "url": "https://visualcv.com"
- },
- {
- "name": "Vocus.io",
- "url": "https://vocus.io"
- },
- {
- "name": "Voscreen",
- "url": "https://voscreen.com"
- },
- {
- "name": "Wanderprep",
- "url": "https://wanderprep.com"
- },
- {
- "name": "Warmshowers",
- "url": "https:/warmshowers.org"
- },
- {
- "name": "Watch Documentaries",
- "url": "https://watchdocumentaries.com"
- },
- {
- "name": "We Work Remotely",
- "url": "https://weworkremotely.com"
- },
- {
- "name": "Web2PDFConvert",
- "url": "https://web2pdfconvert.com"
- },
- {
- "name": "Welcome to My Garden",
- "url": "https://welcometomygarden.org"
- },
- {
- "name": "When2meet ",
- "url": "https://when2meet.com"
- },
- {
- "name": "Where's George",
- "url": "https://wheresgeorge.com"
- },
- {
- "name": "Where's Willy",
- "url": "https://whereswilly.com"
- },
- {
- "name": "WikiHow",
- "url": "https://wikihow.com"
- },
- {
- "name": "Windy",
- "url": "https://www.windy.com"
- },
- {
- "name": "WonderHowTo",
- "url": "https://wonderhowto.com"
- },
- {
- "name": "Working Nomads",
- "url": "https://workingnomads.com"
- },
- {
- "name": "Wormhole",
- "url": "https://wormhole.app"
- },
- {
- "name": "Y Combinator Jobs",
- "url": "https://ycombinator.com"
- },
- {
- "name": "Yes Promo",
- "url": "https://yespromo.me"
- },
- {
- "name": "YouGlish",
- "url": "https://youglish.com"
- },
- {
- "name": "Zamzar",
- "url": "https://zamzar.com"
- },
- {
- "name": "Zippyshare",
- "url": "https://zippyshare.com"
- },
- {
- "name": "Zoom Earth",
- "url": "https://zoom.earth"
- },
- {
- "name": "Zoom.it",
- "url": "https://zoom.it"
- }
-]
diff --git a/Server/utils/imageProcessing.js b/Server/utils/imageProcessing.js
deleted file mode 100644
index 470a5c27e..000000000
--- a/Server/utils/imageProcessing.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import sharp from "sharp";
-/**
- * Generates a 64 * 64 pixel image from a given image
- * @param {} file
- */
-const GenerateAvatarImage = async (file) => {
- try {
- // Resize to target 64 * 64
- let resizedImageBuffer = await sharp(file.buffer)
- .resize({
- width: 64,
- height: 64,
- fit: "cover",
- })
- .toBuffer();
-
- //Get b64 string
- const base64Image = resizedImageBuffer.toString("base64");
- return base64Image;
- } catch (error) {
- throw error;
- }
-};
-
-export { GenerateAvatarImage };
diff --git a/Server/utils/logger.js b/Server/utils/logger.js
deleted file mode 100644
index e263085a7..000000000
--- a/Server/utils/logger.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { createLogger, format, transports } from "winston";
-
-class Logger {
- constructor() {
- const consoleFormat = format.printf(
- ({ level, message, service, method, details, timestamp }) => {
- if (message instanceof Object) {
- message = JSON.stringify(message, null, 2);
- }
-
- if (details instanceof Object) {
- details = JSON.stringify(details, null, 2);
- }
- let msg = `${timestamp} ${level}:`;
- service && (msg += ` [${service}]`);
- method && (msg += `(${method})`);
- message && (msg += ` ${message}`);
- details && (msg += ` (details: ${details})`);
- return msg;
- }
- );
-
- this.logger = createLogger({
- level: "info",
- format: format.combine(format.timestamp()),
- transports: [
- new transports.Console({
- format: format.combine(
- format.colorize(),
- format.prettyPrint(),
- format.json(),
- consoleFormat
- ),
- }),
- new transports.File({
- format: format.combine(format.json()),
- filename: "app.log",
- }),
- ],
- });
- }
- /**
- * Logs an informational message.
- * @param {Object} config - The configuration object.
- * @param {string} config.message - The message to log.
- * @param {string} config.service - The service name.
- * @param {string} config.method - The method name.
- * @param {Object} config.details - Additional details.
- */
- info(config) {
- this.logger.info(config.message, {
- service: config.service,
- method: config.method,
- details: config.details,
- });
- }
-
- /**
- * Logs a warning message.
- * @param {Object} config - The configuration object.
- * @param {string} config.message - The message to log.
- * @param {string} config.service - The service name.
- * @param {string} config.method - The method name.
- * @param {Object} config.details - Additional details.
- */
- warn(config) {
- this.logger.warn(config.message, {
- service: config.service,
- method: config.method,
- details: config.details,
- });
- }
-
- /**
- * Logs an error message.
- * @param {Object} config - The configuration object.
- * @param {string} config.message - The message to log.
- * @param {string} config.service - The service name.
- * @param {string} config.method - The method name.
- * @param {Object} config.details - Additional details.
- */
- error(config) {
- this.logger.error(config.message, {
- service: config.service,
- method: config.method,
- details: config.details,
- stack: config.stack,
- });
- }
-}
-
-const logger = new Logger();
-export { Logger };
-
-export default logger;
diff --git a/Server/utils/utils.js b/Server/utils/utils.js
deleted file mode 100644
index 5bd9258f9..000000000
--- a/Server/utils/utils.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Converts a request body parameter to a boolean.
- * @param {string | boolean} value
- * @returns {boolean}
- */
-const ParseBoolean = (value) => {
- if (value === true || value === "true") {
- return true;
- } else if (
- value === false ||
- value === "false" ||
- value === null ||
- value === undefined
- ) {
- return false;
- }
-};
-
-const getTokenFromHeaders = (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");
-
- return parts[1];
-};
-
-const tokenType = Object.freeze({
- ACCESS_TOKEN: "Access token",
- REFRESH_TOKEN: "Refresh token",
-});
-
-export { ParseBoolean, getTokenFromHeaders, tokenType };
diff --git a/Server/validation/joi.js b/Server/validation/joi.js
deleted file mode 100644
index 0967d9150..000000000
--- a/Server/validation/joi.js
+++ /dev/null
@@ -1,605 +0,0 @@
-import joi from "joi";
-
-//****************************************
-// Custom Validators
-//****************************************
-
-const roleValidatior = (role) => (value, helpers) => {
- const hasRole = role.some((role) => value.includes(role));
- if (!hasRole) {
- throw new joi.ValidationError(
- `You do not have the required authorization. Required roles: ${role.join(", ")}`
- );
- }
- return value;
-};
-
-//****************************************
-// Auth
-//****************************************
-
-const passwordPattern =
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\-_=+[\]{};:'",.<>~`|\\/])[A-Za-z0-9!?@#$%^&*()\-_=+[\]{};:'",.<>~`|\\/]+$/;
-
-const loginValidation = joi.object({
- email: joi
- .string()
- .email()
- .required()
- .custom((value, helpers) => {
- const lowercasedValue = value.toLowerCase();
- if (value !== lowercasedValue) {
- return helpers.message("Email must be in lowercase");
- }
- return lowercasedValue;
- }),
- password: joi.string().min(8).required().pattern(passwordPattern),
-});
-
-const registrationBodyValidation = joi.object({
- firstName: joi
- .string()
- .required()
- .pattern(/^[A-Za-z]+$/),
- lastName: joi
- .string()
- .required()
- .pattern(/^[A-Za-z]+$/),
- email: joi
- .string()
- .email()
- .required()
- .custom((value, helpers) => {
- const lowercasedValue = value.toLowerCase();
- if (value !== lowercasedValue) {
- return helpers.message("Email must be in lowercase");
- }
- return lowercasedValue;
- }),
- password: joi.string().min(8).required().pattern(passwordPattern),
- profileImage: joi.any(),
- role: joi
- .array()
- .items(joi.string().valid("superadmin", "admin", "user", "demo"))
- .min(1)
- .required(),
- teamId: joi.string().allow("").required(),
- inviteToken: joi.string().allow("").required(),
-});
-
-const editUserParamValidation = joi.object({
- userId: joi.string().required(),
-});
-
-const editUserBodyValidation = joi.object({
- firstName: joi.string().pattern(/^[A-Za-z]+$/),
- lastName: joi.string().pattern(/^[A-Za-z]+$/),
- profileImage: joi.any(),
- newPassword: joi.string().min(8).pattern(passwordPattern),
- password: joi.string().min(8).pattern(passwordPattern),
- deleteProfileImage: joi.boolean(),
- role: joi.array(),
-});
-
-const recoveryValidation = joi.object({
- email: joi
- .string()
- .email({ tlds: { allow: false } })
- .required(),
-});
-
-const recoveryTokenValidation = joi.object({
- recoveryToken: joi.string().required(),
-});
-
-const newPasswordValidation = joi.object({
- recoveryToken: joi.string().required(),
- password: joi.string().min(8).required().pattern(passwordPattern),
- confirm: joi.string(),
-});
-
-const deleteUserParamValidation = joi.object({
- email: joi.string().email().required(),
-});
-
-const inviteRoleValidation = joi.object({
- roles: joi.custom(roleValidatior(["admin", "superadmin"])).required(),
-});
-
-const inviteBodyValidation = joi.object({
- email: joi.string().trim().email().required().messages({
- "string.empty": "Email is required",
- "string.email": "Must be a valid email address",
- }),
- role: joi.array().required(),
- teamId: joi.string().required(),
-});
-
-const inviteVerificationBodyValidation = joi.object({
- token: joi.string().required(),
-});
-
-//****************************************
-// Monitors
-//****************************************
-
-const getMonitorByIdParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const getMonitorByIdQueryValidation = joi.object({
- status: joi.boolean(),
- sortOrder: joi.string().valid("asc", "desc"),
- limit: joi.number(),
- dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
- numToDisplay: joi.number(),
- normalize: joi.boolean(),
-});
-
-const getMonitorsByTeamIdParamValidation = joi.object({
- teamId: joi.string().required(),
-});
-
-const getMonitorsByTeamIdQueryValidation = joi.object({
- limit: joi.number(),
- type: joi
- .alternatives()
- .try(
- joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port"),
- joi
- .array()
- .items(
- joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port")
- )
- ),
- page: joi.number(),
- rowsPerPage: joi.number(),
- filter: joi.string(),
- field: joi.string(),
- order: joi.string().valid("asc", "desc"),
-});
-
-const getMonitorStatsByIdParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-const getMonitorStatsByIdQueryValidation = joi.object({
- status: joi.string(),
- limit: joi.number(),
- sortOrder: joi.string().valid("asc", "desc"),
- dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
- numToDisplay: joi.number(),
- normalize: joi.boolean(),
-});
-
-const getCertificateParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const createMonitorBodyValidation = joi.object({
- _id: joi.string(),
- userId: joi.string().required(),
- teamId: joi.string().required(),
- name: joi.string().required(),
- description: joi.string().required(),
- type: joi.string().required(),
- url: joi.string().required(),
- port: joi.number(),
- isActive: joi.boolean(),
- interval: joi.number(),
- thresholds: joi.object().keys({
- usage_cpu: joi.number(),
- usage_memory: joi.number(),
- usage_disk: joi.number(),
- usage_temperature: joi.number(),
- }),
- notifications: joi.array().items(joi.object()),
- secret: joi.string(),
- jsonPath: joi.string().allow(""),
- expectedValue: joi.string().allow(""),
- matchMethod: joi.string(),
-});
-
-const editMonitorBodyValidation = joi.object({
- name: joi.string(),
- description: joi.string(),
- interval: joi.number(),
- notifications: joi.array().items(joi.object()),
- secret: joi.string(),
- jsonPath: joi.string().allow(""),
- expectedValue: joi.string().allow(""),
- matchMethod: joi.string(),
-});
-
-const pauseMonitorParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const getMonitorURLByQueryValidation = joi.object({
- monitorURL: joi.string().uri().required(),
-});
-
-const getHardwareDetailsByIdParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const getHardwareDetailsByIdQueryValidation = joi.object({
- dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
-});
-
-//****************************************
-// Alerts
-//****************************************
-
-const createAlertParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const createAlertBodyValidation = joi.object({
- checkId: joi.string().required(),
- monitorId: joi.string().required(),
- userId: joi.string().required(),
- status: joi.boolean(),
- message: joi.string(),
- notifiedStatus: joi.boolean(),
- acknowledgeStatus: joi.boolean(),
-});
-
-const getAlertsByUserIdParamValidation = joi.object({
- userId: joi.string().required(),
-});
-
-const getAlertsByMonitorIdParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const getAlertByIdParamValidation = joi.object({
- alertId: joi.string().required(),
-});
-
-const editAlertParamValidation = joi.object({
- alertId: joi.string().required(),
-});
-
-const editAlertBodyValidation = joi.object({
- status: joi.boolean(),
- message: joi.string(),
- notifiedStatus: joi.boolean(),
- acknowledgeStatus: joi.boolean(),
-});
-
-const deleteAlertParamValidation = joi.object({
- alertId: joi.string().required(),
-});
-
-//****************************************
-// Checks
-//****************************************
-
-const createCheckParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const createCheckBodyValidation = joi.object({
- monitorId: joi.string().required(),
- status: joi.boolean().required(),
- responseTime: joi.number().required(),
- statusCode: joi.number().required(),
- message: joi.string().required(),
-});
-
-const getChecksParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const getChecksQueryValidation = joi.object({
- sortOrder: joi.string().valid("asc", "desc"),
- limit: joi.number(),
- dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
- filter: joi.string().valid("all", "down", "resolve"),
- page: joi.number(),
- rowsPerPage: joi.number(),
- status: joi.boolean(),
-});
-
-const getTeamChecksParamValidation = joi.object({
- teamId: joi.string().required(),
-});
-
-const getTeamChecksQueryValidation = joi.object({
- sortOrder: joi.string().valid("asc", "desc"),
- limit: joi.number(),
- dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
- filter: joi.string().valid("all", "down", "resolve"),
- page: joi.number(),
- rowsPerPage: joi.number(),
- status: joi.boolean(),
-});
-
-const deleteChecksParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const deleteChecksByTeamIdParamValidation = joi.object({
- teamId: joi.string().required(),
-});
-
-const updateChecksTTLBodyValidation = joi.object({
- ttl: joi.number().required(),
-});
-
-//****************************************
-// PageSpeedCheckValidation
-//****************************************
-
-const getPageSpeedCheckParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-//Validation schema for the monitorId parameter
-const createPageSpeedCheckParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-//Validation schema for the monitorId body
-const createPageSpeedCheckBodyValidation = joi.object({
- url: joi.string().required(),
-});
-
-const deletePageSpeedCheckParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-//****************************************
-// MaintenanceWindowValidation
-//****************************************
-
-const createMaintenanceWindowBodyValidation = joi.object({
- monitors: joi.array().items(joi.string()).required(),
- name: joi.string().required(),
- active: joi.boolean(),
- start: joi.date().required(),
- end: joi.date().required(),
- repeat: joi.number().required(),
- expiry: joi.date(),
-});
-
-const getMaintenanceWindowByIdParamValidation = joi.object({
- id: joi.string().required(),
-});
-
-const getMaintenanceWindowsByTeamIdQueryValidation = joi.object({
- active: joi.boolean(),
- page: joi.number(),
- rowsPerPage: joi.number(),
- field: joi.string(),
- order: joi.string().valid("asc", "desc"),
-});
-
-const getMaintenanceWindowsByMonitorIdParamValidation = joi.object({
- monitorId: joi.string().required(),
-});
-
-const deleteMaintenanceWindowByIdParamValidation = joi.object({
- id: joi.string().required(),
-});
-
-const editMaintenanceWindowByIdParamValidation = joi.object({
- id: joi.string().required(),
-});
-
-const editMaintenanceByIdWindowBodyValidation = joi.object({
- active: joi.boolean(),
- name: joi.string(),
- repeat: joi.number(),
- start: joi.date(),
- end: joi.date(),
- expiry: joi.date(),
- monitors: joi.array(),
-});
-
-//****************************************
-// SettingsValidation
-//****************************************
-const updateAppSettingsBodyValidation = joi.object({
- apiBaseUrl: joi.string().allow(""),
- logLevel: joi.string().valid("debug", "none", "error", "warn").allow(""),
- clientHost: joi.string().allow(""),
- dbType: joi.string().allow(""),
- dbConnectionString: joi.string().allow(""),
- redisHost: joi.string().allow(""),
- redisPort: joi.number().allow(null, ""),
- jwtTTL: joi.string().allow(""),
- pagespeedApiKey: joi.string().allow(""),
- systemEmailHost: joi.string().allow(""),
- systemEmailPort: joi.number().allow(""),
- systemEmailAddress: joi.string().allow(""),
- systemEmailPassword: joi.string().allow(""),
-});
-
-//****************************************
-// Status Page Validation
-//****************************************
-
-const getStatusPageParamValidation = joi.object({
- url: joi.string().required(),
-});
-
-const getStatusPageQueryValidation = joi.object({
- type: joi.string().valid("uptime", "distributed").required(),
-});
-
-const createStatusPageBodyValidation = joi.object({
- userId: joi.string().required(),
- teamId: joi.string().required(),
- type: joi.string().valid("uptime", "distributed").required(),
- companyName: joi.string().required(),
- url: joi.string().required(),
- timezone: joi.string().optional(),
- color: joi.string().optional(),
- monitors: joi
- .array()
- .items(joi.string().pattern(/^[0-9a-fA-F]{24}$/))
- .required()
- .messages({
- "string.pattern.base": "Must be a valid monitor ID",
- "array.base": "Monitors must be an array",
- "array.empty": "At least one monitor is required",
- "any.required": "Monitors are required",
- }),
- isPublished: joi.boolean(),
- showCharts: joi.boolean().optional(),
- showUptimePercentage: joi.boolean(),
-});
-
-const imageValidation = joi
- .object({
- fieldname: joi.string().required(),
- originalname: joi.string().required(),
- encoding: joi.string().required(),
- mimetype: joi
- .string()
- .valid("image/jpeg", "image/png", "image/jpg")
- .required()
- .messages({
- "string.valid": "File must be a valid image (jpeg, jpg, or png)",
- }),
- size: joi.number().max(3145728).required().messages({
- "number.max": "File size must be less than 3MB",
- }),
- buffer: joi.binary().required(),
- destination: joi.string(),
- filename: joi.string(),
- path: joi.string(),
- })
- .messages({
- "any.required": "Image file is required",
- });
-
- const webhookConfigValidation = joi.object({
- webhookUrl: joi.string().uri()
- .when('$platform', {
- switch: [
- {
- is: 'telegram',
- then: joi.optional()
- },
- {
- is: 'discord',
- then: joi.required().messages({
- 'string.empty': 'Discord webhook URL is required',
- 'string.uri': 'Discord webhook URL must be a valid URL',
- 'any.required': 'Discord webhook URL is required'
- })
- },
- {
- is: 'slack',
- then: joi.required().messages({
- 'string.empty': 'Slack webhook URL is required',
- 'string.uri': 'Slack webhook URL must be a valid URL',
- 'any.required': 'Slack webhook URL is required'
- })
- }
- ]
- }),
- botToken: joi.string()
- .when('$platform', {
- is: 'telegram',
- then: joi.required().messages({
- 'string.empty': 'Telegram bot token is required',
- 'any.required': 'Telegram bot token is required'
- }),
- otherwise: joi.optional()
- }),
- chatId: joi.string()
- .when('$platform', {
- is: 'telegram',
- then: joi.required().messages({
- 'string.empty': 'Telegram chat ID is required',
- 'any.required': 'Telegram chat ID is required'
- }),
- otherwise: joi.optional()
- })
- }).required();
-
- const triggerNotificationBodyValidation = joi.object({
- monitorId: joi.string().required().messages({
- 'string.empty': 'Monitor ID is required',
- 'any.required': 'Monitor ID is required'
- }),
- type: joi.string().valid('webhook').required().messages({
- 'string.empty': 'Notification type is required',
- 'any.required': 'Notification type is required',
- 'any.only': 'Notification type must be webhook'
- }),
- platform: joi.string().valid('telegram', 'discord', 'slack').required().messages({
- 'string.empty': 'Platform type is required',
- 'any.required': 'Platform type is required',
- 'any.only': 'Platform must be telegram, discord, or slack'
- }),
- config: webhookConfigValidation.required().messages({
- 'any.required': 'Webhook configuration is required'
- })
- });
-
-export {
- roleValidatior,
- loginValidation,
- registrationBodyValidation,
- recoveryValidation,
- recoveryTokenValidation,
- newPasswordValidation,
- inviteRoleValidation,
- inviteBodyValidation,
- inviteVerificationBodyValidation,
- createMonitorBodyValidation,
- getMonitorByIdParamValidation,
- getMonitorByIdQueryValidation,
- getMonitorsByTeamIdParamValidation,
- getMonitorsByTeamIdQueryValidation,
- getMonitorStatsByIdParamValidation,
- getMonitorStatsByIdQueryValidation,
- getHardwareDetailsByIdParamValidation,
- getHardwareDetailsByIdQueryValidation,
- getCertificateParamValidation,
- editMonitorBodyValidation,
- pauseMonitorParamValidation,
- getMonitorURLByQueryValidation,
- editUserParamValidation,
- editUserBodyValidation,
- createAlertParamValidation,
- createAlertBodyValidation,
- getAlertsByUserIdParamValidation,
- getAlertsByMonitorIdParamValidation,
- getAlertByIdParamValidation,
- editAlertParamValidation,
- editAlertBodyValidation,
- deleteAlertParamValidation,
- createCheckParamValidation,
- createCheckBodyValidation,
- getChecksParamValidation,
- getChecksQueryValidation,
- getTeamChecksParamValidation,
- getTeamChecksQueryValidation,
- deleteChecksParamValidation,
- deleteChecksByTeamIdParamValidation,
- updateChecksTTLBodyValidation,
- deleteUserParamValidation,
- getPageSpeedCheckParamValidation,
- createPageSpeedCheckParamValidation,
- deletePageSpeedCheckParamValidation,
- createPageSpeedCheckBodyValidation,
- createMaintenanceWindowBodyValidation,
- getMaintenanceWindowByIdParamValidation,
- getMaintenanceWindowsByTeamIdQueryValidation,
- getMaintenanceWindowsByMonitorIdParamValidation,
- deleteMaintenanceWindowByIdParamValidation,
- editMaintenanceWindowByIdParamValidation,
- editMaintenanceByIdWindowBodyValidation,
- updateAppSettingsBodyValidation,
- createStatusPageBodyValidation,
- getStatusPageParamValidation,
- getStatusPageQueryValidation,
- imageValidation,
- triggerNotificationBodyValidation,
- webhookConfigValidation,
-};
diff --git a/docs/diagrams/Bluewave Uptime Auth Flow.pdf b/docs/diagrams/Bluewave Uptime Auth Flow.pdf
deleted file mode 100644
index d7c7b45ca..000000000
Binary files a/docs/diagrams/Bluewave Uptime Auth Flow.pdf and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime Auth Flow.png b/docs/diagrams/Bluewave Uptime Auth Flow.png
deleted file mode 100644
index 32498b3b4..000000000
Binary files a/docs/diagrams/Bluewave Uptime Auth Flow.png and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime High Level Overview.pdf b/docs/diagrams/Bluewave Uptime High Level Overview.pdf
deleted file mode 100644
index 1409d10fb..000000000
Binary files a/docs/diagrams/Bluewave Uptime High Level Overview.pdf and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime High Level Overview.png b/docs/diagrams/Bluewave Uptime High Level Overview.png
deleted file mode 100644
index 65a2866b7..000000000
Binary files a/docs/diagrams/Bluewave Uptime High Level Overview.png and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime JobQueue Flow.pdf b/docs/diagrams/Bluewave Uptime JobQueue Flow.pdf
deleted file mode 100644
index e39825f43..000000000
Binary files a/docs/diagrams/Bluewave Uptime JobQueue Flow.pdf and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime JobQueue Flow.png b/docs/diagrams/Bluewave Uptime JobQueue Flow.png
deleted file mode 100644
index 8794017f0..000000000
Binary files a/docs/diagrams/Bluewave Uptime JobQueue Flow.png and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime Monitor Flow.pdf b/docs/diagrams/Bluewave Uptime Monitor Flow.pdf
deleted file mode 100644
index 97d56d781..000000000
Binary files a/docs/diagrams/Bluewave Uptime Monitor Flow.pdf and /dev/null differ
diff --git a/docs/diagrams/Bluewave Uptime Monitor Flow.png b/docs/diagrams/Bluewave Uptime Monitor Flow.png
deleted file mode 100644
index 5cfc8bca8..000000000
Binary files a/docs/diagrams/Bluewave Uptime Monitor Flow.png and /dev/null differ
diff --git a/Client/env.sh b/env.sh
similarity index 100%
rename from Client/env.sh
rename to env.sh
diff --git a/guides/.gitbook/assets/Screenshot 2024-10-10 at 10.37.36โฏPM.png b/guides/.gitbook/assets/Screenshot 2024-10-10 at 10.37.36โฏPM.png
deleted file mode 100644
index 31dbacbe0..000000000
Binary files a/guides/.gitbook/assets/Screenshot 2024-10-10 at 10.37.36โฏPM.png and /dev/null differ
diff --git a/guides/users-guide/maintenance-mode.md b/guides/users-guide/maintenance-mode.md
deleted file mode 100644
index 7881dbfd8..000000000
--- a/guides/users-guide/maintenance-mode.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-icon: clock-nine
----
-
-# Maintenance mode
-
-The Maintenance mode allows you to manage and schedule maintenance periods for monitored URLs. During a maintenance window, no pings or HTTP requests are sent to the specified URLs, preventing downtime alerts during scheduled maintenance activities.
-
-### Creating a new maintenance window
-
-When you click the **"Create Maintenance Window"** button, you will see a page similar to this:
-
-
-
-Here's how to set up a new maintenance window:
-
-**General settings:**
-
-* **Maintenance repeat**: Select how often the maintenance window should repeat (e.g., don't repeat, daily, weekly).
-* **Date**: Select the date the maintenance window should start.
-* **Start time**: Set the time of day the window begins.
-* **Duration**: Specify how long the maintenance window should last (in seconds, minutes, or hours).
-
-**Monitor related settings:**
-
-* **Friendly Name**: Provide a descriptive name for the maintenance window (e.g., "Maintenance at 10:37 PM for 30 minutes").
-* **Add Monitors**: Select the specific monitors (URLs) for which this maintenance window will apply by typing and selecting from the list.
-
-Once all fields are filled, click **Create maintenance** to save the new window.
-
-### Editing a maintenance window
-
-* To edit an existing window, click the gear icon under **Actions** in the table view.
-* Update the required fields and save your changes.
-
diff --git a/index.html b/index.html
index ee30de68d..2d13bd25d 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,16 @@
-Checkmate web page
+
+
+
+
+
+
+
+ Checkmate
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Client/package-lock.json b/package-lock.json
similarity index 100%
rename from Client/package-lock.json
rename to package-lock.json
diff --git a/Client/package.json b/package.json
similarity index 100%
rename from Client/package.json
rename to package.json
diff --git a/Client/public/checkmate_favicon.svg b/public/checkmate_favicon.svg
similarity index 100%
rename from Client/public/checkmate_favicon.svg
rename to public/checkmate_favicon.svg
diff --git a/Client/src/App.jsx b/src/App.jsx
similarity index 100%
rename from Client/src/App.jsx
rename to src/App.jsx
diff --git a/Client/src/Components/ActionsMenu/index.jsx b/src/Components/ActionsMenu/index.jsx
similarity index 100%
rename from Client/src/Components/ActionsMenu/index.jsx
rename to src/Components/ActionsMenu/index.jsx
diff --git a/Client/src/Components/Alert/index.css b/src/Components/Alert/index.css
similarity index 100%
rename from Client/src/Components/Alert/index.css
rename to src/Components/Alert/index.css
diff --git a/Client/src/Components/Alert/index.jsx b/src/Components/Alert/index.jsx
similarity index 100%
rename from Client/src/Components/Alert/index.jsx
rename to src/Components/Alert/index.jsx
diff --git a/Client/src/Components/Animated/PulseDot.jsx b/src/Components/Animated/PulseDot.jsx
similarity index 100%
rename from Client/src/Components/Animated/PulseDot.jsx
rename to src/Components/Animated/PulseDot.jsx
diff --git a/Client/src/Components/Avatar/index.css b/src/Components/Avatar/index.css
similarity index 100%
rename from Client/src/Components/Avatar/index.css
rename to src/Components/Avatar/index.css
diff --git a/Client/src/Components/Avatar/index.jsx b/src/Components/Avatar/index.jsx
similarity index 100%
rename from Client/src/Components/Avatar/index.jsx
rename to src/Components/Avatar/index.jsx
diff --git a/Client/src/Components/Breadcrumbs/index.css b/src/Components/Breadcrumbs/index.css
similarity index 100%
rename from Client/src/Components/Breadcrumbs/index.css
rename to src/Components/Breadcrumbs/index.css
diff --git a/Client/src/Components/Breadcrumbs/index.jsx b/src/Components/Breadcrumbs/index.jsx
similarity index 100%
rename from Client/src/Components/Breadcrumbs/index.jsx
rename to src/Components/Breadcrumbs/index.jsx
diff --git a/Client/src/Components/Charts/AreaChart/index.jsx b/src/Components/Charts/AreaChart/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/AreaChart/index.jsx
rename to src/Components/Charts/AreaChart/index.jsx
diff --git a/Client/src/Components/Charts/BarChart/index.css b/src/Components/Charts/BarChart/index.css
similarity index 100%
rename from Client/src/Components/Charts/BarChart/index.css
rename to src/Components/Charts/BarChart/index.css
diff --git a/Client/src/Components/Charts/BarChart/index.jsx b/src/Components/Charts/BarChart/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/BarChart/index.jsx
rename to src/Components/Charts/BarChart/index.jsx
diff --git a/Client/src/Components/Charts/ChartBox/index.jsx b/src/Components/Charts/ChartBox/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/ChartBox/index.jsx
rename to src/Components/Charts/ChartBox/index.jsx
diff --git a/Client/src/Components/Charts/CustomGauge/index.css b/src/Components/Charts/CustomGauge/index.css
similarity index 100%
rename from Client/src/Components/Charts/CustomGauge/index.css
rename to src/Components/Charts/CustomGauge/index.css
diff --git a/Client/src/Components/Charts/CustomGauge/index.jsx b/src/Components/Charts/CustomGauge/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/CustomGauge/index.jsx
rename to src/Components/Charts/CustomGauge/index.jsx
diff --git a/Client/src/Components/Charts/LegendBox/index.jsx b/src/Components/Charts/LegendBox/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/LegendBox/index.jsx
rename to src/Components/Charts/LegendBox/index.jsx
diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.css b/src/Components/Charts/MonitorDetailsAreaChart/index.css
similarity index 100%
rename from Client/src/Components/Charts/MonitorDetailsAreaChart/index.css
rename to src/Components/Charts/MonitorDetailsAreaChart/index.css
diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx b/src/Components/Charts/MonitorDetailsAreaChart/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx
rename to src/Components/Charts/MonitorDetailsAreaChart/index.jsx
diff --git a/Client/src/Components/Charts/StatusPageBarChart/index.jsx b/src/Components/Charts/StatusPageBarChart/index.jsx
similarity index 100%
rename from Client/src/Components/Charts/StatusPageBarChart/index.jsx
rename to src/Components/Charts/StatusPageBarChart/index.jsx
diff --git a/Client/src/Components/Charts/Utils/chartUtilFunctions.js b/src/Components/Charts/Utils/chartUtilFunctions.js
similarity index 100%
rename from Client/src/Components/Charts/Utils/chartUtilFunctions.js
rename to src/Components/Charts/Utils/chartUtilFunctions.js
diff --git a/Client/src/Components/Charts/Utils/chartUtils.jsx b/src/Components/Charts/Utils/chartUtils.jsx
similarity index 100%
rename from Client/src/Components/Charts/Utils/chartUtils.jsx
rename to src/Components/Charts/Utils/chartUtils.jsx
diff --git a/Client/src/Components/Charts/Utils/gradientUtils.jsx b/src/Components/Charts/Utils/gradientUtils.jsx
similarity index 100%
rename from Client/src/Components/Charts/Utils/gradientUtils.jsx
rename to src/Components/Charts/Utils/gradientUtils.jsx
diff --git a/Client/src/Components/Check/Check.jsx b/src/Components/Check/Check.jsx
similarity index 100%
rename from Client/src/Components/Check/Check.jsx
rename to src/Components/Check/Check.jsx
diff --git a/Client/src/Components/Check/check.css b/src/Components/Check/check.css
similarity index 100%
rename from Client/src/Components/Check/check.css
rename to src/Components/Check/check.css
diff --git a/Client/src/Components/CircularCount/index.jsx b/src/Components/CircularCount/index.jsx
similarity index 100%
rename from Client/src/Components/CircularCount/index.jsx
rename to src/Components/CircularCount/index.jsx
diff --git a/Client/src/Components/ConfigBox/index.jsx b/src/Components/ConfigBox/index.jsx
similarity index 100%
rename from Client/src/Components/ConfigBox/index.jsx
rename to src/Components/ConfigBox/index.jsx
diff --git a/Client/src/Components/Dialog/genericDialog.jsx b/src/Components/Dialog/genericDialog.jsx
similarity index 100%
rename from Client/src/Components/Dialog/genericDialog.jsx
rename to src/Components/Dialog/genericDialog.jsx
diff --git a/Client/src/Components/Dialog/index.jsx b/src/Components/Dialog/index.jsx
similarity index 100%
rename from Client/src/Components/Dialog/index.jsx
rename to src/Components/Dialog/index.jsx
diff --git a/Client/src/Components/Dot/index.jsx b/src/Components/Dot/index.jsx
similarity index 100%
rename from Client/src/Components/Dot/index.jsx
rename to src/Components/Dot/index.jsx
diff --git a/Client/src/Components/Fallback/index.css b/src/Components/Fallback/index.css
similarity index 100%
rename from Client/src/Components/Fallback/index.css
rename to src/Components/Fallback/index.css
diff --git a/Client/src/Components/Fallback/index.jsx b/src/Components/Fallback/index.jsx
similarity index 100%
rename from Client/src/Components/Fallback/index.jsx
rename to src/Components/Fallback/index.jsx
diff --git a/Client/src/Components/GenericFallback/NetworkError.jsx b/src/Components/GenericFallback/NetworkError.jsx
similarity index 100%
rename from Client/src/Components/GenericFallback/NetworkError.jsx
rename to src/Components/GenericFallback/NetworkError.jsx
diff --git a/Client/src/Components/GenericFallback/index.jsx b/src/Components/GenericFallback/index.jsx
similarity index 100%
rename from Client/src/Components/GenericFallback/index.jsx
rename to src/Components/GenericFallback/index.jsx
diff --git a/Client/src/Components/HOC/withAdminCheck.jsx b/src/Components/HOC/withAdminCheck.jsx
similarity index 100%
rename from Client/src/Components/HOC/withAdminCheck.jsx
rename to src/Components/HOC/withAdminCheck.jsx
diff --git a/Client/src/Components/Heading/index.jsx b/src/Components/Heading/index.jsx
similarity index 100%
rename from Client/src/Components/Heading/index.jsx
rename to src/Components/Heading/index.jsx
diff --git a/Client/src/Components/Host/index.jsx b/src/Components/Host/index.jsx
similarity index 100%
rename from Client/src/Components/Host/index.jsx
rename to src/Components/Host/index.jsx
diff --git a/Client/src/Components/HttpStatusLabel/index.jsx b/src/Components/HttpStatusLabel/index.jsx
similarity index 100%
rename from Client/src/Components/HttpStatusLabel/index.jsx
rename to src/Components/HttpStatusLabel/index.jsx
diff --git a/Client/src/Components/IconBox/index.jsx b/src/Components/IconBox/index.jsx
similarity index 100%
rename from Client/src/Components/IconBox/index.jsx
rename to src/Components/IconBox/index.jsx
diff --git a/Client/src/Components/Image/index.jsx b/src/Components/Image/index.jsx
similarity index 100%
rename from Client/src/Components/Image/index.jsx
rename to src/Components/Image/index.jsx
diff --git a/Client/src/Components/Inputs/Checkbox/index.css b/src/Components/Inputs/Checkbox/index.css
similarity index 100%
rename from Client/src/Components/Inputs/Checkbox/index.css
rename to src/Components/Inputs/Checkbox/index.css
diff --git a/Client/src/Components/Inputs/Checkbox/index.jsx b/src/Components/Inputs/Checkbox/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/Checkbox/index.jsx
rename to src/Components/Inputs/Checkbox/index.jsx
diff --git a/Client/src/Components/Inputs/ColorPicker/index.jsx b/src/Components/Inputs/ColorPicker/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/ColorPicker/index.jsx
rename to src/Components/Inputs/ColorPicker/index.jsx
diff --git a/Client/src/Components/Inputs/Image/index.css b/src/Components/Inputs/Image/index.css
similarity index 100%
rename from Client/src/Components/Inputs/Image/index.css
rename to src/Components/Inputs/Image/index.css
diff --git a/Client/src/Components/Inputs/Image/index.jsx b/src/Components/Inputs/Image/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/Image/index.jsx
rename to src/Components/Inputs/Image/index.jsx
diff --git a/Client/src/Components/Inputs/Radio/index.css b/src/Components/Inputs/Radio/index.css
similarity index 100%
rename from Client/src/Components/Inputs/Radio/index.css
rename to src/Components/Inputs/Radio/index.css
diff --git a/Client/src/Components/Inputs/Radio/index.jsx b/src/Components/Inputs/Radio/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/Radio/index.jsx
rename to src/Components/Inputs/Radio/index.jsx
diff --git a/Client/src/Components/Inputs/Search/index.jsx b/src/Components/Inputs/Search/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/Search/index.jsx
rename to src/Components/Inputs/Search/index.jsx
diff --git a/Client/src/Components/Inputs/Select/index.css b/src/Components/Inputs/Select/index.css
similarity index 100%
rename from Client/src/Components/Inputs/Select/index.css
rename to src/Components/Inputs/Select/index.css
diff --git a/Client/src/Components/Inputs/Select/index.jsx b/src/Components/Inputs/Select/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/Select/index.jsx
rename to src/Components/Inputs/Select/index.jsx
diff --git a/Client/src/Components/Inputs/TextInput/Adornments/index.jsx b/src/Components/Inputs/TextInput/Adornments/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/TextInput/Adornments/index.jsx
rename to src/Components/Inputs/TextInput/Adornments/index.jsx
diff --git a/Client/src/Components/Inputs/TextInput/index.jsx b/src/Components/Inputs/TextInput/index.jsx
similarity index 100%
rename from Client/src/Components/Inputs/TextInput/index.jsx
rename to src/Components/Inputs/TextInput/index.jsx
diff --git a/Client/src/Components/Label/index.css b/src/Components/Label/index.css
similarity index 100%
rename from Client/src/Components/Label/index.css
rename to src/Components/Label/index.css
diff --git a/Client/src/Components/Label/index.jsx b/src/Components/Label/index.jsx
similarity index 100%
rename from Client/src/Components/Label/index.jsx
rename to src/Components/Label/index.jsx
diff --git a/Client/src/Components/LanguageSelector.jsx b/src/Components/LanguageSelector.jsx
similarity index 100%
rename from Client/src/Components/LanguageSelector.jsx
rename to src/Components/LanguageSelector.jsx
diff --git a/Client/src/Components/Layouts/HomeLayout/index.css b/src/Components/Layouts/HomeLayout/index.css
similarity index 100%
rename from Client/src/Components/Layouts/HomeLayout/index.css
rename to src/Components/Layouts/HomeLayout/index.css
diff --git a/Client/src/Components/Layouts/HomeLayout/index.jsx b/src/Components/Layouts/HomeLayout/index.jsx
similarity index 100%
rename from Client/src/Components/Layouts/HomeLayout/index.jsx
rename to src/Components/Layouts/HomeLayout/index.jsx
diff --git a/Client/src/Components/Link/index.jsx b/src/Components/Link/index.jsx
similarity index 100%
rename from Client/src/Components/Link/index.jsx
rename to src/Components/Link/index.jsx
diff --git a/Client/src/Components/Link/link.css b/src/Components/Link/link.css
similarity index 100%
rename from Client/src/Components/Link/link.css
rename to src/Components/Link/link.css
diff --git a/Client/src/Components/MonitorCountHeader/index.jsx b/src/Components/MonitorCountHeader/index.jsx
similarity index 100%
rename from Client/src/Components/MonitorCountHeader/index.jsx
rename to src/Components/MonitorCountHeader/index.jsx
diff --git a/Client/src/Components/MonitorCountHeader/skeleton.jsx b/src/Components/MonitorCountHeader/skeleton.jsx
similarity index 100%
rename from Client/src/Components/MonitorCountHeader/skeleton.jsx
rename to src/Components/MonitorCountHeader/skeleton.jsx
diff --git a/Client/src/Components/MonitorCreateHeader/index.jsx b/src/Components/MonitorCreateHeader/index.jsx
similarity index 100%
rename from Client/src/Components/MonitorCreateHeader/index.jsx
rename to src/Components/MonitorCreateHeader/index.jsx
diff --git a/Client/src/Components/MonitorCreateHeader/skeleton.jsx b/src/Components/MonitorCreateHeader/skeleton.jsx
similarity index 100%
rename from Client/src/Components/MonitorCreateHeader/skeleton.jsx
rename to src/Components/MonitorCreateHeader/skeleton.jsx
diff --git a/Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx b/src/Components/MonitorStatusHeader/ConfigButton/index.jsx
similarity index 100%
rename from Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx
rename to src/Components/MonitorStatusHeader/ConfigButton/index.jsx
diff --git a/Client/src/Components/MonitorStatusHeader/index.jsx b/src/Components/MonitorStatusHeader/index.jsx
similarity index 100%
rename from Client/src/Components/MonitorStatusHeader/index.jsx
rename to src/Components/MonitorStatusHeader/index.jsx
diff --git a/Client/src/Components/MonitorStatusHeader/skeleton.jsx b/src/Components/MonitorStatusHeader/skeleton.jsx
similarity index 100%
rename from Client/src/Components/MonitorStatusHeader/skeleton.jsx
rename to src/Components/MonitorStatusHeader/skeleton.jsx
diff --git a/Client/src/Components/MonitorTimeFrameHeader/index.jsx b/src/Components/MonitorTimeFrameHeader/index.jsx
similarity index 100%
rename from Client/src/Components/MonitorTimeFrameHeader/index.jsx
rename to src/Components/MonitorTimeFrameHeader/index.jsx
diff --git a/Client/src/Components/MonitorTimeFrameHeader/skeleton.jsx b/src/Components/MonitorTimeFrameHeader/skeleton.jsx
similarity index 100%
rename from Client/src/Components/MonitorTimeFrameHeader/skeleton.jsx
rename to src/Components/MonitorTimeFrameHeader/skeleton.jsx
diff --git a/Client/src/Components/ProgressBars/index.css b/src/Components/ProgressBars/index.css
similarity index 100%
rename from Client/src/Components/ProgressBars/index.css
rename to src/Components/ProgressBars/index.css
diff --git a/Client/src/Components/ProgressBars/index.jsx b/src/Components/ProgressBars/index.jsx
similarity index 100%
rename from Client/src/Components/ProgressBars/index.jsx
rename to src/Components/ProgressBars/index.jsx
diff --git a/Client/src/Components/ProgressStepper/index.css b/src/Components/ProgressStepper/index.css
similarity index 100%
rename from Client/src/Components/ProgressStepper/index.css
rename to src/Components/ProgressStepper/index.css
diff --git a/Client/src/Components/ProgressStepper/index.jsx b/src/Components/ProgressStepper/index.jsx
similarity index 100%
rename from Client/src/Components/ProgressStepper/index.jsx
rename to src/Components/ProgressStepper/index.jsx
diff --git a/Client/src/Components/ProtectedDistributedUptimeRoute/index.jsx b/src/Components/ProtectedDistributedUptimeRoute/index.jsx
similarity index 100%
rename from Client/src/Components/ProtectedDistributedUptimeRoute/index.jsx
rename to src/Components/ProtectedDistributedUptimeRoute/index.jsx
diff --git a/Client/src/Components/ProtectedRoute/index.jsx b/src/Components/ProtectedRoute/index.jsx
similarity index 100%
rename from Client/src/Components/ProtectedRoute/index.jsx
rename to src/Components/ProtectedRoute/index.jsx
diff --git a/Client/src/Components/Sidebar/index.css b/src/Components/Sidebar/index.css
similarity index 100%
rename from Client/src/Components/Sidebar/index.css
rename to src/Components/Sidebar/index.css
diff --git a/Client/src/Components/Sidebar/index.jsx b/src/Components/Sidebar/index.jsx
similarity index 100%
rename from Client/src/Components/Sidebar/index.jsx
rename to src/Components/Sidebar/index.jsx
diff --git a/Client/src/Components/Skeletons/FullPage/index.jsx b/src/Components/Skeletons/FullPage/index.jsx
similarity index 100%
rename from Client/src/Components/Skeletons/FullPage/index.jsx
rename to src/Components/Skeletons/FullPage/index.jsx
diff --git a/Client/src/Components/StatBox/index.jsx b/src/Components/StatBox/index.jsx
similarity index 100%
rename from Client/src/Components/StatBox/index.jsx
rename to src/Components/StatBox/index.jsx
diff --git a/Client/src/Components/StatusBoxes/index.jsx b/src/Components/StatusBoxes/index.jsx
similarity index 100%
rename from Client/src/Components/StatusBoxes/index.jsx
rename to src/Components/StatusBoxes/index.jsx
diff --git a/Client/src/Components/StatusBoxes/skeleton.jsx b/src/Components/StatusBoxes/skeleton.jsx
similarity index 100%
rename from Client/src/Components/StatusBoxes/skeleton.jsx
rename to src/Components/StatusBoxes/skeleton.jsx
diff --git a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx b/src/Components/TabPanels/Account/PasswordPanel.jsx
similarity index 100%
rename from Client/src/Components/TabPanels/Account/PasswordPanel.jsx
rename to src/Components/TabPanels/Account/PasswordPanel.jsx
diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/src/Components/TabPanels/Account/ProfilePanel.jsx
similarity index 100%
rename from Client/src/Components/TabPanels/Account/ProfilePanel.jsx
rename to src/Components/TabPanels/Account/ProfilePanel.jsx
diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/src/Components/TabPanels/Account/TeamPanel.jsx
similarity index 100%
rename from Client/src/Components/TabPanels/Account/TeamPanel.jsx
rename to src/Components/TabPanels/Account/TeamPanel.jsx
diff --git a/Client/src/Components/Table/TablePagination/Actions/index.jsx b/src/Components/Table/TablePagination/Actions/index.jsx
similarity index 100%
rename from Client/src/Components/Table/TablePagination/Actions/index.jsx
rename to src/Components/Table/TablePagination/Actions/index.jsx
diff --git a/Client/src/Components/Table/TablePagination/index.jsx b/src/Components/Table/TablePagination/index.jsx
similarity index 100%
rename from Client/src/Components/Table/TablePagination/index.jsx
rename to src/Components/Table/TablePagination/index.jsx
diff --git a/Client/src/Components/Table/index.jsx b/src/Components/Table/index.jsx
similarity index 100%
rename from Client/src/Components/Table/index.jsx
rename to src/Components/Table/index.jsx
diff --git a/Client/src/Components/Table/skeleton.jsx b/src/Components/Table/skeleton.jsx
similarity index 100%
rename from Client/src/Components/Table/skeleton.jsx
rename to src/Components/Table/skeleton.jsx
diff --git a/Client/src/Components/ThemeSwitch/SunAndMoonIcon.jsx b/src/Components/ThemeSwitch/SunAndMoonIcon.jsx
similarity index 100%
rename from Client/src/Components/ThemeSwitch/SunAndMoonIcon.jsx
rename to src/Components/ThemeSwitch/SunAndMoonIcon.jsx
diff --git a/Client/src/Components/ThemeSwitch/index.css b/src/Components/ThemeSwitch/index.css
similarity index 100%
rename from Client/src/Components/ThemeSwitch/index.css
rename to src/Components/ThemeSwitch/index.css
diff --git a/Client/src/Components/ThemeSwitch/index.jsx b/src/Components/ThemeSwitch/index.jsx
similarity index 100%
rename from Client/src/Components/ThemeSwitch/index.jsx
rename to src/Components/ThemeSwitch/index.jsx
diff --git a/Client/src/Components/WalletProvider/index.jsx b/src/Components/WalletProvider/index.jsx
similarity index 100%
rename from Client/src/Components/WalletProvider/index.jsx
rename to src/Components/WalletProvider/index.jsx
diff --git a/Client/src/Features/Auth/authSlice.js b/src/Features/Auth/authSlice.js
similarity index 100%
rename from Client/src/Features/Auth/authSlice.js
rename to src/Features/Auth/authSlice.js
diff --git a/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js b/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js
similarity index 100%
rename from Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js
rename to src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js
diff --git a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js b/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
similarity index 100%
rename from Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
rename to src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
diff --git a/Client/src/Features/Settings/settingsSlice.js b/src/Features/Settings/settingsSlice.js
similarity index 100%
rename from Client/src/Features/Settings/settingsSlice.js
rename to src/Features/Settings/settingsSlice.js
diff --git a/Client/src/Features/UI/uiSlice.js b/src/Features/UI/uiSlice.js
similarity index 100%
rename from Client/src/Features/UI/uiSlice.js
rename to src/Features/UI/uiSlice.js
diff --git a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
similarity index 100%
rename from Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
rename to src/Features/UptimeMonitors/uptimeMonitorsSlice.js
diff --git a/Client/src/Hooks/useIsAdmin.js b/src/Hooks/useIsAdmin.js
similarity index 100%
rename from Client/src/Hooks/useIsAdmin.js
rename to src/Hooks/useIsAdmin.js
diff --git a/Client/src/Hooks/useMonitorUtils.js b/src/Hooks/useMonitorUtils.js
similarity index 100%
rename from Client/src/Hooks/useMonitorUtils.js
rename to src/Hooks/useMonitorUtils.js
diff --git a/Client/src/Pages/Account/index.css b/src/Pages/Account/index.css
similarity index 100%
rename from Client/src/Pages/Account/index.css
rename to src/Pages/Account/index.css
diff --git a/Client/src/Pages/Account/index.jsx b/src/Pages/Account/index.jsx
similarity index 100%
rename from Client/src/Pages/Account/index.jsx
rename to src/Pages/Account/index.jsx
diff --git a/Client/src/Pages/Auth/CheckEmail.jsx b/src/Pages/Auth/CheckEmail.jsx
similarity index 100%
rename from Client/src/Pages/Auth/CheckEmail.jsx
rename to src/Pages/Auth/CheckEmail.jsx
diff --git a/Client/src/Pages/Auth/ForgotPassword.jsx b/src/Pages/Auth/ForgotPassword.jsx
similarity index 100%
rename from Client/src/Pages/Auth/ForgotPassword.jsx
rename to src/Pages/Auth/ForgotPassword.jsx
diff --git a/Client/src/Pages/Auth/Login/Components/EmailStep.jsx b/src/Pages/Auth/Login/Components/EmailStep.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Login/Components/EmailStep.jsx
rename to src/Pages/Auth/Login/Components/EmailStep.jsx
diff --git a/Client/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx b/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx
rename to src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx
diff --git a/Client/src/Pages/Auth/Login/Components/PasswordStep.jsx b/src/Pages/Auth/Login/Components/PasswordStep.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Login/Components/PasswordStep.jsx
rename to src/Pages/Auth/Login/Components/PasswordStep.jsx
diff --git a/Client/src/Pages/Auth/Login/Login.jsx b/src/Pages/Auth/Login/Login.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Login/Login.jsx
rename to src/Pages/Auth/Login/Login.jsx
diff --git a/Client/src/Pages/Auth/NewPasswordConfirmed.jsx b/src/Pages/Auth/NewPasswordConfirmed.jsx
similarity index 100%
rename from Client/src/Pages/Auth/NewPasswordConfirmed.jsx
rename to src/Pages/Auth/NewPasswordConfirmed.jsx
diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/src/Pages/Auth/Register/Register.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Register/Register.jsx
rename to src/Pages/Auth/Register/Register.jsx
diff --git a/Client/src/Pages/Auth/Register/StepOne/index.jsx b/src/Pages/Auth/Register/StepOne/index.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Register/StepOne/index.jsx
rename to src/Pages/Auth/Register/StepOne/index.jsx
diff --git a/Client/src/Pages/Auth/Register/StepThree/index.jsx b/src/Pages/Auth/Register/StepThree/index.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Register/StepThree/index.jsx
rename to src/Pages/Auth/Register/StepThree/index.jsx
diff --git a/Client/src/Pages/Auth/Register/StepTwo/index.jsx b/src/Pages/Auth/Register/StepTwo/index.jsx
similarity index 100%
rename from Client/src/Pages/Auth/Register/StepTwo/index.jsx
rename to src/Pages/Auth/Register/StepTwo/index.jsx
diff --git a/Client/src/Pages/Auth/SetNewPassword.jsx b/src/Pages/Auth/SetNewPassword.jsx
similarity index 100%
rename from Client/src/Pages/Auth/SetNewPassword.jsx
rename to src/Pages/Auth/SetNewPassword.jsx
diff --git a/Client/src/Pages/Auth/hooks/useValidatePassword.jsx b/src/Pages/Auth/hooks/useValidatePassword.jsx
similarity index 100%
rename from Client/src/Pages/Auth/hooks/useValidatePassword.jsx
rename to src/Pages/Auth/hooks/useValidatePassword.jsx
diff --git a/Client/src/Pages/Auth/index.css b/src/Pages/Auth/index.css
similarity index 100%
rename from Client/src/Pages/Auth/index.css
rename to src/Pages/Auth/index.css
diff --git a/Client/src/Pages/DistributedUptime/Create/Hooks/useCreateDistributedUptimeMonitor.jsx b/src/Pages/DistributedUptime/Create/Hooks/useCreateDistributedUptimeMonitor.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Create/Hooks/useCreateDistributedUptimeMonitor.jsx
rename to src/Pages/DistributedUptime/Create/Hooks/useCreateDistributedUptimeMonitor.jsx
diff --git a/Client/src/Pages/DistributedUptime/Create/Hooks/useMonitorFetch.jsx b/src/Pages/DistributedUptime/Create/Hooks/useMonitorFetch.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Create/Hooks/useMonitorFetch.jsx
rename to src/Pages/DistributedUptime/Create/Hooks/useMonitorFetch.jsx
diff --git a/Client/src/Pages/DistributedUptime/Create/index.jsx b/src/Pages/DistributedUptime/Create/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Create/index.jsx
rename to src/Pages/DistributedUptime/Create/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx b/src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx b/src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx b/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/Footer/index.jsx b/src/Pages/DistributedUptime/Details/Components/Footer/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/Footer/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/Footer/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx b/src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx b/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx b/src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx b/src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx b/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx
rename to src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx b/src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx
rename to src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx b/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx
rename to src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx
diff --git a/Client/src/Pages/DistributedUptime/Details/index.jsx b/src/Pages/DistributedUptime/Details/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Details/index.jsx
rename to src/Pages/DistributedUptime/Details/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx b/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx
rename to src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Monitors/Components/Skeleton/index.jsx b/src/Pages/DistributedUptime/Monitors/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Monitors/Components/Skeleton/index.jsx
rename to src/Pages/DistributedUptime/Monitors/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx b/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx
rename to src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx
diff --git a/Client/src/Pages/DistributedUptime/Monitors/index.jsx b/src/Pages/DistributedUptime/Monitors/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptime/Monitors/index.jsx
rename to src/Pages/DistributedUptime/Monitors/index.jsx
diff --git a/Client/src/Pages/DistributedUptimeStatus/Create/Components/VisuallyHiddenInput/index.jsx b/src/Pages/DistributedUptimeStatus/Create/Components/VisuallyHiddenInput/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptimeStatus/Create/Components/VisuallyHiddenInput/index.jsx
rename to src/Pages/DistributedUptimeStatus/Create/Components/VisuallyHiddenInput/index.jsx
diff --git a/Client/src/Pages/DistributedUptimeStatus/Create/index.jsx b/src/Pages/DistributedUptimeStatus/Create/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptimeStatus/Create/index.jsx
rename to src/Pages/DistributedUptimeStatus/Create/index.jsx
diff --git a/Client/src/Pages/DistributedUptimeStatus/Status/Components/Skeleton/index.jsx b/src/Pages/DistributedUptimeStatus/Status/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptimeStatus/Status/Components/Skeleton/index.jsx
rename to src/Pages/DistributedUptimeStatus/Status/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/DistributedUptimeStatus/Status/Hooks/useStatusPageFetchByUrl.jsx b/src/Pages/DistributedUptimeStatus/Status/Hooks/useStatusPageFetchByUrl.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptimeStatus/Status/Hooks/useStatusPageFetchByUrl.jsx
rename to src/Pages/DistributedUptimeStatus/Status/Hooks/useStatusPageFetchByUrl.jsx
diff --git a/Client/src/Pages/DistributedUptimeStatus/Status/index.jsx b/src/Pages/DistributedUptimeStatus/Status/index.jsx
similarity index 100%
rename from Client/src/Pages/DistributedUptimeStatus/Status/index.jsx
rename to src/Pages/DistributedUptimeStatus/Status/index.jsx
diff --git a/Client/src/Pages/Incidents/Components/IncidentTable/index.jsx b/src/Pages/Incidents/Components/IncidentTable/index.jsx
similarity index 100%
rename from Client/src/Pages/Incidents/Components/IncidentTable/index.jsx
rename to src/Pages/Incidents/Components/IncidentTable/index.jsx
diff --git a/Client/src/Pages/Incidents/Components/OptionsHeader/index.jsx b/src/Pages/Incidents/Components/OptionsHeader/index.jsx
similarity index 100%
rename from Client/src/Pages/Incidents/Components/OptionsHeader/index.jsx
rename to src/Pages/Incidents/Components/OptionsHeader/index.jsx
diff --git a/Client/src/Pages/Incidents/Components/OptionsHeader/skeleton.jsx b/src/Pages/Incidents/Components/OptionsHeader/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Incidents/Components/OptionsHeader/skeleton.jsx
rename to src/Pages/Incidents/Components/OptionsHeader/skeleton.jsx
diff --git a/Client/src/Pages/Incidents/Hooks/useChecksFetch.jsx b/src/Pages/Incidents/Hooks/useChecksFetch.jsx
similarity index 100%
rename from Client/src/Pages/Incidents/Hooks/useChecksFetch.jsx
rename to src/Pages/Incidents/Hooks/useChecksFetch.jsx
diff --git a/Client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx b/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx
similarity index 100%
rename from Client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx
rename to src/Pages/Incidents/Hooks/useMonitorsFetch.jsx
diff --git a/Client/src/Pages/Incidents/index.jsx b/src/Pages/Incidents/index.jsx
similarity index 100%
rename from Client/src/Pages/Incidents/index.jsx
rename to src/Pages/Incidents/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Create/Components/CustomThreshold/index.jsx b/src/Pages/Infrastructure/Create/Components/CustomThreshold/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Create/Components/CustomThreshold/index.jsx
rename to src/Pages/Infrastructure/Create/Components/CustomThreshold/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Create/index.jsx b/src/Pages/Infrastructure/Create/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Create/index.jsx
rename to src/Pages/Infrastructure/Create/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx b/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx
rename to src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx b/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx
rename to src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx b/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx
rename to src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx b/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx
rename to src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx b/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx
rename to src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx b/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx
rename to src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx b/src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx
rename to src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx b/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx
rename to src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx b/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx
rename to src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx b/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
rename to src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
diff --git a/Client/src/Pages/Infrastructure/Details/index.jsx b/src/Pages/Infrastructure/Details/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Details/index.jsx
rename to src/Pages/Infrastructure/Details/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx b/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx
rename to src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx b/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx
rename to src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx
diff --git a/Client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx b/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx
rename to src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx
diff --git a/Client/src/Pages/Infrastructure/Monitors/index.jsx b/src/Pages/Infrastructure/Monitors/index.jsx
similarity index 100%
rename from Client/src/Pages/Infrastructure/Monitors/index.jsx
rename to src/Pages/Infrastructure/Monitors/index.jsx
diff --git a/Client/src/Pages/Integrations/index.css b/src/Pages/Integrations/index.css
similarity index 100%
rename from Client/src/Pages/Integrations/index.css
rename to src/Pages/Integrations/index.css
diff --git a/Client/src/Pages/Integrations/index.jsx b/src/Pages/Integrations/index.jsx
similarity index 100%
rename from Client/src/Pages/Integrations/index.jsx
rename to src/Pages/Integrations/index.jsx
diff --git a/Client/src/Pages/Maintenance/CreateMaintenance/index.css b/src/Pages/Maintenance/CreateMaintenance/index.css
similarity index 100%
rename from Client/src/Pages/Maintenance/CreateMaintenance/index.css
rename to src/Pages/Maintenance/CreateMaintenance/index.css
diff --git a/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/src/Pages/Maintenance/CreateMaintenance/index.jsx
similarity index 100%
rename from Client/src/Pages/Maintenance/CreateMaintenance/index.jsx
rename to src/Pages/Maintenance/CreateMaintenance/index.jsx
diff --git a/Client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx b/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx
similarity index 100%
rename from Client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx
rename to src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx
diff --git a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx b/src/Pages/Maintenance/MaintenanceTable/index.jsx
similarity index 100%
rename from Client/src/Pages/Maintenance/MaintenanceTable/index.jsx
rename to src/Pages/Maintenance/MaintenanceTable/index.jsx
diff --git a/Client/src/Pages/Maintenance/index.css b/src/Pages/Maintenance/index.css
similarity index 100%
rename from Client/src/Pages/Maintenance/index.css
rename to src/Pages/Maintenance/index.css
diff --git a/Client/src/Pages/Maintenance/index.jsx b/src/Pages/Maintenance/index.jsx
similarity index 100%
rename from Client/src/Pages/Maintenance/index.jsx
rename to src/Pages/Maintenance/index.jsx
diff --git a/Client/src/Pages/NotFound/index.jsx b/src/Pages/NotFound/index.jsx
similarity index 100%
rename from Client/src/Pages/NotFound/index.jsx
rename to src/Pages/NotFound/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Configure/index.css b/src/Pages/PageSpeed/Configure/index.css
similarity index 100%
rename from Client/src/Pages/PageSpeed/Configure/index.css
rename to src/Pages/PageSpeed/Configure/index.css
diff --git a/Client/src/Pages/PageSpeed/Configure/index.jsx b/src/Pages/PageSpeed/Configure/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Configure/index.jsx
rename to src/Pages/PageSpeed/Configure/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Configure/skeleton.jsx b/src/Pages/PageSpeed/Configure/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Configure/skeleton.jsx
rename to src/Pages/PageSpeed/Configure/skeleton.jsx
diff --git a/Client/src/Pages/PageSpeed/Create/index.css b/src/Pages/PageSpeed/Create/index.css
similarity index 100%
rename from Client/src/Pages/PageSpeed/Create/index.css
rename to src/Pages/PageSpeed/Create/index.css
diff --git a/Client/src/Pages/PageSpeed/Create/index.jsx b/src/Pages/PageSpeed/Create/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Create/index.jsx
rename to src/Pages/PageSpeed/Create/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx b/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx
rename to src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx b/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx
rename to src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx b/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx
rename to src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx b/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx
rename to src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx b/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx
rename to src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx b/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx
rename to src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx b/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx
rename to src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx b/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx
rename to src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx b/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx
rename to src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx b/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx
rename to src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx
diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/src/Pages/PageSpeed/Details/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Details/index.jsx
rename to src/Pages/PageSpeed/Details/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx b/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx
rename to src/Pages/PageSpeed/Monitors/Components/Card/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx b/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx
rename to src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx
diff --git a/Client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx b/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx
rename to src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx
diff --git a/Client/src/Pages/PageSpeed/Monitors/index.jsx b/src/Pages/PageSpeed/Monitors/index.jsx
similarity index 100%
rename from Client/src/Pages/PageSpeed/Monitors/index.jsx
rename to src/Pages/PageSpeed/Monitors/index.jsx
diff --git a/Client/src/Pages/Settings/index.jsx b/src/Pages/Settings/index.jsx
similarity index 100%
rename from Client/src/Pages/Settings/index.jsx
rename to src/Pages/Settings/index.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Components/MonitorList/index.jsx b/src/Pages/StatusPage/Create/Components/MonitorList/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Components/MonitorList/index.jsx
rename to src/Pages/StatusPage/Create/Components/MonitorList/index.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Components/Progress/index.jsx b/src/Pages/StatusPage/Create/Components/Progress/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Components/Progress/index.jsx
rename to src/Pages/StatusPage/Create/Components/Progress/index.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Components/Skeleton/index.jsx b/src/Pages/StatusPage/Create/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Components/Skeleton/index.jsx
rename to src/Pages/StatusPage/Create/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Components/Tabs/Content.jsx b/src/Pages/StatusPage/Create/Components/Tabs/Content.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Components/Tabs/Content.jsx
rename to src/Pages/StatusPage/Create/Components/Tabs/Content.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx b/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx
rename to src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Components/Tabs/index.jsx b/src/Pages/StatusPage/Create/Components/Tabs/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Components/Tabs/index.jsx
rename to src/Pages/StatusPage/Create/Components/Tabs/index.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx b/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx
rename to src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx
diff --git a/Client/src/Pages/StatusPage/Create/Hooks/useMonitorsFetch.jsx b/src/Pages/StatusPage/Create/Hooks/useMonitorsFetch.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/Hooks/useMonitorsFetch.jsx
rename to src/Pages/StatusPage/Create/Hooks/useMonitorsFetch.jsx
diff --git a/Client/src/Pages/StatusPage/Create/index.jsx b/src/Pages/StatusPage/Create/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Create/index.jsx
rename to src/Pages/StatusPage/Create/index.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Components/AdminLink/index.jsx b/src/Pages/StatusPage/Status/Components/AdminLink/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Components/AdminLink/index.jsx
rename to src/Pages/StatusPage/Status/Components/AdminLink/index.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx b/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx
rename to src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx b/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx
rename to src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Components/Skeleton/index.jsx b/src/Pages/StatusPage/Status/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Components/Skeleton/index.jsx
rename to src/Pages/StatusPage/Status/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Components/StatusBar/index.jsx b/src/Pages/StatusPage/Status/Components/StatusBar/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Components/StatusBar/index.jsx
rename to src/Pages/StatusPage/Status/Components/StatusBar/index.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Hooks/useStatusPageDelete.jsx b/src/Pages/StatusPage/Status/Hooks/useStatusPageDelete.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Hooks/useStatusPageDelete.jsx
rename to src/Pages/StatusPage/Status/Hooks/useStatusPageDelete.jsx
diff --git a/Client/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx b/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx
rename to src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx
diff --git a/Client/src/Pages/StatusPage/Status/index.jsx b/src/Pages/StatusPage/Status/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/Status/index.jsx
rename to src/Pages/StatusPage/Status/index.jsx
diff --git a/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx b/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx
rename to src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx
diff --git a/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx b/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx
rename to src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx
diff --git a/Client/src/Pages/StatusPage/StatusPages/index.jsx b/src/Pages/StatusPage/StatusPages/index.jsx
similarity index 100%
rename from Client/src/Pages/StatusPage/StatusPages/index.jsx
rename to src/Pages/StatusPage/StatusPages/index.jsx
diff --git a/Client/src/Pages/Uptime/Configure/index.css b/src/Pages/Uptime/Configure/index.css
similarity index 100%
rename from Client/src/Pages/Uptime/Configure/index.css
rename to src/Pages/Uptime/Configure/index.css
diff --git a/Client/src/Pages/Uptime/Configure/index.jsx b/src/Pages/Uptime/Configure/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Configure/index.jsx
rename to src/Pages/Uptime/Configure/index.jsx
diff --git a/Client/src/Pages/Uptime/Configure/skeleton.jsx b/src/Pages/Uptime/Configure/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Configure/skeleton.jsx
rename to src/Pages/Uptime/Configure/skeleton.jsx
diff --git a/Client/src/Pages/Uptime/Create/index.css b/src/Pages/Uptime/Create/index.css
similarity index 100%
rename from Client/src/Pages/Uptime/Create/index.css
rename to src/Pages/Uptime/Create/index.css
diff --git a/Client/src/Pages/Uptime/Create/index.jsx b/src/Pages/Uptime/Create/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Create/index.jsx
rename to src/Pages/Uptime/Create/index.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx b/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
rename to src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/ChartBoxes/skeleton.jsx b/src/Pages/Uptime/Details/Components/ChartBoxes/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/ChartBoxes/skeleton.jsx
rename to src/Pages/Uptime/Details/Components/ChartBoxes/skeleton.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/CustomLabels.jsx b/src/Pages/Uptime/Details/Components/Charts/CustomLabels.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/Charts/CustomLabels.jsx
rename to src/Pages/Uptime/Details/Components/Charts/CustomLabels.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx b/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx
rename to src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseGaugeChart.jsx b/src/Pages/Uptime/Details/Components/Charts/ResponseGaugeChart.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/Charts/ResponseGaugeChart.jsx
rename to src/Pages/Uptime/Details/Components/Charts/ResponseGaugeChart.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx b/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
rename to src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx b/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx
rename to src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx b/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx
rename to src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx b/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
rename to src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx b/src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx
rename to src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx
diff --git a/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx b/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx
rename to src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx
diff --git a/Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx b/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx
rename to src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx
diff --git a/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx b/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx
rename to src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx
diff --git a/Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx b/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx
rename to src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx
diff --git a/Client/src/Pages/Uptime/Details/index.jsx b/src/Pages/Uptime/Details/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Details/index.jsx
rename to src/Pages/Uptime/Details/index.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/LoadingSpinner/index.jsx b/src/Pages/Uptime/Monitors/Components/LoadingSpinner/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/LoadingSpinner/index.jsx
rename to src/Pages/Uptime/Monitors/Components/LoadingSpinner/index.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx b/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx
rename to src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/Skeleton/index.jsx b/src/Pages/Uptime/Monitors/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/Skeleton/index.jsx
rename to src/Pages/Uptime/Monitors/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/StatusBoxes/index.jsx b/src/Pages/Uptime/Monitors/Components/StatusBoxes/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/StatusBoxes/index.jsx
rename to src/Pages/Uptime/Monitors/Components/StatusBoxes/index.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/StatusBoxes/skeleton.jsx b/src/Pages/Uptime/Monitors/Components/StatusBoxes/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/StatusBoxes/skeleton.jsx
rename to src/Pages/Uptime/Monitors/Components/StatusBoxes/skeleton.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/StatusBoxes/statusBox.jsx b/src/Pages/Uptime/Monitors/Components/StatusBoxes/statusBox.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/StatusBoxes/statusBox.jsx
rename to src/Pages/Uptime/Monitors/Components/StatusBoxes/statusBox.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx b/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx
rename to src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useDebounce.jsx b/src/Pages/Uptime/Monitors/Hooks/useDebounce.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Hooks/useDebounce.jsx
rename to src/Pages/Uptime/Monitors/Hooks/useDebounce.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx b/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx
rename to src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx b/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx
rename to src/Pages/Uptime/Monitors/Hooks/useUtils.jsx
diff --git a/Client/src/Pages/Uptime/Monitors/index.jsx b/src/Pages/Uptime/Monitors/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Monitors/index.jsx
rename to src/Pages/Uptime/Monitors/index.jsx
diff --git a/Client/src/Routes/index.jsx b/src/Routes/index.jsx
similarity index 100%
rename from Client/src/Routes/index.jsx
rename to src/Routes/index.jsx
diff --git a/Client/src/Utils/Logger.js b/src/Utils/Logger.js
similarity index 100%
rename from Client/src/Utils/Logger.js
rename to src/Utils/Logger.js
diff --git a/Client/src/Utils/NetworkService.js b/src/Utils/NetworkService.js
similarity index 100%
rename from Client/src/Utils/NetworkService.js
rename to src/Utils/NetworkService.js
diff --git a/Client/src/Utils/NetworkServiceProvider.jsx b/src/Utils/NetworkServiceProvider.jsx
similarity index 100%
rename from Client/src/Utils/NetworkServiceProvider.jsx
rename to src/Utils/NetworkServiceProvider.jsx
diff --git a/Client/src/Utils/ReadMe.md b/src/Utils/ReadMe.md
similarity index 100%
rename from Client/src/Utils/ReadMe.md
rename to src/Utils/ReadMe.md
diff --git a/Client/src/Utils/Theme/constants.js b/src/Utils/Theme/constants.js
similarity index 100%
rename from Client/src/Utils/Theme/constants.js
rename to src/Utils/Theme/constants.js
diff --git a/Client/src/Utils/Theme/darkTheme.js b/src/Utils/Theme/darkTheme.js
similarity index 100%
rename from Client/src/Utils/Theme/darkTheme.js
rename to src/Utils/Theme/darkTheme.js
diff --git a/Client/src/Utils/Theme/extractColorObject.js b/src/Utils/Theme/extractColorObject.js
similarity index 100%
rename from Client/src/Utils/Theme/extractColorObject.js
rename to src/Utils/Theme/extractColorObject.js
diff --git a/Client/src/Utils/Theme/globalTheme.js b/src/Utils/Theme/globalTheme.js
similarity index 100%
rename from Client/src/Utils/Theme/globalTheme.js
rename to src/Utils/Theme/globalTheme.js
diff --git a/Client/src/Utils/Theme/lightTheme.js b/src/Utils/Theme/lightTheme.js
similarity index 100%
rename from Client/src/Utils/Theme/lightTheme.js
rename to src/Utils/Theme/lightTheme.js
diff --git a/Client/src/Utils/debounce.jsx b/src/Utils/debounce.jsx
similarity index 100%
rename from Client/src/Utils/debounce.jsx
rename to src/Utils/debounce.jsx
diff --git a/Client/src/Utils/fileUtils.js b/src/Utils/fileUtils.js
similarity index 100%
rename from Client/src/Utils/fileUtils.js
rename to src/Utils/fileUtils.js
diff --git a/Client/src/Utils/greeting.jsx b/src/Utils/greeting.jsx
similarity index 100%
rename from Client/src/Utils/greeting.jsx
rename to src/Utils/greeting.jsx
diff --git a/Client/src/Utils/i18n.js b/src/Utils/i18n.js
similarity index 100%
rename from Client/src/Utils/i18n.js
rename to src/Utils/i18n.js
diff --git a/Client/src/Utils/monitorUtils.js b/src/Utils/monitorUtils.js
similarity index 100%
rename from Client/src/Utils/monitorUtils.js
rename to src/Utils/monitorUtils.js
diff --git a/Client/src/Utils/stringUtils.js b/src/Utils/stringUtils.js
similarity index 100%
rename from Client/src/Utils/stringUtils.js
rename to src/Utils/stringUtils.js
diff --git a/Client/src/Utils/timeUtils.js b/src/Utils/timeUtils.js
similarity index 100%
rename from Client/src/Utils/timeUtils.js
rename to src/Utils/timeUtils.js
diff --git a/Client/src/Utils/timezones.json b/src/Utils/timezones.json
similarity index 100%
rename from Client/src/Utils/timezones.json
rename to src/Utils/timezones.json
diff --git a/Client/src/Utils/toastUtils.jsx b/src/Utils/toastUtils.jsx
similarity index 100%
rename from Client/src/Utils/toastUtils.jsx
rename to src/Utils/toastUtils.jsx
diff --git a/Client/src/Validation/error.js b/src/Validation/error.js
similarity index 100%
rename from Client/src/Validation/error.js
rename to src/Validation/error.js
diff --git a/Client/src/Validation/validation.js b/src/Validation/validation.js
similarity index 100%
rename from Client/src/Validation/validation.js
rename to src/Validation/validation.js
diff --git a/Client/src/assets/Images/Google.png b/src/assets/Images/Google.png
similarity index 100%
rename from Client/src/assets/Images/Google.png
rename to src/assets/Images/Google.png
diff --git a/Client/src/assets/Images/avatar_placeholder.png b/src/assets/Images/avatar_placeholder.png
similarity index 100%
rename from Client/src/assets/Images/avatar_placeholder.png
rename to src/assets/Images/avatar_placeholder.png
diff --git a/Client/src/assets/Images/background-grid.svg b/src/assets/Images/background-grid.svg
similarity index 100%
rename from Client/src/assets/Images/background-grid.svg
rename to src/assets/Images/background-grid.svg
diff --git a/Client/src/assets/Images/bwl-logo.svg b/src/assets/Images/bwl-logo.svg
similarity index 100%
rename from Client/src/assets/Images/bwl-logo.svg
rename to src/assets/Images/bwl-logo.svg
diff --git a/Client/src/assets/Images/create-placeholder-dark.svg b/src/assets/Images/create-placeholder-dark.svg
similarity index 100%
rename from Client/src/assets/Images/create-placeholder-dark.svg
rename to src/assets/Images/create-placeholder-dark.svg
diff --git a/Client/src/assets/Images/create-placeholder.svg b/src/assets/Images/create-placeholder.svg
similarity index 100%
rename from Client/src/assets/Images/create-placeholder.svg
rename to src/assets/Images/create-placeholder.svg
diff --git a/Client/src/assets/Images/data_placeholder.svg b/src/assets/Images/data_placeholder.svg
similarity index 100%
rename from Client/src/assets/Images/data_placeholder.svg
rename to src/assets/Images/data_placeholder.svg
diff --git a/Client/src/assets/Images/data_placeholder_dark.svg b/src/assets/Images/data_placeholder_dark.svg
similarity index 100%
rename from Client/src/assets/Images/data_placeholder_dark.svg
rename to src/assets/Images/data_placeholder_dark.svg
diff --git a/Client/src/assets/Images/logo_placeholder.svg b/src/assets/Images/logo_placeholder.svg
similarity index 100%
rename from Client/src/assets/Images/logo_placeholder.svg
rename to src/assets/Images/logo_placeholder.svg
diff --git a/Client/src/assets/Images/sushi_404.svg b/src/assets/Images/sushi_404.svg
similarity index 100%
rename from Client/src/assets/Images/sushi_404.svg
rename to src/assets/Images/sushi_404.svg
diff --git a/Client/src/assets/icons/average-response-icon.svg b/src/assets/icons/average-response-icon.svg
similarity index 100%
rename from Client/src/assets/icons/average-response-icon.svg
rename to src/assets/icons/average-response-icon.svg
diff --git a/Client/src/assets/icons/calendar-check.svg b/src/assets/icons/calendar-check.svg
similarity index 100%
rename from Client/src/assets/icons/calendar-check.svg
rename to src/assets/icons/calendar-check.svg
diff --git a/Client/src/assets/icons/calendar.svg b/src/assets/icons/calendar.svg
similarity index 100%
rename from Client/src/assets/icons/calendar.svg
rename to src/assets/icons/calendar.svg
diff --git a/Client/src/assets/icons/certificate.svg b/src/assets/icons/certificate.svg
similarity index 100%
rename from Client/src/assets/icons/certificate.svg
rename to src/assets/icons/certificate.svg
diff --git a/Client/src/assets/icons/changeLog.svg b/src/assets/icons/changeLog.svg
similarity index 100%
rename from Client/src/assets/icons/changeLog.svg
rename to src/assets/icons/changeLog.svg
diff --git a/Client/src/assets/icons/check-outlined.svg b/src/assets/icons/check-outlined.svg
similarity index 100%
rename from Client/src/assets/icons/check-outlined.svg
rename to src/assets/icons/check-outlined.svg
diff --git a/Client/src/assets/icons/check.svg b/src/assets/icons/check.svg
similarity index 100%
rename from Client/src/assets/icons/check.svg
rename to src/assets/icons/check.svg
diff --git a/Client/src/assets/icons/checkbox-filled.svg b/src/assets/icons/checkbox-filled.svg
similarity index 100%
rename from Client/src/assets/icons/checkbox-filled.svg
rename to src/assets/icons/checkbox-filled.svg
diff --git a/Client/src/assets/icons/checkbox-green.svg b/src/assets/icons/checkbox-green.svg
similarity index 100%
rename from Client/src/assets/icons/checkbox-green.svg
rename to src/assets/icons/checkbox-green.svg
diff --git a/Client/src/assets/icons/checkbox-outline.svg b/src/assets/icons/checkbox-outline.svg
similarity index 100%
rename from Client/src/assets/icons/checkbox-outline.svg
rename to src/assets/icons/checkbox-outline.svg
diff --git a/Client/src/assets/icons/checkbox-red.svg b/src/assets/icons/checkbox-red.svg
similarity index 100%
rename from Client/src/assets/icons/checkbox-red.svg
rename to src/assets/icons/checkbox-red.svg
diff --git a/Client/src/assets/icons/checkmate-icon.svg b/src/assets/icons/checkmate-icon.svg
similarity index 100%
rename from Client/src/assets/icons/checkmate-icon.svg
rename to src/assets/icons/checkmate-icon.svg
diff --git a/Client/src/assets/icons/clock-snooze.svg b/src/assets/icons/clock-snooze.svg
similarity index 100%
rename from Client/src/assets/icons/clock-snooze.svg
rename to src/assets/icons/clock-snooze.svg
diff --git a/Client/src/assets/icons/cpu-chip.svg b/src/assets/icons/cpu-chip.svg
similarity index 100%
rename from Client/src/assets/icons/cpu-chip.svg
rename to src/assets/icons/cpu-chip.svg
diff --git a/Client/src/assets/icons/dashboard.svg b/src/assets/icons/dashboard.svg
similarity index 100%
rename from Client/src/assets/icons/dashboard.svg
rename to src/assets/icons/dashboard.svg
diff --git a/Client/src/assets/icons/discord-icon.svg b/src/assets/icons/discord-icon.svg
similarity index 100%
rename from Client/src/assets/icons/discord-icon.svg
rename to src/assets/icons/discord-icon.svg
diff --git a/Client/src/assets/icons/distributed-uptime.svg b/src/assets/icons/distributed-uptime.svg
similarity index 100%
rename from Client/src/assets/icons/distributed-uptime.svg
rename to src/assets/icons/distributed-uptime.svg
diff --git a/Client/src/assets/icons/docs.svg b/src/assets/icons/docs.svg
similarity index 100%
rename from Client/src/assets/icons/docs.svg
rename to src/assets/icons/docs.svg
diff --git a/Client/src/assets/icons/dots-vertical.svg b/src/assets/icons/dots-vertical.svg
similarity index 100%
rename from Client/src/assets/icons/dots-vertical.svg
rename to src/assets/icons/dots-vertical.svg
diff --git a/Client/src/assets/icons/down-arrow.svg b/src/assets/icons/down-arrow.svg
similarity index 100%
rename from Client/src/assets/icons/down-arrow.svg
rename to src/assets/icons/down-arrow.svg
diff --git a/Client/src/assets/icons/edit.svg b/src/assets/icons/edit.svg
similarity index 100%
rename from Client/src/assets/icons/edit.svg
rename to src/assets/icons/edit.svg
diff --git a/Client/src/assets/icons/email.svg b/src/assets/icons/email.svg
similarity index 100%
rename from Client/src/assets/icons/email.svg
rename to src/assets/icons/email.svg
diff --git a/Client/src/assets/icons/folder.svg b/src/assets/icons/folder.svg
similarity index 100%
rename from Client/src/assets/icons/folder.svg
rename to src/assets/icons/folder.svg
diff --git a/Client/src/assets/icons/history-icon.svg b/src/assets/icons/history-icon.svg
similarity index 100%
rename from Client/src/assets/icons/history-icon.svg
rename to src/assets/icons/history-icon.svg
diff --git a/Client/src/assets/icons/incidents.svg b/src/assets/icons/incidents.svg
similarity index 100%
rename from Client/src/assets/icons/incidents.svg
rename to src/assets/icons/incidents.svg
diff --git a/Client/src/assets/icons/integrations.svg b/src/assets/icons/integrations.svg
similarity index 100%
rename from Client/src/assets/icons/integrations.svg
rename to src/assets/icons/integrations.svg
diff --git a/Client/src/assets/icons/interval-check.svg b/src/assets/icons/interval-check.svg
similarity index 100%
rename from Client/src/assets/icons/interval-check.svg
rename to src/assets/icons/interval-check.svg
diff --git a/Client/src/assets/icons/key.svg b/src/assets/icons/key.svg
similarity index 100%
rename from Client/src/assets/icons/key.svg
rename to src/assets/icons/key.svg
diff --git a/Client/src/assets/icons/left-arrow-double.svg b/src/assets/icons/left-arrow-double.svg
similarity index 100%
rename from Client/src/assets/icons/left-arrow-double.svg
rename to src/assets/icons/left-arrow-double.svg
diff --git a/Client/src/assets/icons/left-arrow-long.svg b/src/assets/icons/left-arrow-long.svg
similarity index 100%
rename from Client/src/assets/icons/left-arrow-long.svg
rename to src/assets/icons/left-arrow-long.svg
diff --git a/Client/src/assets/icons/left-arrow.svg b/src/assets/icons/left-arrow.svg
similarity index 100%
rename from Client/src/assets/icons/left-arrow.svg
rename to src/assets/icons/left-arrow.svg
diff --git a/Client/src/assets/icons/lock.svg b/src/assets/icons/lock.svg
similarity index 100%
rename from Client/src/assets/icons/lock.svg
rename to src/assets/icons/lock.svg
diff --git a/Client/src/assets/icons/logout.svg b/src/assets/icons/logout.svg
similarity index 100%
rename from Client/src/assets/icons/logout.svg
rename to src/assets/icons/logout.svg
diff --git a/Client/src/assets/icons/mail.svg b/src/assets/icons/mail.svg
similarity index 100%
rename from Client/src/assets/icons/mail.svg
rename to src/assets/icons/mail.svg
diff --git a/Client/src/assets/icons/maintenance.svg b/src/assets/icons/maintenance.svg
similarity index 100%
rename from Client/src/assets/icons/maintenance.svg
rename to src/assets/icons/maintenance.svg
diff --git a/Client/src/assets/icons/monitor-graph-line.svg b/src/assets/icons/monitor-graph-line.svg
similarity index 100%
rename from Client/src/assets/icons/monitor-graph-line.svg
rename to src/assets/icons/monitor-graph-line.svg
diff --git a/Client/src/assets/icons/monitors.svg b/src/assets/icons/monitors.svg
similarity index 100%
rename from Client/src/assets/icons/monitors.svg
rename to src/assets/icons/monitors.svg
diff --git a/Client/src/assets/icons/open-in-new-page.svg b/src/assets/icons/open-in-new-page.svg
similarity index 100%
rename from Client/src/assets/icons/open-in-new-page.svg
rename to src/assets/icons/open-in-new-page.svg
diff --git a/Client/src/assets/icons/page-speed.svg b/src/assets/icons/page-speed.svg
similarity index 100%
rename from Client/src/assets/icons/page-speed.svg
rename to src/assets/icons/page-speed.svg
diff --git a/Client/src/assets/icons/pause-icon.svg b/src/assets/icons/pause-icon.svg
similarity index 100%
rename from Client/src/assets/icons/pause-icon.svg
rename to src/assets/icons/pause-icon.svg
diff --git a/Client/src/assets/icons/performance-report.svg b/src/assets/icons/performance-report.svg
similarity index 100%
rename from Client/src/assets/icons/performance-report.svg
rename to src/assets/icons/performance-report.svg
diff --git a/Client/src/assets/icons/radio-checked.svg b/src/assets/icons/radio-checked.svg
similarity index 100%
rename from Client/src/assets/icons/radio-checked.svg
rename to src/assets/icons/radio-checked.svg
diff --git a/Client/src/assets/icons/response-time-icon.svg b/src/assets/icons/response-time-icon.svg
similarity index 100%
rename from Client/src/assets/icons/response-time-icon.svg
rename to src/assets/icons/response-time-icon.svg
diff --git a/Client/src/assets/icons/resume-icon.svg b/src/assets/icons/resume-icon.svg
similarity index 100%
rename from Client/src/assets/icons/resume-icon.svg
rename to src/assets/icons/resume-icon.svg
diff --git a/Client/src/assets/icons/right-arrow-double.svg b/src/assets/icons/right-arrow-double.svg
similarity index 100%
rename from Client/src/assets/icons/right-arrow-double.svg
rename to src/assets/icons/right-arrow-double.svg
diff --git a/Client/src/assets/icons/right-arrow.svg b/src/assets/icons/right-arrow.svg
similarity index 100%
rename from Client/src/assets/icons/right-arrow.svg
rename to src/assets/icons/right-arrow.svg
diff --git a/Client/src/assets/icons/ruler-icon.svg b/src/assets/icons/ruler-icon.svg
similarity index 100%
rename from Client/src/assets/icons/ruler-icon.svg
rename to src/assets/icons/ruler-icon.svg
diff --git a/Client/src/assets/icons/search.svg b/src/assets/icons/search.svg
similarity index 100%
rename from Client/src/assets/icons/search.svg
rename to src/assets/icons/search.svg
diff --git a/Client/src/assets/icons/selector-vertical.svg b/src/assets/icons/selector-vertical.svg
similarity index 100%
rename from Client/src/assets/icons/selector-vertical.svg
rename to src/assets/icons/selector-vertical.svg
diff --git a/Client/src/assets/icons/settings-bold.svg b/src/assets/icons/settings-bold.svg
similarity index 100%
rename from Client/src/assets/icons/settings-bold.svg
rename to src/assets/icons/settings-bold.svg
diff --git a/Client/src/assets/icons/settings.svg b/src/assets/icons/settings.svg
similarity index 100%
rename from Client/src/assets/icons/settings.svg
rename to src/assets/icons/settings.svg
diff --git a/Client/src/assets/icons/slack-icon.svg b/src/assets/icons/slack-icon.svg
similarity index 100%
rename from Client/src/assets/icons/slack-icon.svg
rename to src/assets/icons/slack-icon.svg
diff --git a/Client/src/assets/icons/solana_logo.svg b/src/assets/icons/solana_logo.svg
similarity index 100%
rename from Client/src/assets/icons/solana_logo.svg
rename to src/assets/icons/solana_logo.svg
diff --git a/Client/src/assets/icons/speedometer-icon.svg b/src/assets/icons/speedometer-icon.svg
similarity index 100%
rename from Client/src/assets/icons/speedometer-icon.svg
rename to src/assets/icons/speedometer-icon.svg
diff --git a/Client/src/assets/icons/status-pages.svg b/src/assets/icons/status-pages.svg
similarity index 100%
rename from Client/src/assets/icons/status-pages.svg
rename to src/assets/icons/status-pages.svg
diff --git a/Client/src/assets/icons/support.svg b/src/assets/icons/support.svg
similarity index 100%
rename from Client/src/assets/icons/support.svg
rename to src/assets/icons/support.svg
diff --git a/Client/src/assets/icons/top-right-arrow.svg b/src/assets/icons/top-right-arrow.svg
similarity index 100%
rename from Client/src/assets/icons/top-right-arrow.svg
rename to src/assets/icons/top-right-arrow.svg
diff --git a/Client/src/assets/icons/trash-bin.svg b/src/assets/icons/trash-bin.svg
similarity index 100%
rename from Client/src/assets/icons/trash-bin.svg
rename to src/assets/icons/trash-bin.svg
diff --git a/Client/src/assets/icons/up-arrow.svg b/src/assets/icons/up-arrow.svg
similarity index 100%
rename from Client/src/assets/icons/up-arrow.svg
rename to src/assets/icons/up-arrow.svg
diff --git a/Client/src/assets/icons/upt_logo.png b/src/assets/icons/upt_logo.png
similarity index 100%
rename from Client/src/assets/icons/upt_logo.png
rename to src/assets/icons/upt_logo.png
diff --git a/Client/src/assets/icons/uptime-icon.svg b/src/assets/icons/uptime-icon.svg
similarity index 100%
rename from Client/src/assets/icons/uptime-icon.svg
rename to src/assets/icons/uptime-icon.svg
diff --git a/Client/src/assets/icons/user-edit.svg b/src/assets/icons/user-edit.svg
similarity index 100%
rename from Client/src/assets/icons/user-edit.svg
rename to src/assets/icons/user-edit.svg
diff --git a/Client/src/assets/icons/user-two.svg b/src/assets/icons/user-two.svg
similarity index 100%
rename from Client/src/assets/icons/user-two.svg
rename to src/assets/icons/user-two.svg
diff --git a/Client/src/assets/icons/user.svg b/src/assets/icons/user.svg
similarity index 100%
rename from Client/src/assets/icons/user.svg
rename to src/assets/icons/user.svg
diff --git a/Client/src/assets/icons/zapier-icon.svg b/src/assets/icons/zapier-icon.svg
similarity index 100%
rename from Client/src/assets/icons/zapier-icon.svg
rename to src/assets/icons/zapier-icon.svg
diff --git a/Client/src/index.css b/src/index.css
similarity index 100%
rename from Client/src/index.css
rename to src/index.css
diff --git a/Client/src/locales/gb.json b/src/locales/gb.json
similarity index 100%
rename from Client/src/locales/gb.json
rename to src/locales/gb.json
diff --git a/Client/src/locales/tr.json b/src/locales/tr.json
similarity index 100%
rename from Client/src/locales/tr.json
rename to src/locales/tr.json
diff --git a/Client/src/main.jsx b/src/main.jsx
similarity index 100%
rename from Client/src/main.jsx
rename to src/main.jsx
diff --git a/Client/src/store.js b/src/store.js
similarity index 100%
rename from Client/src/store.js
rename to src/store.js
diff --git a/Client/vite.config.js b/vite.config.js
similarity index 100%
rename from Client/vite.config.js
rename to vite.config.js