mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-14 21:59:42 -06:00
Merge branch 're-navbar-side-menu' of https://github.com/MuhammadKhalilzadeh/bluewave-uptime into re-navbar-side-menu
This commit is contained in:
537
README.md
537
README.md
@@ -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
5
Server/.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
node_modules
|
||||
.env
|
||||
*.log
|
||||
*.log
|
||||
*.sh
|
||||
docker/mongo/mongo_data/*
|
||||
docker/redis/redis_data/*
|
||||
@@ -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)
|
||||
|
||||
42
Server/controllers/queueController.js
Normal file
42
Server/controllers/queueController.js
Normal 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,
|
||||
};
|
||||
2
Server/docker/mongo/Dockerfile
Normal file
2
Server/docker/mongo/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
||||
FROM mongo
|
||||
EXPOSE 27017
|
||||
2
Server/docker/redis/Dockerfile
Normal file
2
Server/docker/redis/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
||||
FROM redis
|
||||
EXPOSE 6379
|
||||
157
Server/index.js
157
Server/index.js
@@ -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
899
Server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
13
Server/routes/queueRoute.js
Normal file
13
Server/routes/queueRoute.js
Normal 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
195
Server/service/jobQueue.js
Normal 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;
|
||||
469
Server/utils/emailTemplates/downAlertTemplate.js
Normal file
469
Server/utils/emailTemplates/downAlertTemplate.js
Normal 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> </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> </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 }
|
||||
BIN
Server/utils/emailTemplates/images/image1.png
Normal file
BIN
Server/utils/emailTemplates/images/image1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
435
Server/utils/emailTemplates/registerTemplate.js
Normal file
435
Server/utils/emailTemplates/registerTemplate.js
Normal 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> </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> </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}
|
||||
@@ -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}
|
||||
Reference in New Issue
Block a user