adding webpush'

This commit is contained in:
aeroreyna
2022-04-23 22:52:46 -07:00
parent 70fffbf55d
commit f7c016264b
5 changed files with 256 additions and 40 deletions

View File

@@ -229,6 +229,20 @@ userDB = (DB) => {
});
}
DB.setWebSubscription = (profileid, webSubscription)=>{
const _id = DB.ObjectID(profileid);
let update = {
$set:{
webSubscription
}
}
if (userProfileCache[profileid]) delete userProfileCache[profileid];
return DB.profileCols.updateOne({_id}, update).catch((err)=>{
console.log(err);
return false;
});
}
DB.addNotification = async (profileid, message, postid, commentIndx, actorid) => {
const _id = DB.ObjectID(profileid);
let update = {

View File

@@ -7,10 +7,17 @@ const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const Notifications = require("./notifications");
const webPush = require('web-push');
const publicVapidKey = process.env.PUBLIC_VAPID_KEY;
const privateVapidKey = process.env.PRIVATE_VAPID_KEY;
const webPushEmail = process.env.WEB_PUSH_EMAIL;
webPush.setVapidDetails('mailto:' + webPushEmail, publicVapidKey, privateVapidKey);
var corsOptions = {
origin: ['http://localhost:8080', "https://social.emmint.com"],
credentials: true };
credentials: true
};
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
@@ -21,15 +28,15 @@ const crypto = require('crypto');
const DB = require("./mongoDB.js");
// Utilities
const getSessionId = function(req){
const getSessionId = function (req) {
const session_id = req.cookies.session_id || req.query.session_id || req.body.session_id;
return session_id;
}
const getUserId = function(req){
const getUserId = function (req) {
const user_sid = req.cookies.user_sid || req.query.user_sid || req.body.user_sid;
return user_sid;
}
const getProfileId = function(req){
const getProfileId = function (req) {
const profile_id = req.cookies.profile_id || req.query.profile_id || req.body.profile_id;
return profile_id;
}
@@ -42,7 +49,7 @@ const postRoute = require('./routes/post.js');
const paymentsRoute = require('./routes/payments.js');
DB.getDB.then((DB)=>{
DB.getDB.then((DB) => {
// middleware function to check for logged-in users
const sessionChecker = async (req, res, next) => {
@@ -52,13 +59,13 @@ DB.getDB.then((DB)=>{
if (session_id && user_sid) {
const userInfo = await DB.checkSessionOnDB(session_id, user_sid);
req.userInfo = userInfo;
if(!await DB.getProfileCache(profile_id)){
if (!await DB.getProfileCache(profile_id)) {
const latestProfile = await DB.latestProfile(user_sid);
res.cookie('profile_id', latestProfile._id, cookiesOptions);
profile_id = latestProfile._id;
}
req.profileInfo = {_id: profile_id}
if(!userInfo) return res.redirect('/login');
req.profileInfo = { _id: profile_id }
if (!userInfo) return res.redirect('/login');
next();
} else {
return res.redirect('/login');
@@ -67,23 +74,30 @@ DB.getDB.then((DB)=>{
// route for Home-Page
app.get('/', sessionChecker, async (req, res) => {
if(req.userInfo) return res.json({
if (req.userInfo) return res.json({
status: "ok",
userInfo: req.userInfo,
profileInfo:req.profileInfo
profileInfo: req.profileInfo
});
res.json({status: "ok"});
res.json({ status: "ok" });
});
app.post('/subscribe', (req, res) => {
const subscription = req.body;
res.status(201).json({});
const profileid = getProfileId(req);
DB.setWebSubscription(profileid, subscription);
});
// route for user signup
const signup = async function(req, res){
const signup = async function (req, res) {
const username = req.query.username || req.body.username;
const password = req.query.password || req.body.password;
const email = req.query.email || req.body.email;
const profile = req.query.profile || req.body.profile;
if(!username || !password || !email) return res.json({status: "fail"});
if (!username || !password || !email) return res.json({ status: "fail" });
//Check if the new user has an invitation
if(!await DB.getInvitation(email)) return res.json({status: "Not invitation found!"});
if (!await DB.getInvitation(email)) return res.json({ status: "Not invitation found!" });
//const hashedPassword = await bcrypt.hash(password, 10);
const hashedPassword = await bcrypt.hash(password, 10);
const response = await DB.newUser({
@@ -91,7 +105,7 @@ DB.getDB.then((DB)=>{
email: email.toLowerCase(),
password: hashedPassword
});
if(!response.toLowerCase){
if (!response.toLowerCase) {
let user = {
userid: response.insertedId,
profile: profile,
@@ -100,7 +114,7 @@ DB.getDB.then((DB)=>{
await DB.newProfile(userObj);
return await login(req, res);
}
return res.json({status: response})
return res.json({ status: response })
}
app.route('/signup').get(async (req, res) => {
return await signup(req, res);
@@ -117,19 +131,19 @@ DB.getDB.then((DB)=>{
};
// route for user Login
const login = async function(req, res){
const login = async function (req, res) {
const session_id = getSessionId(req);
const user_sid = getUserId(req);
if (session_id && user_sid) {
const userInfo = await DB.checkSessionOnDB(session_id, user_sid);
if(userInfo) return res.redirect('/');
if (userInfo) return res.redirect('/');
}
const username = req.body.username || req.query.username;
const password = req.body.password || req.query.password || "";
const user = await DB.getUser(username);
if (!user) return res.json({status: "user not founded"});
if (!user) return res.json({ status: "user not founded" });
const samePass = await bcrypt.compare(password, user.password);
if(!samePass) return res.json({status: "incorrect password"});
if (!samePass) return res.json({ status: "incorrect password" });
const doc = await DB.newSession(user._id);
res.cookie('user_sid', user._id, cookiesOptions);
res.cookie('session_id', doc.insertedId, cookiesOptions);
@@ -164,17 +178,17 @@ DB.getDB.then((DB)=>{
const user_sid = getUserId(req);
if (session_id && user_sid) {
const userInfo = await DB.checkSessionOnDB(session_id, user_sid);
if(userInfo) return res.redirect('/');
if (userInfo) return res.redirect('/');
}
const username = req.body.username;
const user = await DB.getUser(username);
if (!user) return res.json({status: "user not founded"});
if (!user) return res.json({ status: "user not founded" });
const password = generatePassword();
const hashedPassword = await bcrypt.hash(password, 10);
DB.resetUserPassword(username, hashedPassword);
//We need to limit this to every 2 hours or something like this.
Notifications.sendEmail(username, "Your new credentials",
`
`
<p> Hello,</p>
<p> This is your new password: ${password}</p>
<p><a href="https://social.emmint.com/">Log in</a></p>
@@ -190,7 +204,7 @@ DB.getDB.then((DB)=>{
app.post('/changeProfile', sessionChecker, async (req, res) => {
const user_sid = getUserId(req);
let profile = await DB.getProfile(req.body.profileid);
if(!profile || profile.userid != user_sid) return res.json({
if (!profile || profile.userid != user_sid) return res.json({
status: "profile does not belong to the logged user",
});
res.cookie('profile_id', profile._id, cookiesOptions);
@@ -210,7 +224,7 @@ DB.getDB.then((DB)=>{
});
// route for user logout
const logout = function(req, res){
const logout = function (req, res) {
const session_id = getSessionId(req);
const user_sid = getUserId(req);
if (session_id && user_sid) {
@@ -239,7 +253,7 @@ DB.getDB.then((DB)=>{
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
}).catch((err)=>{
}).catch((err) => {
throw err;
});

View File

@@ -1,10 +1,18 @@
const nodemailer = require("nodemailer");
const DBGetter = require("./mongoDB.js");
const { Expo } = require('expo-server-sdk');
const webPush = require('web-push');
const sendWebNotification = async (subscription, title, body) => {
const payload = JSON.stringify({
title,
body
});
webPush.sendNotification(subscription, payload);
}
const sendPushNotification = async (profileToken, body, data) => {
if(!profileToken) return 0;
if (!profileToken) return 0;
let expo = new Expo();
// Create the messages that you want to send to clients
@@ -376,6 +384,7 @@ const Notifications = {
}
const notifBody = `${senderProfile.profile.firstName} post in your profile`;
sendPushNotification(profile.token, notifBody, {});
sendWebNotification(profile.webSubscription, notifBody, message)
DB.addNotification(toProfileId, notifBody, post._id, null, senderProfile._id);
return youGotANewPostTemplate(profile, user.username, senderProfile, message);
},

180
package-lock.json generated
View File

@@ -19,7 +19,8 @@
"mongodb": "^3.6.3",
"nodemailer": "^6.6.3",
"stripe": "^8.178.0",
"vimeo": "^2.1.1"
"vimeo": "^2.1.1",
"web-push": "^3.4.5"
}
},
"node_modules/@mapbox/node-pre-gyp": {
@@ -122,6 +123,17 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"node_modules/asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"dependencies": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -149,6 +161,11 @@
"safe-buffer": "^5.1.1"
}
},
"node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"node_modules/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
@@ -186,6 +203,11 @@
"node": ">=0.6.19"
}
},
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"node_modules/buffer-from": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
@@ -353,6 +375,14 @@
"node": ">=10"
}
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -526,6 +556,17 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"node_modules/http_ece": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz",
"integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==",
"dependencies": {
"urlsafe-base64": "~1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@@ -628,6 +669,25 @@
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
"integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
},
"node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/lodash._baseiteratee": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz",
@@ -772,6 +832,11 @@
"node": ">= 0.6"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -783,6 +848,11 @@
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"node_modules/minipass": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
@@ -1309,6 +1379,11 @@
"requires-port": "^1.0.0"
}
},
"node_modules/urlsafe-base64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
"integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1338,6 +1413,25 @@
"tus-js-client": "^1.5.1"
}
},
"node_modules/web-push": {
"version": "3.4.5",
"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.4.5.tgz",
"integrity": "sha512-2njbTqZ6Q7ZqqK14YpK1GGmaZs3NmuGYF5b7abCXulUIWFSlSYcZ3NBJQRFcMiQDceD7vQknb8FUuvI1F7Qe/g==",
"dependencies": {
"asn1.js": "^5.3.0",
"http_ece": "1.1.0",
"https-proxy-agent": "^5.0.0",
"jws": "^4.0.0",
"minimist": "^1.2.5",
"urlsafe-base64": "^1.0.0"
},
"bin": {
"web-push": "src/cli.js"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
@@ -1440,6 +1534,17 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1463,6 +1568,11 @@
"safe-buffer": "^5.1.1"
}
},
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
@@ -1494,6 +1604,11 @@
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
},
"buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"buffer-from": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
@@ -1622,6 +1737,14 @@
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g=="
},
"ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -1768,6 +1891,14 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"http_ece": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz",
"integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==",
"requires": {
"urlsafe-base64": "~1.0.0"
}
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@@ -1849,6 +1980,25 @@
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
"integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
},
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"requires": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"lodash._baseiteratee": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz",
@@ -1965,6 +2115,11 @@
"mime-db": "1.48.0"
}
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1973,6 +2128,11 @@
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"minipass": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
@@ -2368,6 +2528,11 @@
"requires-port": "^1.0.0"
}
},
"urlsafe-base64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
"integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -2391,6 +2556,19 @@
"tus-js-client": "^1.5.1"
}
},
"web-push": {
"version": "3.4.5",
"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.4.5.tgz",
"integrity": "sha512-2njbTqZ6Q7ZqqK14YpK1GGmaZs3NmuGYF5b7abCXulUIWFSlSYcZ3NBJQRFcMiQDceD7vQknb8FUuvI1F7Qe/g==",
"requires": {
"asn1.js": "^5.3.0",
"http_ece": "1.1.0",
"https-proxy-agent": "^5.0.0",
"jws": "^4.0.0",
"minimist": "^1.2.5",
"urlsafe-base64": "^1.0.0"
}
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",

View File

@@ -20,6 +20,7 @@
"mongodb": "^3.6.3",
"nodemailer": "^6.6.3",
"stripe": "^8.178.0",
"vimeo": "^2.1.1"
"vimeo": "^2.1.1",
"web-push": "^3.4.5"
}
}