Pushnotifications now

This commit is contained in:
Kasper Rynning-Tønnesen
2020-01-31 16:13:14 +01:00
parent 2e17b4cca3
commit 9350c7647e
11 changed files with 309 additions and 13 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
public/index.html
public/sw/
config/env/lottery.config.js
config/env/push.config.js
# Logs
logs

64
api/subscriptions.js Normal file
View File

@@ -0,0 +1,64 @@
const express = require("express");
const path = require("path");
const router = express.Router();
const webpush = require("web-push"); //requiring the web-push module
const mongoose = require("mongoose");
const schedule = require("node-schedule");
mongoose.connect("mongodb://localhost:27017/vinlottis", {
useNewUrlParser: true
});
const config = require(path.join(__dirname + "/../config/env/push.config"));
const Subscription = require(path.join(__dirname + "/../schemas/Subscription"));
const vapidKeys = {
publicKey: config.publicKey,
privateKey: config.privateKey
};
//setting our previously generated VAPID keys
webpush.setVapidDetails(
config.mailto,
vapidKeys.publicKey,
vapidKeys.privateKey
);
const sendNotification = (subscription, dataToSend = "") => {
webpush.sendNotification(subscription, dataToSend);
};
router.use((req, res, next) => {
next();
});
router.route("/save-subscription").post(async (req, res) => {
const subscription = req.body;
await saveToDatabase(subscription); //Method to save the subscription to Database
res.json({ message: "success" });
});
const saveToDatabase = async subscription => {
let found = await Subscription.find({
endpoint: subscription.endpoint,
"keys.p256dh": subscription.keys.p256dh,
"keys.auth": subscription.keys.auth
});
if (found.length > 0) {
return;
} else {
let newSubscription = new Subscription(subscription);
await newSubscription.save();
}
};
schedule.scheduleJob("0 50 14 * * 5", async () => {
let subs = await Subscription.find();
for (let i = 0; i < subs.length; i++) {
let subscription = subs[i]; //get subscription from your databse here.
const message = "Husk vinlotteriet, det begynner om 10 minutter!";
sendNotification(subscription, message);
}
});
module.exports = router;

5
config/env/push.config.example.js vendored Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
publicKey: "",
privateKey: "",
mailto: ""
};

View File

@@ -41,7 +41,8 @@ const ServiceWorkerConfig = {
},
plugins: [
new webpack.DefinePlugin({
__DATE__: new Date().getTime()
__DATE__: new Date().getTime(),
__PUBLICKEY__: JSON.stringify(require("./env/push.config").publicKey)
})
]
};

164
package-lock.json generated
View File

