Merge branch 're-navbar-side-menu' of https://github.com/MuhammadKhalilzadeh/bluewave-uptime into re-navbar-side-menu

This commit is contained in:
MuhammadKhalilzadeh
2024-06-06 22:06:03 +03:30
15 changed files with 2561 additions and 266 deletions

537
README.md
View File

@@ -9,6 +9,9 @@ BlueWave uptime monitoring application
1. [Installation (Client)](#client)
2. [Installation (Server)](#server)
3. [Configuration(Server)](#config-server)
- [Environment](#environmental-variables)
- [Database](#databases)
- [Docker Images](#docker-images)
4. [Endpoints](#endpoints)
###### Auth
- <code>POST</code> [/api/v1/auth/register](#post-register)
@@ -59,18 +62,62 @@ BlueWave uptime monitoring application
---
#### Configuration {#config-server}
#### Configuration <a id="config-server"></a>
Configure the server with the following environmental variables
##### Environmental Variables
| ENV Variable Name | Required/Optional | Type | Description | Accepted Values |
| -------------------- | ----------------- | --------- | ----------------------------------------------------- | ------------------- |
| JWT_SECRET | Required | `string` | JWT secret | |
| DB_TYPE | Optional | `string` | Specify DB to use | `MongoDB \| FakeDB` |
| DB_CONNECTION_STRING | Required | `string` | Specifies URL for MongoDB Database | |
| PORT | Optional | `integer` | Specifies Port for Server | |
| MAILERSEND_API_KEY | Required | `string` | Specifies API KEY for MailerSend service | |
| SYSTEM_EMAIL_ADDRESS | Required | `string` | Specifies System email to be used in emailing service | |
Configure the server with the following environmental variables:
| ENV Variable Name | Required/Optional | Type | Description | Accepted Values |
| -------------------- | ----------------- | --------- | ------------------------------------------------------------------------------------------- | ------------------- |
| JWT_SECRET | Required | `string` | JWT secret | |
| DB_TYPE | Optional | `string` | Specify DB to use | `MongoDB \| FakeDB` |
| DB_CONNECTION_STRING | Required | `string` | Specifies URL for MongoDB Database | |
| PORT | Optional | `integer` | Specifies Port for Server | |
| SENDGRID_API_KEY | Required | `string` | Specifies API KEY for SendGrid email service | |
| SYSTEM_EMAIL_ADDRESS | Required | `string` | Specifies System email to be used in emailing service, must be a verified email by sendgrid | |
| LOGIN_PAGE_URL | Required | `string` | Login url to be used in emailing service | |
| REDIS_HOST | Required | `string` | Host address for Redis database | |
| REDIS_PORT | Required | `integer` | Port for Redis database | |
---
##### Databases
This project requires a number of databases to run:
1. Main database for the application. This project includes an implementation for a MongoDB database as well as a MongoDB Docker image.
2. A Redis database is required for the Queue implementation in the PingService. This project includes a Redis docker image.
You may run your own databases locally, or you may use the docker images included in the project to get up and running quickly.
###### (Optional) Running Docker Images <a id="docker-images"></a>
Docker images are located in `./Server/docker`
<details>
<summary><b>MongoDB Image</b></summary>
Located in `./Server/docker/mongo`
The `./Server/docker/mongo/mongo_data` folder should be mounted to the MongoDB container in order to persist data.
From the `mongo` folder run
1. Build the image: `docker build -t <db_image_name> .`
2. Run the docker image: `docker run -d -p 27017:27017 -v $(pwd)/../mongo/mongo_data:/data/db --name uptime_database_mongo uptime_database_mongo`
</details>
<details>
<summary><b>Redis Image</b></summary>
Located in `./Server/docker/redis`
the `./Server/docker/redis/redis_data` folder should be mounted to the Redis container in order to persist data.
From the `Redis` folder run
1. Build the image: `docker build -t <db_image_name>`
2. Run the image: `docker run -d -p 6379:6379 -v $(pwd)/../redis/redis_data:/data --name uptime_redis uptime_redis`
</details>
---
@@ -1110,7 +1157,475 @@ curl --request POST \
---
### Error handling {#error-handling}
###### Checks
<details>
<summary id='post-checks'><code>POST</code><b>/api/v1/checks/{monitorId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | POST |
###### Response Payload
> | Type | Notes |
> | ------- | --------------------------- |
> | `Check` | Returns newly created check |
###### Body
> | Name | Type | Notes |
> | ------------ | --------- | -------------------------------------- |
> | monitorId | `string` | Monitor associated with Check |
> | status | `boolean` | `true` for up and `false` for down |
> | responseTime | `number` | How long it took the server to respond |
> | statusCode | `number` | HTTP Status code of response |
> | message | `string` | |
##### Sample CURL request
```
curl --request POST \
--url http://localhost:5000/api/v1/checks/66562414035c4ce6a8a610ac \
--header 'Authorization: <bearer_token>' \
--header 'Content-Type: application/json' \
--data '{
"monitorId": "66562414035c4ce6a8a610ac",
"status": true,
"responseTime": 1,
"statusCode": 200,
"message": "good"
}'
```
###### Sample Response
```json
{
"success": true,
"msg": "Check created",
"data": {
"monitorId": "66562414035c4ce6a8a610ac",
"status": true,
"responseTime": 1,
"statusCode": 200,
"message": "good",
"_id": "66576decba9f70148ea1f354",
"createdAt": "2024-05-29T18:03:24.445Z",
"updatedAt": "2024-05-29T18:03:24.445Z",
"__v": 0
}
}
```
</details>
<details>
<summary id='get-checks'><code>GET</code><b>/api/v1/checks/{monitorId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | GET |
###### Response Payload
> | Type | Notes |
> | --------------- | ------------------------ |
> | `Array<Checks>` | Array of `Check` objects |
##### Sample CURL request
```
curl --request GET \
--url http://localhost:5000/api/v1/checks/66562414035c4ce6a8a610ac \
--header 'Authorization: <bearer_token>' \
```
###### Sample Response
```json
{
"success": true,
"msg": "Checks retrieved",
"data": [
{
"_id": "66576c0194e11c0d4409d3c1",
"monitorId": "66562414035c4ce6a8a610ac",
"status": true,
"responseTime": 1,
"statusCode": 200,
"message": "good",
"createdAt": "2024-05-29T17:55:13.581Z",
"updatedAt": "2024-05-29T17:55:13.581Z",
"__v": 0
},
{
"_id": "66576c0994e11c0d4409d3c5",
"monitorId": "66562414035c4ce6a8a610ac",
"status": true,
"responseTime": 2,
"statusCode": 200,
"message": "good",
"createdAt": "2024-05-29T17:55:21.127Z",
"updatedAt": "2024-05-29T17:55:21.127Z",
"__v": 0
}
]
}
```
</details>
<details>
<summary id='delete-checks'><code>POST</code><b>/api/v1/checks/delete/{monitorId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | POST |
###### Response Payload
> | Type | Notes |
> | -------- | -------------------------------------------------------------------- |
> | `Object` | `{deletedCount: n}` Returns an object showing how many items deleted |
##### Sample CURL request
```
curl --request POST \
--url http://localhost:5000/api/v1/checks/delete/66562414035c4ce6a8a610ac \
--header 'Authorization: <bearer_token>' \
```
###### Sample Response
```json
{
"success": true,
"msg": "Checks deleted",
"data": {
"deletedCount": 3
}
}
```
</details>
---
###### Alerts
<details>
<summary id='create-alert'><code>POST</code><b>/api/v1/alerts/{monitorId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | POST |
###### Response Payload
> | Type | Notes |
> | ------- | ----------------------------- |
> | `Alert` | Returns newly created `Alert` |
###### Body
"checkId": "66577a3fd16dcf7c1ce35148",
"monitorId": "6657789ebf6766ee8e2d2edb",
"userId": "6654d1a2634754f789e1f115",
"status": false,
"message": "This is a test alert",
"notifiedStatus": "false",
"acknowledgeStatus": false
> | Name | Type | Notes |
> | ----------------- | --------- | --------------------------------------- |
> | checkId | `string` | Id of `Check` associated with `Alert` |
> | monitorId | `string` | Id of `Monitor` associated with `Alert` |
> | userId | `string` | Id of `User` associated with `Alert` |
> | status | `boolean` | Status of `Alert` |
> | message | `string` | `Alert` message |
> | notifiedStatus | `boolean` | |
> | acknowledgeStatus | `boolean` | |
##### Sample CURL request
```
```
###### Sample Response
```json
```
</details>
<details>
<summary id='get-alerts-user-id'><code>GET</code><b>/api/v1/alerts/user/{userId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | GET |
###### Response Payload
> | Type | Notes |
> | -------------- | --------------------------------------- |
> | `Array<Alert>` | Returns all `Alert` created by a `User` |
##### Sample CURL request
```
curl --request GET \
--url http://localhost:5000/api/v1/alerts/user/6654d1a2634754f789e1f115 \
--header 'Authorization: <bearer_token>'
```
###### Sample Response
```json
{
"success": true,
"msg": "Got alerts",
"data": [
{
"_id": "6657813d809adfded891a6b7",
"checkId": "66577a3fd16dcf7c1ce35148",
"monitorId": "6657789ebf6766ee8e2d2edb",
"userId": "6654d1a2634754f789e1f115",
"status": false,
"message": "This is a test alert",
"notifiedStatus": false,
"acknowledgeStatus": false,
"createdAt": "2024-05-29T19:25:49.317Z",
"updatedAt": "2024-05-29T19:25:49.317Z",
"__v": 0
}
]
}
```
</details>
<details>
<summary id='get-alerts-monitor-id'><code>GET</code><b>/api/v1/alerts/monitor/{monitorId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | GET |
###### Response Payload
> | Type | Notes |
> | -------------- | -------------------------------------------------------------- |
> | `Array<Alert>` | Returns an array of `Alert` belonging to a specified `Monitor` |
##### Sample CURL request
```
curl --request GET \
--url http://localhost:5000/api/v1/alerts/monitor/6657789ebf6766ee8e2d2edb \
--header 'Authorization: <bearer_token>' \
```
###### Sample Response
```json
{
"success": true,
"msg": "Got alerts by Monitor",
"data": [
{
"_id": "6657813d809adfded891a6b7",
"checkId": "66577a3fd16dcf7c1ce35148",
"monitorId": "6657789ebf6766ee8e2d2edb",
"userId": "6654d1a2634754f789e1f115",
"status": false,
"message": "This is a test alert",
"notifiedStatus": false,
"acknowledgeStatus": false,
"createdAt": "2024-05-29T19:25:49.317Z",
"updatedAt": "2024-05-29T19:25:49.317Z",
"__v": 0
}
]
}
```
</details>
<details>
<summary id='get-alert-alert-id'><code>GET</code><b>/api/v1/alerts/{alertId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | GET |
###### Response Payload
> | Type | Notes |
> | ------- | ------------------------- |
> | `Alert` | Returns specified `Alert` |
##### Sample CURL request
```
curl --request GET \
--url http://localhost:5000/api/v1/alerts/66577ddae5ff3c91437d0887 \
--header 'Authorization: <bearer_token>' \
```
###### Sample Response
```json
{
"success": true,
"msg": "Got Alert By alertID",
"data": {
"_id": "66577ddae5ff3c91437d0887",
"checkId": "66577a3fd16dcf7c1ce35148",
"monitorId": "6657789ebf6766ee8e2d2edb",
"userId": "6654d1a2634754f789e1f115",
"status": false,
"message": "This is a test alert",
"notifiedStatus": false,
"acknowledgeStatus": false,
"createdAt": "2024-05-29T19:11:22.205Z",
"updatedAt": "2024-05-29T19:11:22.205Z",
"__v": 0
}
}
```
</details>
<details>
<summary id='edit-alert'><code>POST</code><b>/api/v1/alerts/edit/{alertId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | POST |
###### Response Payload
> | Type | Notes |
> | ------- | ---------------------- |
> | `Alert` | Returns edited `Alert` |
###### Body
> | Name | Type | Notes |
> | ----------------- | --------- | ------------------------------------------ |
> | checkId | `string` | ID of `Check` associated with `Alert` |
> | monitorId | `string` | ID of `Monitor` id associated with `Alert` |
> | userId | `string` | ID of `User` associated with `Alert` |
> | status | `boolean` | Alert status |
> | message | `string` | Alert message |
> | notifiedStatus | `boolean` | |
> | acknowledgeStatus | `boolean` | |
##### Sample CURL request
```
curl --request POST \
--url http://localhost:5000/api/v1/alerts/edit/66577ddae5ff3c91437d0887 \
--header 'Authorization: <bearer_token>' \
--header 'Content-Type: application/json' \
--data '{
"acknowledgeStatus": true
}'
```
###### Sample Response
```json
{
"success": true,
"msg": "Edited alert",
"data": {
"_id": "66577ddae5ff3c91437d0887",
"checkId": "66577a3fd16dcf7c1ce35148",
"monitorId": "6657789ebf6766ee8e2d2edb",
"userId": "6654d1a2634754f789e1f115",
"status": false,
"message": "This is a test alert",
"notifiedStatus": false,
"acknowledgeStatus": true,
"createdAt": "2024-05-29T19:11:22.205Z",
"updatedAt": "2024-05-29T19:12:23.951Z",
"__v": 0
}
}
```
</details>
<details>
<summary id='delete-alert'><code>POST</code><b>/api/v1/alerts/delete/{alertId}</b></summary>
###### Method/Headers
> | Method/Headers | Value |
> | -------------- | ----- |
> | Method | POST |
###### Response Payload
> | Type | Notes |
> | ------- | --------------------------- |
> | `Alert` | Returns the deleted `Alert` |
##### Sample CURL request
```
curl --request POST \
--url http://localhost:5000/api/v1/alerts/delete/66577ddae5ff3c91437d0887 \
--header 'Authorization: <bearer_token>' \
```
###### Sample Response
```json
{
"success": true,
"msg": "Deleted alert",
"data": {
"_id": "66577ddae5ff3c91437d0887",
"checkId": "66577a3fd16dcf7c1ce35148",
"monitorId": "6657789ebf6766ee8e2d2edb",
"userId": "6654d1a2634754f789e1f115",
"status": false,
"message": "This is a test alert",
"notifiedStatus": false,
"acknowledgeStatus": true,
"createdAt": "2024-05-29T19:11:22.205Z",
"updatedAt": "2024-05-29T19:12:23.951Z",
"__v": 0
}
}
```
</details>
---
### Error handling
Errors are returned in a standard format:

5
Server/.gitignore vendored
View File

@@ -1,3 +1,6 @@
node_modules
.env
*.log
*.log
*.sh
docker/mongo/mongo_data/*
docker/redis/redis_data/*

View File

@@ -9,6 +9,8 @@ const logger = require("../utils/logger");
require("dotenv").config();
var jwt = require("jsonwebtoken");
const SERVICE_NAME = "auth";
const { sendEmail } = require('../utils/sendEmail')
const { registerTemplate } = require('../utils/emailTemplates/registerTemplate')
/**
* Creates and returns JWT token with an arbitrary payload
@@ -53,13 +55,15 @@ const registerController = async (req, res, next) => {
// Create a new user
try {
const newUser = await req.db.insertUser(req, res);
// TODO: Send an email to user
// Will add this later
logger.info("New user created!", {
service: SERVICE_NAME,
userId: newUser._id,
});
const token = issueToken(newUser._doc);
// Sending email to user with pre defined template
const template = registerTemplate('https://www.bluewavelabs.ca');
await sendEmail([newUser.email], 'Welcome to Uptime Monitor', template, 'Registered.');
return res
.status(200)

View File

@@ -0,0 +1,42 @@
const express = require("express");
const logger = require("../utils/logger");
const SERVICE_NAME = "JobQueue";
const getJobs = async (req, res, next) => {
try {
const jobs = await req.jobQueue.getJobs();
return res.status(200).json({ jobs });
} catch (error) {
error.service = SERVICE_NAME;
next(error);
return;
}
};
const addJob = async (req, res, next) => {
try {
await req.jobQueue.addJob(Math.random().toString(36).substring(7));
return res.send("Added job");
} catch (error) {
error.service = SERVICE_NAME;
next(error);
return;
}
};
const obliterateQueue = async (req, res, next) => {
try {
const obliterated = await req.jobQueue.obliterate();
return res.status(200).send("Obliterated jobs");
} catch (error) {
error.service = SERVICE_NAME;
next(error);
return;
}
};
module.exports = {
getJobs,
addJob,
obliterateQueue,
};

View File

@@ -0,0 +1,2 @@
FROM mongo
EXPOSE 27017

View File

@@ -0,0 +1,2 @@
FROM redis
EXPOSE 6379

View File

@@ -10,84 +10,91 @@ require("dotenv").config();
const logger = require("./utils/logger");
const { verifyJWT } = require("./middleware/verifyJWT");
const { handleErrors } = require("./middleware/handleErrors");
const queueRouter = require("./routes/queueRoute");
const JobQueue = require("./service/jobQueue");
// const { sendEmail } = require('./utils/sendEmail')
// Need to wrap server setup in a function to handle async nature of JobQueue
const startApp = async () => {
// const { sendEmail } = require('./utils/sendEmail')
// **************************
// Here is where we can swap out DBs easily. Spin up a mongoDB instance and try it out.
// Simply comment out the FakeDB and uncomment the MongoDB or vice versa.
// We can easily swap between any type of data source as long as the methods are implemented
//
// FakeDB
// const db = require("./db/FakeDb");
//
// MongoDB
// const db = require("./db/MongoDB");
//
// **************************
// **************************
// Here is where we can swap out DBs easily. Spin up a mongoDB instance and try it out.
// Simply comment out the FakeDB and uncomment the MongoDB or vice versa.
// We can easily swap between any type of data source as long as the methods are implemented
//
// FakeDB
// const db = require("./db/FakeDb");
//
// MongoDB
// const db = require("./db/MongoDB");
//
// **************************
const DB_TYPE = {
MongoDB: () => require("./db/MongoDB"),
FakedDB: () => require("./db/FakeDb"),
};
const DB_TYPE = {
MongoDB: () => require("./db/MongoDB"),
FakedDB: () => require("./db/FakeDb"),
const db = DB_TYPE[process.env.DB_TYPE]
? DB_TYPE[process.env.DB_TYPE]()
: require("./db/FakeDb");
const jobQueue = await JobQueue.createJobQueue();
/**
* NOTES
* Email Service will be added
* Logger Service will be added (Winston or similar)
*/
const app = express();
// middlewares
app.use(
cors()
//We will add configuration later
);
app.use(express.json());
app.use(helmet());
// **************************
// Make DB accessible anywhere we have a Request object
// By adding the DB to the request object, we can access it in any route
// Thus we do not need to import it in every route file, and we can easily swap out DBs as there is only one place to change it
// Same applies for JobQueue
// **************************
app.use((req, res, next) => {
req.db = db;
req.jobQueue = jobQueue;
next();
});
//routes
app.use("/api/v1/auth", authRouter);
app.use("/api/v1/monitors", monitorRouter);
app.use("/api/v1/checks", verifyJWT, checkRouter);
app.use("/api/v1/alerts", verifyJWT, alertRouter);
//Temporary route for testing, remove later
app.use("/api/v1/job", queueRouter);
//health check
app.use("/api/v1/healthy", (req, res) => {
try {
logger.info("Checking Health of the server.");
return res.status(200).json({ message: "Healthy" });
} catch (error) {
logger.error(error.message);
return res.status(500).json({ message: error.message });
}
});
/**
* Error handler middleware
* Should be called last
*/
app.use(handleErrors);
connectDbAndRunServer(app, db);
};
const db = DB_TYPE[process.env.DB_TYPE]
? DB_TYPE[process.env.DB_TYPE]()
: require("./db/FakeDb");
/**
* NOTES
* Email Service will be added
* Logger Service will be added (Winston or similar)
*/
const app = express();
// middlewares
app.use(
cors()
//We will add configuration later
);
app.use(express.json());
app.use(helmet());
// **************************
// Make DB accessible anywhere we have a Request object
// By adding the DB to the request object, we can access it in any route
// Thus we do not need to import it in every route file, and we can easily swap out DBs as there is only one place to change it
// **************************
app.use((req, res, next) => {
req.db = db;
next();
startApp().catch((error) => {
console.log(error);
});
//routes
app.use("/api/v1/auth", authRouter);
app.use("/api/v1/monitors", monitorRouter);
app.use("/api/v1/checks", verifyJWT, checkRouter);
app.use("/api/v1/alerts", verifyJWT, alertRouter);
// Testing email service
// app.use('/sendEmail', async (req, res) => {
// const response = sendEmail(['veysel.boybay@bluewavelabs.ca'], 'Testing email service', '<h1>Testing Bluewavelabs</h1>');
// console.log(response);
// })
//health check
app.use("/api/v1/healthy", (req, res) => {
try {
logger.info("Checking Health of the server.");
return res.status(200).json({ message: "Healthy" });
} catch (error) {
logger.error(error.message);
return res.status(500).json({ message: error.message });
}
});
/**
* Error handler middleware
* Should be called last
*/
app.use(handleErrors);
connectDbAndRunServer(app, db);

899
Server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,9 @@
"author": "",
"license": "ISC",
"dependencies": {
"@sendgrid/mail": "^8.1.3",
"bcrypt": "^5.1.1",
"bullmq": "5.7.15",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",

View File

@@ -0,0 +1,13 @@
const router = require("express").Router();
const queueController = require("../controllers/queueController");
// Get Jobs
router.get("/", queueController.getJobs);
// Add Job
router.post("/", queueController.addJob);
// Obliterate Queue
router.post("/obliterate", queueController.obliterateQueue);
module.exports = router;

195
Server/service/jobQueue.js Normal file
View File

@@ -0,0 +1,195 @@
const { Queue, Worker, Job } = require("bullmq");
const QUEUE_NAME = "monitors";
const connection = {
host: process.env.REDIS_HOST || "127.0.0.1",
port: process.env.REDIS_PORT || 6379,
};
const JOBS_PER_WORKER = 5;
const logger = require("../utils/logger");
class JobQueue {
/**
* Constructs a new JobQueue
* @constructor
* @throws {Error}
*/
constructor() {
console.log(process.env.REDIS_PORT);
this.queue = new Queue(QUEUE_NAME, {
connection,
});
this.workers = [];
}
/**
* Static factory method to create a JobQueue
* @static
* @async
* @returns {Promise<JobQueue>} - Returns a new JobQueue
*
*/
static async createJobQueue() {
const queue = new JobQueue();
try {
const workerStats = await queue.getWorkerStats();
await queue.scaleWorkers(workerStats);
return queue;
} catch (error) {
throw error;
}
}
/**
* Creates a worker for the queue
* Operations are carried out in the async callback
* @returns {Worker} The newly created worker
*/
createWorker() {
const worker = new Worker(
QUEUE_NAME,
async (job) => {
// TODO Ping a monitor
console.log(`${job.name} completed, workers: ${this.workers.length}`);
},
{
connection,
}
);
return worker;
}
/**
* @typedef {Object} WorkerStats
* @property {Array<Job>} jobs - Array of jobs in the Queue
* @property {number} - workerLoad - The number of jobs per 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<WorkerStats>} - Returns the worker stats
*/
async getWorkerStats() {
try {
const jobs = await this.queue.getRepeatableJobs();
const load = jobs.length / this.workers.length;
return { jobs, load };
} catch (error) {
throw error;
}
}
/**
* Scale Workers
* This function scales workers based on the load per worker
* If the load is higher than the JOBS_PER_WORKER threshold, we add more workers
* If the load is lower than the JOBS_PER_WORKER threshold, we release workers
* This approach ignores server performance, which we should add in the future
*
* @async
* @param {WorkerStats} workerStats - The payload for the job.
* @returns {Promise<boolean>}
*/
async scaleWorkers(workerStats) {
if (this.workers.length === 0) {
// There are no workers, need to add one
const worker = this.createWorker();
this.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 - this.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();
this.workers.push(worker);
}
return true;
}
if (workerStats.load < JOBS_PER_WORKER) {
// Find out how much excess capacity we have
const workerCapacity = this.workers.length * JOBS_PER_WORKER;
const excessCapacity = workerCapacity - workerStats.jobs.length;
// Calculate how many workers to remove
const workersToRemove = Math.floor(excessCapacity / JOBS_PER_WORKER);
for (let i = 0; i < workersToRemove; i++) {
const worker = this.workers.pop();
try {
await worker.close();
} catch (error) {
// Catch the error instead of throwing it
console.error("Error closing worker", error);
}
}
return true;
}
return false;
}
/**
* Gets all jobs in the queue.
*
* @async
* @returns {Promise<Array<Job>>}
* @throws {Error} - Throws error if getting jobs fails
*/
async getJobs() {
try {
const jobs = await this.queue.getRepeatableJobs();
console.log("jobs", jobs);
return jobs;
} catch (error) {
throw error;
}
}
/**
* Adds a job to the queue and scales workers based on worker stats.
*
* @async
* @param {string} jobName - The name of the job to be added.
* @param {Monitor} payload - The payload for the job.
* @throws {Error} - Will throw an error if the job cannot be added or workers don't scale
*/
async addJob(jobName, payload) {
try {
await this.queue.add(jobName, payload, {
repeat: {
every: 1000,
limit: 100,
},
});
const workerStats = await this.getWorkerStats();
await this.scaleWorkers(workerStats);
} catch (error) {
throw error;
}
}
/**
* @async
* @returns {Promise<boolean>} - Returns true if obliteration is successful
*/
async obliterate() {
try {
await this.queue.obliterate();
return true;
} catch (error) {
throw error;
}
}
}
module.exports = JobQueue;

View File

@@ -0,0 +1,469 @@
/**
*
* @param {String} loginUrl - login Url shared with registered user.
* @param {String} monitorName
* @param {String} timeStamp
* @returns {String} returns html content as String
*/
const downAlertTemplate = (loginUrl, monitorName, timeStamp) => {
return `
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="x-apple-disable-message-reformatting">
<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]-->
<title></title>
<style type="text/css">
@media only screen and (min-width: 570px) {
.u-row {
width: 550px !important;
}
.u-row .u-col {
vertical-align: top;
}
.u-row .u-col-100 {
width: 550px !important;
}
}
@media (max-width: 570px) {
.u-row-container {
max-width: 100% !important;
padding-left: 0px !important;
padding-right: 0px !important;
}
.u-row .u-col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important;
}
.u-row {
width: 100% !important;
}
.u-col {
width: 100% !important;
}
.u-col > div {
margin: 0 auto;
}
}
body {
margin: 0;
padding: 0;
}
table,
tr,
td {
vertical-align: top;
border-collapse: collapse;
}
p {
margin: 0;
}
.ie-container table,
.mso-container table {
table-layout: fixed;
}
* {
line-height: inherit;
}
a[x-apple-data-detectors='true'] {
color: inherit !important;
text-decoration: none !important;
}
table, td { color: #000000; } #u_body a { color: #0000ee; text-decoration: underline; }
</style>
<!--[if !mso]><!--><link href="https://fonts.googleapis.com/css?family=Lato:400,700&display=swap" rel="stylesheet" type="text/css"><link href="https://fonts.googleapis.com/css?family=Lato:400,700&display=swap" rel="stylesheet" type="text/css"><!--<![endif]-->
</head>
<body class="clean-body u_body" style="margin: 0;padding: 0;-webkit-text-size-adjust: 100%;background-color: #e0e5eb;color: #000000">
<!--[if IE]><div class="ie-container"><![endif]-->
<!--[if mso]><div class="mso-container"><![endif]-->
<table id="u_body" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;min-width: 320px;Margin: 0 auto;background-color: #e0e5eb;width:100%" cellpadding="0" cellspacing="0">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color: #e0e5eb;"><![endif]-->
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: transparent;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="background-color: #f5fbff;width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="background-color: #f5fbff;height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<table height="0px" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 0px solid #BBBBBB;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;font-size: 0px;line-height: 0px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<span>&#160;</span>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:13px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><table width="100%"><tr><td><![endif]-->
<h1 style="margin: 0px; color: #3aaee0; line-height: 140%; text-align: center; word-wrap: break-word; font-family: arial black,AvenirNext-Heavy,avant garde,arial; font-size: 28px; font-weight: 400;"><span><span><span><span>Uptime Monitor</span></span></span></span></h1>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<div style="font-size: 11px; font-weight: 700; color: #0068a5; line-height: 10%; text-align: center; word-wrap: break-word;">
<p style="line-height: 10%;">by BlueWave Labs</p>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:15px 10px 10px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><table width="100%"><tr><td><![endif]-->
<h1 style="margin: 0px; color: #e03e2d; line-height: 140%; text-align: center; word-wrap: break-word; font-family: book antiqua,palatino; font-size: 35px; font-weight: 400;"><strong>System Down Alert</strong></h1>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:0px 10px 10px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><table width="100%"><tr><td><![endif]-->
<h3 style="margin: 0px; color: #e03e2d; line-height: 140%; text-align: center; word-wrap: break-word; font-family: book antiqua,palatino; font-size: 18px; font-weight: 400;"><span><span><span><span>Your monitor service requires your attention</span></span></span></span></h3>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<div style="font-size: 14px; line-height: 140%; text-align: center; word-wrap: break-word;">
<p style="line-height: 140%;">
<li>${monitorName}</li>
<li>${timeStamp}</li>
</p>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #d5827c;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #d5827c;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="background-color: #f5fbff;width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="background-color: #f5fbff;height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<div style="font-size: 11px; font-weight: 700; color: #000000; line-height: 10%; text-align: center; word-wrap: break-word;">
<p style="line-height: 10%;">Login your account to see more details!</p>
</div>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><style>.v-button {background: transparent !important;}</style><![endif]-->
<div align="center">
<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://www.bluewavelabs.ca" style="height:40px; v-text-anchor:middle; width:98px;" arcsize="12.5%" strokecolor="#7e8c8d" strokeweight="3px" fillcolor="#ffffff"><w:anchorlock/><center style="color:#000000;"><![endif]-->
<a href="${process.env.LOGIN_PAGE_URL || loginUrl}" target="_blank" class="v-button" style="box-sizing: border-box;display: inline-block;text-decoration: none;-webkit-text-size-adjust: none;text-align: center;color: #000000; background-color: #ffffff; border-radius: 5px;-webkit-border-radius: 5px; -moz-border-radius: 5px; width:auto; max-width:100%; overflow-wrap: break-word; word-break: break-word; word-wrap:break-word; mso-border-alt: none;border-top-width: 3px; border-top-style: solid; border-top-color: #7e8c8d; border-left-width: 3px; border-left-style: solid; border-left-color: #7e8c8d; border-right-width: 3px; border-right-style: solid; border-right-color: #7e8c8d; border-bottom-width: 3px; border-bottom-style: solid; border-bottom-color: #7e8c8d;font-size: 14px;">
<span style="display:block;padding:10px 30px;line-height:120%;"><span style="font-size: 16px; line-height: 19.2px; font-family: Lato, sans-serif;">Login</span></span>
</a>
<!--[if mso]></center></v:roundrect><![endif]-->
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #ffffff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #ffffff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="background-color: #f5fbff;width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="background-color: #f5fbff;height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px 10px 15px;font-family:'Lato',sans-serif;" align="left">
<div style="font-size: 14px; color: #7e8c8d; line-height: 140%; text-align: center; word-wrap: break-word;">
<p style="font-size: 14px; line-height: 140%;">© 2024 BlueWave Labs. All Rights Reserved.</p>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<table height="0px" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 0px solid #BBBBBB;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;font-size: 0px;line-height: 0px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<span>&#160;</span>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if mso]></div><![endif]-->
<!--[if IE]></div><![endif]-->
</body>
</html>
`
}
module.exports = { downAlertTemplate }

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -0,0 +1,435 @@
/**
*
* @param {String} loginUrl - login Url shared with registered user.
* @returns {String} returns html content as String
*/
const registerTemplate = (loginUrl) => {
return `
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="x-apple-disable-message-reformatting">
<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]-->
<title></title>
<style type="text/css">
@media only screen and (min-width: 570px) {
.u-row {
width: 550px !important;
}
.u-row .u-col {
vertical-align: top;
}
.u-row .u-col-100 {
width: 550px !important;
}
}
@media (max-width: 570px) {
.u-row-container {
max-width: 100% !important;
padding-left: 0px !important;
padding-right: 0px !important;
}
.u-row .u-col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important;
}
.u-row {
width: 100% !important;
}
.u-col {
width: 100% !important;
}
.u-col > div {
margin: 0 auto;
}
}
body {
margin: 0;
padding: 0;
}
table,
tr,
td {
vertical-align: top;
border-collapse: collapse;
}
p {
margin: 0;
}
.ie-container table,
.mso-container table {
table-layout: fixed;
}
* {
line-height: inherit;
}
a[x-apple-data-detectors='true'] {
color: inherit !important;
text-decoration: none !important;
}
table, td { color: #000000; } #u_body a { color: #0000ee; text-decoration: underline; } @media (max-width: 480px) { #u_content_image_2 .v-src-width { width: auto !important; } #u_content_image_2 .v-src-max-width { max-width: 55% !important; } }
</style>
<!--[if !mso]><!--><link href="https://fonts.googleapis.com/css?family=Lato:400,700&display=swap" rel="stylesheet" type="text/css"><link href="https://fonts.googleapis.com/css?family=Lato:400,700&display=swap" rel="stylesheet" type="text/css"><!--<![endif]-->
</head>
<body class="clean-body u_body" style="margin: 0;padding: 0;-webkit-text-size-adjust: 100%;background-color: #e0e5eb;color: #000000">
<!--[if IE]><div class="ie-container"><![endif]-->
<!--[if mso]><div class="mso-container"><![endif]-->
<table id="u_body" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;min-width: 320px;Margin: 0 auto;background-color: #e0e5eb;width:100%" cellpadding="0" cellspacing="0">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color: #e0e5eb;"><![endif]-->
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: transparent;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<table height="0px" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 0px solid #BBBBBB;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;font-size: 0px;line-height: 0px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<span>&#160;</span>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:13px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><table width="100%"><tr><td><![endif]-->
<h1 style="margin: 0px; color: #3aaee0; line-height: 140%; text-align: center; word-wrap: break-word; font-family: arial black,AvenirNext-Heavy,avant garde,arial; font-size: 28px; font-weight: 400;"><span><span><span><span>Uptime Monitor</span></span></span></span></h1>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<div style="font-size: 11px; font-weight: 700; color: #0068a5; line-height: 10%; text-align: center; word-wrap: break-word;">
<p style="line-height: 10%;">by BlueWave Labs</p>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f5fbff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #f5fbff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:15px 10px 10px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><table width="100%"><tr><td><![endif]-->
<h1 style="margin: 0px; color: #000000; line-height: 140%; text-align: center; word-wrap: break-word; font-family: book antiqua,palatino; font-size: 35px; font-weight: 400;"><span><span><span>Registered!</span></span></span></h1>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:0px 10px 10px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><table width="100%"><tr><td><![endif]-->
<h3 style="margin: 0px; color: #000000; line-height: 140%; text-align: center; word-wrap: break-word; font-family: book antiqua,palatino; font-size: 18px; font-weight: 400;"><span>your account is ready for use</span></h3>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #d5827c;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #d5827c;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="background-color: #f5fbff;width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="background-color: #f5fbff;height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table id="u_content_image_2" style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding-right: 0px;padding-left: 0px;" align="center">
<img align="center" border="0" src="./Server/utils/emailTemplates/images/image1.png" alt="Image" title="Image" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: inline-block !important;border: none;height: auto;float: none;width: 39%;max-width: 206.7px;" width="206.7" class="v-src-width v-src-max-width"/>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<!--[if mso]><style>.v-button {background: transparent !important;}</style><![endif]-->
<div align="center">
<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://www.bluewavelabs.ca" style="height:40px; v-text-anchor:middle; width:98px;" arcsize="12.5%" strokecolor="#7e8c8d" strokeweight="3px" fillcolor="#ffffff"><w:anchorlock/><center style="color:#000000;"><![endif]-->
<a href="${process.env.LOGIN_PAGE_URL || loginUrl}" target="_blank" class="v-button" style="box-sizing: border-box;display: inline-block;text-decoration: none;-webkit-text-size-adjust: none;text-align: center;color: #000000; background-color: #ffffff; border-radius: 5px;-webkit-border-radius: 5px; -moz-border-radius: 5px; width:auto; max-width:100%; overflow-wrap: break-word; word-break: break-word; word-wrap:break-word; mso-border-alt: none;border-top-width: 3px; border-top-style: solid; border-top-color: #7e8c8d; border-left-width: 3px; border-left-style: solid; border-left-color: #7e8c8d; border-right-width: 3px; border-right-style: solid; border-right-color: #7e8c8d; border-bottom-width: 3px; border-bottom-style: solid; border-bottom-color: #7e8c8d;font-size: 14px;">
<span style="display:block;padding:10px 30px;line-height:120%;"><span style="font-size: 16px; line-height: 19.2px; font-family: Lato, sans-serif;">Login</span></span>
</a>
<!--[if mso]></center></v:roundrect><![endif]-->
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #ffffff;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: #ffffff;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="background-color: #f5fbff;width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="background-color: #f5fbff;height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px 10px 15px;font-family:'Lato',sans-serif;" align="left">
<div style="font-size: 14px; color: #7e8c8d; line-height: 140%; text-align: center; word-wrap: break-word;">
<p style="font-size: 14px; line-height: 140%;">© 2024 BlueWave Labs. All Rights Reserved.</p>
</div>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<div class="u-row-container" style="padding: 0px;background-color: transparent">
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 550px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: transparent;">
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:550px;"><tr style="background-color: transparent;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="550" style="width: 550px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
<div class="u-col u-col-100" style="max-width: 320px;min-width: 550px;display: table-cell;vertical-align: top;">
<div style="height: 100%;width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;"><!--<![endif]-->
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
<table height="0px" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 0px solid #BBBBBB;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;font-size: 0px;line-height: 0px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
<span>&#160;</span>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td><![endif]-->
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if mso]></div><![endif]-->
<!--[if IE]></div><![endif]-->
</body>
</html>
`
}
module.exports = {registerTemplate}

View File

@@ -1,43 +1,36 @@
const { MailerSend, EmailParams, Sender, Recipient } = require('mailersend')
const logger = require('../utils/logger')
const mailersend = new MailerSend({
apiKey: process.env.MAILERSEND_API_KEY,
})
const sgMail = require('@sendgrid/mail')
const logger = require('./logger')
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
const SERVICE_NAME = 'Email_Service';
/**
* @async
* @function
* @param {[string]} receivers - takes an array of strings
* @param {string} subject - takes a single string
* @param {string} contentHTML - takes a single string that contains HTML
* @returns {JSON}
* @example
* sendEmail(['veysel@bluewavelabs.ca','alex@bluewavelabs.ca','monzer@bluewavelabs.ca'],'Testing Email Servide','<h1>BlueWaveLabs</h1>')
* @param {[String]} receivers - takes an array of strings
* @param {String} subject - takes a single string
* @param {String} contentHTML - takes a single string that contains HTML
* @param {String} contentText - takes a string to be used if contentHTML is not compatible
* @example
* await sendEmail(['veysel@bluewavelabs.ca','alex@bluewavelabs.ca'],'Testing Email Service','<h1>BlueWaveLabs</h1>','Testing Email Service')
*/
// TODO: from email should be in .env file
const sendEmail = async (receivers,subject,contentHTML) => {
// Sender
const from = process.env.SYSTEM_EMAIL_ADDRESS;
const sender = new Sender(from);
// receivers
let recipients = []
receivers.map(email => recipients.push(new Recipient(email)));
// Set params
const emailParams = new EmailParams()
.setFrom(sender)
.setTo(recipients)
.setSubject(subject)
.setHtml(contentHTML);
const sendEmail = async (receivers,subject, contentHTML, contentText = null ) => {
const msg = {
to: receivers,
from: {
name: 'Uptime System',
email: process.env.SYSTEM_EMAIL_ADDRESS // must be verified email by sendgrid
},
subject: subject,
text: contentText || contentHTML,
html: contentHTML,
}
try {
const response = await mailersend.email.send(emailParams);
logger.info("Email sent to receivers!",{"service":"Email"})
return response;
await sgMail.send(msg);
logger.info(`Emails sent to receivers:${receivers} with the subject:${subject}`,{service:SERVICE_NAME})
} catch (error) {
logger.error(error.body,{"service":"email"})
console.log(error.body)
logger.error(`Sending Email action failed, ERROR:${error}`, { service: SERVICE_NAME });
}
}
module.exports = {sendEmail}