Skip to content

Instantly share code, notes, and snippets.

@aliams

aliams/db.js Secret

Last active April 17, 2018 21:00
Show Gist options
  • Save aliams/5b0d1cf073b96c50198f5268cd75b834 to your computer and use it in GitHub Desktop.
Save aliams/5b0d1cf073b96c50198f5268cd75b834 to your computer and use it in GitHub Desktop.
Service worker with push example (client and server)
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 };
<!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>
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'); });
}
});
});
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);
});
});
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); })
])
    );
}
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