Merge pull request #72 from cowingtonpost1/main

Add SMTP and discord webhooks for verification codes
This commit is contained in:
Green! 2023-02-04 21:21:11 -05:00 committed by GitHub
commit 24772eb6c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 311 additions and 108 deletions

View file

@ -55,11 +55,27 @@ Table of contents
(Example configuration with none-json notes) (Example configuration with none-json notes)
```json ```json
{ {
"verification": false, // disabled by default "sendgrid_verification": false,
"api_key":" Your sendgrid API key used to access your account from the API to send emails", "sendgrid_options": {
"sendFromEmail":"The email that will send the one time password (MUST BE VERIFIED IN SENDGRID)", "api_key": "YOUR_SENDGRID_API_KEY",
"type": "code", // DO NOT TOUCH "sendFromEmail": "THE EMAIL THE CODES WILL BE SENT FROM (MUST BE VERIFIED IN SENDGRID)",
"email": " The email you want to use for recieving OTP " "to_email": "THE EMAIL YOU WANT THE CODES SENT TO"
},
"discord_verification": false,
"webhook_url": "YOUR DISCORD WEBHOOK URL",
"smtp_verification": false,
"smtp_options": {
"to_email": "THE EMAIL YOU WANT THE CODES SENT TO",
"sendFromEmail": "THE EMAIL THE CODES ARE SENT FROM",
"host": "YOUR SMTP HOST",
"port": 465,
"auth": {
"user": "SMTP USER",
"pass": "YOUR PASSWORD"
}
}
} }
``` ```
@ -69,8 +85,10 @@ Email verification is a new and unique feature that we've implemented in the eve
### What does it do ### What does it do
When a user tries to access the website, before allowed access they will be asked for a One time password sent to an email set in the deployment configuration. Once verified, they will have 15 day access to the site. When a user tries to access the website, before allowed access they will be asked for a One time password sent to an email set in the deployment configuration. Once verified, they will have 15 day access to the site.
#### SendGrid Setup Instructions
* Firstly, We need to enable verification within the deployment configuration * Firstly, We need to enable verification within the deployment configuration
* change `"verification":false,` to `"verification":true,` * change `"sendgrid_verification":false,` to `"sendgrid_verification":true,` above the SendGrid Section
* _Note: You have to reboot the node app for any changes to take place._ * _Note: You have to reboot the node app for any changes to take place._
* Now, we need to use an api to send a message * Now, we need to use an api to send a message
* Make an account at Sendgrid (https://app.sendgrid.com/) * Make an account at Sendgrid (https://app.sendgrid.com/)
@ -79,7 +97,22 @@ When a user tries to access the website, before allowed access they will be aske
* Go to settings -> Sender authentication and click Verify a Single Sender * Go to settings -> Sender authentication and click Verify a Single Sender
* Now, We need to get the API key to connect to the API * Now, We need to get the API key to connect to the API
* Go to settings -> API Keys -> and make an API key. * Go to settings -> API Keys -> and make an API key.
* Complete the information in the deployment config `deployment.config.json` such as: * Complete the information in the deployment config `deployment.config.json` under the `sendgrid_options` section such as: sendFromEmail, to_email and api_key
#### Discord Webhook Setup Instructions
* Set discord_verification to true in the deployment configuration.
* Create a channel in a discord server you have admin in.
* Click the Edit Channel button.
* Click Integrations
* Click create web hook and copy the URL.
* Paste it under the `webhook_url` section in the deployment configuration.
#### SMTP Setup Instructions
* Set `smtp_verification` to true.
* Change `to_email` to the email address you want the codes to be sent to.
* Change `sendFromEmail to the email address that is going to send the codes.
* Get the host and port from your email provider's documentation.
* Fill in your username and password under the `user` and `pass` section under auth.
## Advanced Deployment ## Advanced Deployment

212
app.js
View file

@ -1,149 +1,175 @@
import createBareServer from "@tomphttp/bare-server-node" import createBareServer from "@tomphttp/bare-server-node";
import http from "http" import http from "http";
import { fileURLToPath } from "url" import {createRequire} from "module";
import { dirname, join } from "path" import {dirname, join} from "path";
import serveStatic from "serve-static" import serveStatic from "serve-static";
import { createRequire } from "module" import {fileURLToPath} from "url";
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url);
const config = require("./deployment.config.json") const config = require("./deployment.config.json");
import fs from "fs" import fs from "fs";
var base64data var base64data;
import sgTransport from "nodemailer-sendgrid-transport" import sgTransport from "nodemailer-sendgrid-transport";
import nodemailer from "nodemailer" import nodemailer from "nodemailer";
import fetch from "node-fetch";
const options = { const options = {
auth: { auth : {
api_key: config.api_key, api_key : config.sendgrid_options.api_key,
}, },
} };
const mailerAgent = nodemailer.createTransport(sgTransport(options)) const sendgridMailerAgent = nodemailer.createTransport(sgTransport(options));
function sendVerificationEmail(UUID, mailTo, OTP) { const smtpMailerAgent = nodemailer.createTransport(config.smtp_options);
const email = { function sendVerificationEmail(UUID, OTP) {
to: mailTo, let email = {
from: `${config.sendFromEmail}`, to : "",
subject: `NebulaWEB personal access code ${OTP}`, from : "",
text: ` subject : `NebulaWEB personal access code ${OTP}`,
text : `
####### ACCESS CODE (OTP) ${OTP} ####### ####### ACCESS CODE (OTP) ${OTP} #######
####### DO NOT SHARE THIS CODE! ####### ####### DO NOT SHARE THIS CODE! #######
(this message is automated)`, (this message is automated)`,
html: ` html : `
####### ACCESS CODE (OTP) ${OTP} ####### ####### ACCESS CODE (OTP) ${OTP} #######
####### DO NOT SHARE THIS CODE! ####### ####### DO NOT SHARE THIS CODE! #######
(this message is automated) (this message is automated)
`, `,
} };
if (config.verification == true) { if (config.sendgrid_verification == true) {
mailerAgent.sendMail(email, (err, res) => { email.to = config.sendgrid_options.to_email
email.from = config.sendgrid_options.sendFromEmail
sendgridMailerAgent.sendMail(email, (err, res) => {
if (err) { if (err) {
console.log(err) console.log(err);
} }
console.log(res) console.log(res);
}) });
}
if (config.smtp_verification == true) {
email.to = config.smtp_options.to_email
email.from = config.smtp_options.sendFromEmail
smtpMailerAgent.sendMail(email, (err, res) => {
if (err) {
console.log(err);
}
console.log(res);
});
}
if (config.discord_verification == true) {
fetch(config.webhook_url, {
method : "POST",
headers : {
"Content-Type" : "application/json",
},
body : JSON.stringify({
content : `Your NebulaWEB access code is ${OTP}`,
}),
});
} }
} }
function getNewCode() { function getNewCode() {
var seq = (Math.floor(Math.random() * 10000) + 10000).toString().substring(1) var seq = (Math.floor(Math.random() * 10000) + 10000).toString().substring(1);
if (seq == "0") { if (seq == "0") {
getNewCode() getNewCode();
} }
return seq return seq;
} }
const PORT = process.env.PORT || 3000 const PORT = process.env.PORT || 3000;
const bareServer = createBareServer("/bare/", { const bareServer = createBareServer("/bare/", {
logErrors: false, logErrors : false,
localAddress: undefined, localAddress : undefined,
}) });
const serve = serveStatic( const serve =
join(dirname(fileURLToPath(import.meta.url)), "static/"), serveStatic(join(dirname(fileURLToPath(import.meta.url)), "static/"), {
{ fallthrough : false,
fallthrough: false, maxAge : 5 * 60 * 1000,
maxAge: 5 * 60 * 1000, });
}
)
const server = http.createServer() const server = http.createServer();
server.on("request", (request, response) => { server.on("request", (request, response) => {
try { try {
if (bareServer.shouldRoute(request)) { if (bareServer.shouldRoute(request)) {
bareServer.routeRequest(request, response) bareServer.routeRequest(request, response);
} else { } else {
let base64data let base64data;
const url = request.url const url = request.url;
if (url.startsWith("/sendNewCode")) { if (url.startsWith("/sendNewCode")) {
const OTP = getNewCode() const OTP = getNewCode();
fs.writeFile("./memory.txt", OTP, function (err) { fs.writeFile("./memory.txt", OTP, function(err) {
if (err) return console.log(err) if (err)
console.log(`Wrote OTP code to temp file`) return console.log(err);
}) console.log(`Wrote OTP code to temp file`);
});
fs.readFile("./memory.txt", "utf8", (err, data) => { fs.readFile("./memory.txt", "utf8", (err, data) => {
if (err) { if (err) {
console.error(err) console.error(err);
return return;
} }
console.log(data) console.log(data);
sendVerificationEmail("10", config.email, data) sendVerificationEmail("10", data);
let buff = new Buffer(data) let buff = new Buffer(data);
base64data = buff.toString("base64") base64data = buff.toString("base64");
console.log("302") console.log("302");
response.writeHead(302, { response.writeHead(302, {
location: "/unv.html?c=" + base64data, location : "/unv.html?c=" + base64data,
}) });
response.end() response.end();
}) });
} else if (url.startsWith("/verification")) { } else if (url.startsWith("/verification")) {
var body var body;
if (config.verification == true) { if (config.sendgrid_verification == true ||
const body = "true" config.discord_verification == true ||
config.smtp_verificaton == true) {
const body = "true";
response.writeHead(200, { response.writeHead(200, {
"Content-Length": Buffer.byteLength(body), "Content-Length" : Buffer.byteLength(body),
"Content-Type": "text/plain", "Content-Type" : "text/plain",
}) });
response.end(body) response.end(body);
} else { } else {
const body = "false" const body = "false";
response.writeHead(200, { response.writeHead(200, {
"Content-Length": Buffer.byteLength(body), "Content-Length" : Buffer.byteLength(body),
"Content-Type": "text/plain", "Content-Type" : "text/plain",
}) });
response.end(body) response.end(body);
} }
} else { } else {
serve(request, response, (err) => { serve(request, response, (err) => {
response.writeHead(err?.statusCode || 500, null, { response.writeHead(err?.statusCode || 500, null, {
"Content-Type": "text/plain", "Content-Type" : "text/plain",
}) });
response.end(err?.stack) response.end(err?.stack);
}) });
} }
} }
} catch (e) { } catch (e) {
response.writeHead(500, "Internal Server Error", { response.writeHead(500, "Internal Server Error", {
"Content-Type": "text/plain", "Content-Type" : "text/plain",
}) });
response.end(e.stack) response.end(e.stack);
} }
}) });
server.on("upgrade", (req, socket, head) => { server.on("upgrade", (req, socket, head) => {
if (bareServer.shouldRoute(req)) { if (bareServer.shouldRoute(req)) {
bareServer.routeUpgrade(req, socket, head) bareServer.routeUpgrade(req, socket, head);
} else { } else {
socket.end() socket.end();
} }
}) });
server.listen(PORT) server.listen(PORT);
if (process.env.UNSAFE_CONTINUE) if (process.env.UNSAFE_CONTINUE)
process.on("uncaughtException", (err, origin) => { process.on("uncaughtException", (err, origin) => {
console.error(`Critical error (${origin}):`) console.error(`Critical error (${origin}):`);
console.error(err) console.error(err);
console.error("UNSAFELY CONTINUING EXECUTION") console.error("UNSAFELY CONTINUING EXECUTION");
console.error() console.error();
}) });
console.log(`Server running at http://localhost:${PORT}/.`) console.log(`Server running at http://localhost:${PORT}/.`);

View file

@ -1,7 +1,23 @@
{ {
"verification": false, "sendgrid_verification": false,
"api_key":"YOUR_SENDGRID_API_KEY", "sendgrid_options": {
"sendFromEmail":"THE EMAIL THE CODES WILL BE SENT FROM (MUST BE VERIFIED IN SENDGRID)", "api_key": "YOUR_SENDGRID_API_KEY",
"type": "code", "sendFromEmail": "THE EMAIL THE CODES WILL BE SENT FROM (MUST BE VERIFIED IN SENDGRID)",
"email": "YOUR_EMAIL_HERE" "to_email": "THE EMAIL YOU WANT THE CODES SENT TO"
},
"discord_verification": false,
"webhook_url": "YOUR DISCORD WEBHOOK URL",
"smtp_verification": false,
"smtp_options": {
"to_email": "THE EMAIL YOU WANT THE CODES SENT TO",
"sendFromEmail": "THE EMAIL THE CODES ARE SENT FROM",
"host": "YOUR SMTP HOST",
"port": 465,
"auth": {
"user": "SMTP USER",
"pass": "YOUR PASSWORD"
}
}
} }

127
package-lock.json generated
View file

@ -10,6 +10,7 @@
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@tomphttp/bare-server-node": "^1.2.2", "@tomphttp/bare-server-node": "^1.2.2",
"node-fetch": "^3.3.0",
"nodemailer": "^6.8.0", "nodemailer": "^6.8.0",
"nodemailer-sendgrid-transport": "^0.2.0", "nodemailer-sendgrid-transport": "^0.2.0",
"serve-static": "^1.15.0" "serve-static": "^1.15.0"
@ -385,6 +386,14 @@
"node": ">=0.10" "node": ">=0.10"
} }
}, },
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -668,6 +677,28 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"optional": true "optional": true
}, },
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/forever-agent": { "node_modules/forever-agent": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -689,6 +720,17 @@
"node": ">= 0.12" "node": ">= 0.12"
} }
}, },
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/fresh": { "node_modules/fresh": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@ -1088,6 +1130,41 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "6.8.0", "version": "6.8.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
@ -1587,6 +1664,14 @@
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
} }
}, },
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/webidl-conversions": { "node_modules/webidl-conversions": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz",
@ -1937,6 +2022,11 @@
"assert-plus": "^1.0.0" "assert-plus": "^1.0.0"
} }
}, },
"data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
},
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -2156,6 +2246,15 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"optional": true "optional": true
}, },
"fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
}
},
"forever-agent": { "forever-agent": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -2171,6 +2270,14 @@
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"requires": {
"fetch-blob": "^3.1.2"
}
},
"fresh": { "fresh": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@ -2512,6 +2619,21 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"requires": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
}
},
"nodemailer": { "nodemailer": {
"version": "6.8.0", "version": "6.8.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
@ -2898,6 +3020,11 @@
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
} }
}, },
"web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
},
"webidl-conversions": { "webidl-conversions": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz",

View file

@ -14,6 +14,7 @@
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@tomphttp/bare-server-node": "^1.2.2", "@tomphttp/bare-server-node": "^1.2.2",
"node-fetch": "^3.3.0",
"nodemailer": "^6.8.0", "nodemailer": "^6.8.0",
"nodemailer-sendgrid-transport": "^0.2.0", "nodemailer-sendgrid-transport": "^0.2.0",
"serve-static": "^1.15.0" "serve-static": "^1.15.0"