diff --git a/dbTools/profile.js b/dbTools/profile.js index 2992604..7d8731d 100644 --- a/dbTools/profile.js +++ b/dbTools/profile.js @@ -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 = { diff --git a/index.js b/index.js index 79f84b7..f8a33d2 100644 --- a/index.js +++ b/index.js @@ -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,14 +59,14 @@ 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'); - next(); + 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({ - status: "ok", - userInfo: req.userInfo, - profileInfo:req.profileInfo + if (req.userInfo) return res.json({ + status: "ok", + userInfo: req.userInfo, + 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); @@ -146,7 +160,7 @@ DB.getDB.then((DB)=>{ app.route('/login').get(async (req, res) => { return await login(req, res); }).post(async (req, res) => { - return await login(req, res); + return await login(req, res); }); function generatePassword() { @@ -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", -` + Notifications.sendEmail(username, "Your new credentials", + `
Hello,
This is your new password: ${password}
@@ -184,20 +198,20 @@ DB.getDB.then((DB)=>{ return res.json({ status: "ok", details: 'Check your email for new password' - }); + }); }); 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({ - status: "profile does not belong to the logged user", - }); + 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); return res.json({ status: "ok", ...profile - }); + }); }); app.post('/token/', sessionChecker, async (req, res) => { @@ -206,11 +220,11 @@ DB.getDB.then((DB)=>{ DB.setProfileToken(profileid, token); return res.json({ status: "ok" - }); + }); }); // 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) { @@ -226,7 +240,7 @@ DB.getDB.then((DB)=>{ app.get('/logout', (req, res) => { return logout(req, res); }); - + app.use('/user', sessionChecker, profileRoute); app.use('/post', sessionChecker, postRoute); app.use('/payments', sessionChecker, paymentsRoute); @@ -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; }); diff --git a/notifications.js b/notifications.js index bde45e1..2ddcc3b 100644 --- a/notifications.js +++ b/notifications.js @@ -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); }, diff --git a/package-lock.json b/package-lock.json index 1913281..03ad867 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index fbae892..b1d288f 100644 --- a/package.json +++ b/package.json @@ -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" } }