-
-
Save aliams/5b0d1cf073b96c50198f5268cd75b834 to your computer and use it in GitHub Desktop.
Service worker with push example (client and server)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const DB_URI = 'mongodb://user:pass@server.com'; | |
const mongoose = require('mongoose'); | |
mongoose.Promise = global.Promise; | |
const { Schema } = mongoose; | |
const SubscriptionSchema = new Schema({ | |
endpoint: { type: String, index: true }, | |
keys: { | |
auth: { type: String }, | |
p256dh: { type: String } | |
}, | |
created: {type: Date, default: Date.now } | |
}); | |
const Subscription = mongoose.model('Subscription', SubscriptionSchema); | |
mongoose.connect(DB_URI).catch(function(e) { console.error(e); }); | |
module.exports = { Subscription }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Web Push Sample</title> | |
</head> | |
<body> | |
<script src="util.js"></script> | |
<script> | |
if (navigator.serviceWorker) { | |
navigator.serviceWorker.register('sw.js'); | |
navigator.serviceWorker.ready | |
.then(function(reg) { | |
return reg.pushManager.getSubscription().then(function(sub) { | |
if (sub) | |
// renew subscription if we're within 5 days of expiration | |
if (sub.expirationTime && | |
sub.expirationTime - 432000000 < Date.now()) { | |
return sub.unsubscribe().then(function() { | |
return subscribePush(reg); | |
}); | |
} | |
return sub; | |
} | |
return subscribePush(reg); | |
}); | |
}) | |
.then(function(sub) { saveSubscription(sub); }); | |
} | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const webPush = require('web-push'); | |
const vapidPublicKey = process.env.VAPID_PUBLIC_KEY; | |
const vapidPrivateKey = process.env.VAPID_PRIVATE_KEY; | |
webPush.setVapidDetails( | |
'mailto:email@outlook.com', | |
vapidPublicKey, | |
vapidPrivateKey | |
); | |
const pushMessage = ''; | |
const { Subscription } = require('./db'); | |
Subscription.find().cursor().eachAsync(function(sub) { | |
return webPush.sendNotification(sub, pushMessage).catch(function(e) { | |
if (e.statusCode === 410) { | |
return Subscription.remove({ endpoint: sub.endpoint }).exec() | |
.catch(function(sub) { console.error('failed'); }); | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
process.env.VAPID_PUBLIC_KEY = ''; | |
process.env.VAPID_PRIVATE_KEY = ''; | |
const PORT = 4000; | |
const express = require('express'); | |
const bodyParser = require('body-parser'); | |
const { Subscription } = require('./db'); | |
const app = express(); | |
app.use(express.static('src')); | |
app.use(bodyParser.json()); | |
app.listen(PORT, function() { | |
console.log('Server listening on port ' + PORT); | |
}); | |
const webPush = require('web-push'); | |
const vapidPublicKey = process.env.VAPID_PUBLIC_KEY; | |
const vapidPrivateKey = process.env.VAPID_PRIVATE_KEY; | |
// helper to generate VAPID keys if needed | |
// webPush.generateVAPIDKeys(); | |
webPush.setVapidDetails( | |
'mailto:email@outlook.com', | |
vapidPublicKey, | |
vapidPrivateKey | |
); | |
app.get('/api/key', function(req, res) { | |
res.send({ key: vapidPublicKey }); | |
}); | |
app.post('/api/subscribe', function(req, res) { | |
const sub = req.body.subscription; | |
Subscription.findOne({ endpoint: sub.endpoint }) | |
.then(function(exists) { | |
if (exists) { | |
res.status(400).send('exists'); | |
} else { | |
const data = new Subscription(sub); | |
data.save().then(function() { | |
res.status(200).send('success'); | |
}); | |
} | |
}) | |
.catch(function(e) { | |
res.status(500).send(e.message); | |
}); | |
}); | |
app.post('/api/unsubscribe', async function(req, res) { | |
const sub = req.body.subscription; | |
Subscription.remove({ endpoint: sub.endpoint }) | |
.then(function() { | |
res.status(200).send('Success'); | |
}) | |
.catch(function(e) { | |
res.status(500).send(e.message); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
importScripts('util.js'); | |
self.onpush = function(event) { | |
const payload = event.data ? event.data.text() : 'no payload'; | |
event.waitUntil( | |
registration.showNotification('Did you know?', { | |
body: payload, | |
icon: 'icon.png' | |
}) | |
); | |
} | |
self.onnotificationclick = function(event) { | |
event.notification.close(); | |
event.waitUntil( | |
clients.matchAll({ type: 'window', includeUncontrolled: true }) | |
.then(function(clientList) { | |
if (clientList.length > 0) { | |
let client = clientList[0]; | |
for (let i = 0; i < clientList.length; i++) { | |
if (clientList[i].focused) { | |
client = clientList[i]; | |
} | |
} | |
return client.focus(); | |
} | |
return clients.openWindow('/'); | |
}) | |
); | |
} | |
self.onpushsubscriptionchange = function(event) { | |
event.waitUntil( | |
Promise.all([ | |
Promise.resolve(event.oldSubscription ? | |
deleteSubscription(event.oldSubscription) : true), | |
Promise.resolve(event.newSubscription ? | |
event.newSubscription : | |
subscribePush(registration)) | |
.then(function(sub) { return saveSubscription(sub); }) | |
]) | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function urlB64ToUint8Array(base64String) { | |
const padding = '='.repeat((4 - base64String.length % 4) % 4); | |
const base64 = (base64String + padding) | |
.replace(/\-/g, '+').replace(/_/g, '/'); | |
const rawData = window.atob(base64); | |
const outputArray = new Uint8Array(rawData.length); | |
for (let i = 0; i < rawData.length; ++i) { | |
outputArray[i] = rawData.charCodeAt(i); | |
} | |
return outputArray; | |
} | |
function subscribePush(reg) { | |
return getPublicKey().then(function(key) { | |
return reg.pushManager.subscribe({ | |
userVisibleOnly: true, | |
applicationServerKey: key | |
}); | |
}); | |
} | |
function getPublicKey() { | |
return fetch('./api/key') | |
.then(function(response) { return response.json(); }) | |
.then(function(data) { return urlB64ToUint8Array(data.key); }); | |
} | |
function publishSubscription(subscription, remove) { | |
return fetch('./api/' + (remove ? 'un' : '') + 'subscribe', { | |
method: 'post', | |
headers: { | |
'Content-type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
subscription: subscription | |
}) | |
}); | |
} | |
function saveSubscription(subscription) { | |
return publishSubscription(subscription); | |
} | |
function deleteSubscription(subscription) { | |
return publishSubscription(subscription, true); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment