Я нашел решение. Я не знаю, является ли это оптимальным, но он работает для меня:
сначала требуется выполнить много шагов настройки:
//Installl web push module
npm install web-push --save
//Generate VAPID keys by console
cd ./node_modules/.bin/web-push generate-vapid-keys
//Store VAPID keys on environment
publicVapidKey: 'generated key',
privateVapidKey: 'generated key'
process.env.PUBLIC_VAPID_KEY = config.publicVapidKey;
process.env.PRIVATE_VAPID_KEY = config.privateVapidKey;
вот код контроллера nodejs, который получает подписку от клиентаи сохраните его в БД:
//Subscriptions
app.post('/subscribe', (req, res) => {
const subscription = req.body.subscription;
const userId = req.body.userId;
console.dir(subscription);
//TODO: Store subscription keys and userId in DB
webpush.setVapidDetails(
process.env.DOMAIN,
process.env.PUBLIC_VAPID_KEY,
process.env.PRIVATE_VAPID_KEY
);
res.sendStatus(200);
const payload = JSON.stringify({
title: model.lang.pushTitle,
body: model.lang.pushBody
});
webpush.sendNotification(subscription, payload);
});
Вот метод, который я нашел для отправки сообщения с сервера на клиент или клиенты:
//Send push message
//TODO: Recover subscription keys from DB
var subscription = {
endpoint: 'recover from DB',
expirationTime: null,
keys: {
p256dh: 'recover from DB',
auth: 'recover from DB'
}
};
const payload = JSON.stringify({
title: 'Notification Title',
body: 'Notification message'
});
webpush.setVapidDetails(
process.env.DOMAIN,
process.env.PUBLIC_VAPID_KEY,
process.env.PRIVATE_VAPID_KEY
);
webpush.sendNotification(subscription, payload)
.catch(function(err) {
console.log(err);
});
здесь есть методы в сценарии просмотра клиентачтобы сделать подписку с необходимыми данными:
//Start subscription
const publicVapidKey = 'public key of server';
if (window.Notification) {
if (Notification.permission != 'granted') {
Notification.requestPermission(() => {
if (Notification.permission === 'granted') {
getSubscriptionObject().then(subscribe)
}
}).catch(function(err) {
console.log(err);
});
}
}
//Generate subscription object
function getSubscriptionObject() {
return navigator.serviceWorker.register('/layout/service-worker-push.js')
.then((worker) => {
return worker.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
}).catch(function(err) {
console.log(err);
});
}).catch(function(err) {
console.log(err);
});
}
//Send subscription to server
function subscribe(subscription) {
return fetch(window.location.origin + '/subscribe', {
method: 'POST',
body: JSON.stringify({
subscription: subscription,
userId: mv.user_id != undefined ? mv.user_id : ''
}),
headers: {
'content-type': 'application/json'
}
}).catch(function(err) {
console.log(err);
});
}
//Decoder base64 to uint8
function urlBase64ToUint8Array(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;
}
Вот этот вокер (должен быть импортирован в поле зрения), который творит чудеса в клиенте
//Event that shows a notification when is received by push
self.addEventListener('push', event => {
const data = event.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: "/layout/src/android-chrome-192x192.png"
});
});
//Event on notification click (have problems almost in Chrome)
self.addEventListener('notificationclick', () => {
console.log('Notificación pulsada!');
});
и, ну, в общем, импорто работающем представителе
<script type="text/javascript" src="/layout/service-worker-push.js"></script>
примечание: я проверил его только на локальном хосте, поэтому не знаю, требуется ли сертификат SSL.