@@ -1177,6 +1177,14 @@
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
"dev": true
},
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"requires": {
"es6-promisify": "^5.0.0"
}
},
"aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
@@ -1607,8 +1615,7 @@
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
},
"body-parser": {
"version": "1.19.0",
@@ -1816,6 +1823,11 @@
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
},
"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-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
@@ -2644,6 +2656,15 @@
"sha.js": "^2.4.8"
}
},
"cron-parser": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.13.0.tgz",
"integrity": "sha512-UWeIpnRb0eyoWPVk+pD3TDpNx3KCFQeezO224oJIkktBrcW6RoAPOx5zIKprZGfk6vcYSmA8yQXItejSaDBhbQ==",
"requires": {
"is-nan": "^1.2.1",
"moment-timezone": "^0.5.25"
}
},
"cross-env": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
@@ -3024,7 +3045,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
@@ -3284,6 +3304,14 @@
"safer-buffer": "^2.1.0"
}
},
"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",
@@ -3428,8 +3456,15 @@
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
"dev": true
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"requires": {
"es6-promise": "^4.0.3"
}
},
"escape-html": {
"version": "1.0.3",
@@ -5379,12 +5414,39 @@
"sshpk": "^1.7.0"
}
},
"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"
}
},
"https-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"dev": true
},
"https-proxy-agent": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz",
"integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
}
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -5694,6 +5756,14 @@
"is-extglob": "^2.1.1"
}
},
"is-nan": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz",
"integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==",
"requires": {
"define-properties": "^1.1.3"
}
},
"is-number": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
@@ -5919,6 +5989,25 @@
"verror": "1.10.0"
}
},
"jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"requires": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"kareem": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
@@ -6036,6 +6125,11 @@
"integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==",
"dev": true
},
"long-timeout": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
"integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -6276,8 +6370,7 @@
"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==",
"dev": true
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"minimalistic-crypto-utils": {
"version": "1.0.1",
@@ -6419,6 +6512,14 @@
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"moment-timezone": {
"version": "0.5.27",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz",
"integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==",
"requires": {
"moment": ">= 2.9.0"
}
},
"mongodb": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.2.tgz",
@@ -6747,6 +6848,16 @@
}
}
},
"node-schedule": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-1.3.2.tgz",
"integrity": "sha512-GIND2pHMHiReSZSvS6dpZcDH7pGPGFfWBIEud6S00Q8zEIzAs9ommdyRK1ZbQt8y1LyZsJYZgPnyi7gpU2lcdw==",
"requires": {
"cron-parser": "^2.7.3",
"long-timeout": "0.1.1",
"sorted-array-functions": "^1.0.0"
}
},
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -6876,8 +6987,7 @@
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
},
"object-visit": {
"version": "1.0.1",
@@ -9162,6 +9272,11 @@
}
}
},
"sorted-array-functions": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz",
"integrity": "sha512-sWpjPhIZJtqO77GN+LD8dDsDKcWZ9GCOJNqKzi1tvtjGIzwfoyuRH8S0psunmc6Z5P+qfDqztSbwYR5X/e1UTg=="
},
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -10257,6 +10372,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="
},
"use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -10438,6 +10558,32 @@
"minimalistic-assert": "^1.0.0"
}
},
"web-push": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.4.3.tgz",
"integrity": "sha512-nt/hRSlfRDTwvem//7jle1+cy62lBoxFshad8ai2Q4SlHZS00oHnrw5Dul3jSWXR+bOcnZkwnRs3tW+daNTuyA==",
"requires": {
"asn1.js": "^5.0.0",
"http_ece": "1.1.0",
"https-proxy-agent": "^3.0.0",
"jws": "^3.1.3",
"minimist": "^1.2.0",
"urlsafe-base64": "^1.0.0"
},
"dependencies": {
"asn1.js": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.3.0.tgz",
"integrity": "sha512-WHnQJFcOrIWT1RLOkFFBQkFVvyt9BPOOrH+Dp152Zk4R993rSzXUGPmkybIcUFhHE2d/iHH+nCaOWVCDbO8fgA==",
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
}
}
},
"webpack": {
"version": "4.41.5",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz",

View File

@@ -28,6 +28,7 @@
"mongoose": "^5.8.7",
"node-fetch": "^2.6.0",
"node-sass": "^4.13.0",
"node-schedule": "^1.3.2",
"passport": "^0.4.1",
"passport-local": "^1.0.0",
"passport-local-mongoose": "^6.0.1",
@@ -36,7 +37,8 @@
"vue": "~2.6",
"vue-analytics": "^5.22.1",
"vue-router": "~3.0",
"vuex": "^3.1.1"
"vuex": "^3.1.1",
"web-push": "^3.4.3"
},
"devDependencies": {
"@babel/core": "~7.2",

View File

@@ -6,11 +6,29 @@ var STATIC_CACHE_URLS = ["/"];
console.log("Nåværende versjon:", version);
self.addEventListener("activate", event => {
event.waitUntil(
new Promise(resolve => {
const applicationServerKey = urlB64ToUint8Array(__PUBLICKEY__);
const options = { applicationServerKey, userVisibleOnly: true };
self.registration.pushManager.subscribe(options).then(subscription =>
saveSubscription(subscription).then(() => {
resolve();
})
);
})
);
event.waitUntil(removeCache(CACHE_NAME));
event.waitUntil(removeCache(CACHE_NAME_API));
event.waitUntil(addCache(CACHE_NAME, STATIC_CACHE_URLS));
});
self.addEventListener("push", function(event) {
if (event.data) {
showLocalNotification("Vinlotteri!", event.data.text(), self.registration);
} else {
}
});
self.addEventListener("install", event => {
console.log("Arbeids arbeideren installerer seg.");
self.skipWaiting();
@@ -49,6 +67,41 @@ self.addEventListener("fetch", event => {
}
});
function showLocalNotification(title, body, swRegistration) {
const options = {
body,
icon: "https://lottis.vin/public/assets/images/favicon.png",
image: "https://lottis.vin/public/assets/images/favicon.png",
vibrate: [300]
};
swRegistration.showNotification(title, options);
}
async function saveSubscription(subscription) {
const SERVER_URL = "https://lottis.vin/subscription/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
}
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
function addCache(cacheKey, cacheUrls) {
return caches.open(cacheKey).then(cache => {
console.log("Legger til cache", cache);

13
schemas/Subscription.js Normal file
View File

@@ -0,0 +1,13 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Subscription = new Schema({
endpoint: String,
expirationTime: Number,
keys: {
p256dh: String,
auth: String
}
});
module.exports = mongoose.model("Subscription", Subscription);

View File

@@ -6,6 +6,7 @@ const User = require(path.join(__dirname + "/schemas/User"));
const updateApi = require(path.join(__dirname + "/api/update"));
const retrieveApi = require(path.join(__dirname + "/api/retrieve"));
const subscriptionApi = require(path.join(__dirname + "/api/subscriptions"));
const loginApi = require(path.join(__dirname + "/api/login"));
const bodyParser = require("body-parser");
@@ -79,6 +80,7 @@ app.use("/dist", express.static(path.join(__dirname, "public/dist")));
app.use("/", loginApi);
app.use("/api/", updateApi);
app.use("/api/", retrieveApi);
app.use("/subscription", subscriptionApi);
app.use("/service-worker.js", function(req, res) {
res.sendFile(path.join(__dirname, "public/sw/serviceWorker.js"));

View File

@@ -23,6 +23,15 @@ export default {
console.log(
"Arbeids arbeideren din er installert. Du kan nå gå offline frem til neste trekning."
);
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
}
window.Notification.requestPermission().then(permission => {
if (permission !== "granted") {
throw new Error("Permission not granted for Notification");
}
});
})
.catch(error => {
console.error("Arbeids arbeideren klarer ikke arbeide.", error);
@@ -40,7 +49,7 @@ export default {
@font-face {
font-family: "knowit";
font-weight: 600;
src: url("/../public/assets/fonts/bold.eot"),
src: url("/../public/assets/fonts/bold.woff"),
url("/../public/assets/fonts/bold.woff") format("woff"), local("Arial");
font-display: swap;
}

View File

@@ -3,7 +3,7 @@
@font-face {
font-family: "knowit";
font-weight: 600;
src: url("/../../public/assets/fonts/bold.eot");
src: url("/../../public/assets/fonts/bold.woff");
}
@font-face {