Using webpush, just a quick and dirty POC to see if i can get push notifications to work. index.html just has two buttons, one to subscribe, one to test the push notification.
I have sufficient logs to see that the subscription happens. Clicking notify does post to /notify and returns a 200. I generated keys (pair is in server.js, the public key is in index.html)
Chrome outputs the worker’s console.log‘s, but neither Chrome nor Firefox actually fire the push notification.
Running this locally on port 3000 (node server.js) and then I have an A record pointing to a subdomain I have a reverse proxy (nginx proxy manager) running to force SSL (with websockets enabled if that matters). So I’m accessing this from a valid certificate https://sub.mydomain.com in Chrome and Firefox, but the push notification never actually fires.
I’m certain push notifications are allowed in browser settings, the subscription works to enable the permission for the domain. I even tried enabling service worker debugging in Firefox–no dice.
server.js
const express = require('express');
const webpush = require('web-push');
const path = require('path');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
// Serve static files from the 'public' directory
app.use(express.static(path.join(__dirname, 'public')));
const publicVapidKey = 'my pub key';
const privateVapidKey = 'my priv key';
webpush.setVapidDetails('mailto:[email protected]', publicVapidKey, privateVapidKey);
let subscriptions = [];
app.post('/subscribe', (req, res) => {
const subscription = req.body;
subscriptions.push(subscription);
console.log('subscriptions: ', subscriptions);
res.status(201).json({});
});
app.post('/notify', (req, res) => {
console.log('hit /notify');
const payload = JSON.stringify({
title: 'New Notification',
message: 'You have a new notification!'
});
subscriptions.forEach(sub => {
webpush.sendNotification(sub, payload).catch(err => {
console.error('Error sending notification:', err);
});
});
res.status(200).json({});
});
app.use(express.static('public'));
app.listen(3000, () => {
console.log('Server started on port 3000');
});
worker.js
self.addEventListener('push', event => {
console.log('push received');
console.log(event);
console.log(event.data);
console.log(event.data.json());
const data = event.data.json();
// debugger;
self.registration.showNotification(data.title, {
body: data.message,
icon: '/icon.png'
});
// tried this too:
//const notification = new Notification(data.title, {
// body: data.message,
// icon: '/icon.png'
//});
});
index.html (relevant parts)
// Subscribe for push notifications
async function subscribeUser() {
const registration = await navigator.serviceWorker.register('/worker.js', { scope: '/' });
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
await fetch('/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
headers: { 'Content-Type': 'application/json' }
});
alert('Subscribed successfully!');
}
// Send a test notification
async function sendNotification() {
await fetch('/notify', { method: 'POST' });
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}
document.getElementById('subscribe').addEventListener('click', subscribeUser);
document.getElementById('notify').addEventListener('click', sendNotification);