简体   繁体   中英

Push notifications on JavaScript with NodeJs Express

I wanted to send notifications to specific users from NodeJs. The idea is to be independant from firebase and any kind of third party server if it's possible. I'm finding a lot of info about how to build a service worker and a lot of info about show notifications, but I can't find anything that could work for me about sendind the push messages to the workker to be shown.

Here is the service-worker.js

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/layout/service-worker.js')
    .then(function (registration) {
        registration.pushManager.subscribe({
            userVisibleOnly: true
        }).then(function (subscription) {
            isPushEnabled = true;
            console.log("subscription.endpoint: ", subscription.endpoint);
            //Here I will have to store the endpoint on my DB
        });
    }).catch(function (err) {
        console.log(err);
    });
}

//I think that this could be the listener, but I don't know how to trigger it from the server
this.onpush = function (event) {
    console.log(event.data);
}

I have this code for show the notification (when I could send notifications)

var notificationTitle = 'Title';
var notificationOptions = {
    "body": "Some text",
    //"icon": "images/icon.png",
    "vibrate": [200, 100, 200, 100, 200, 100, 400]
}
showNotification(notificationTitle, notificationOptions);

The idea is to implement this code inside the "onpush" event qhen I will know what format I will recieve.

So I need a method about send the push to those methods, but don't now how to. I read something about node web-push module with VAPID keys, but I still not found the sender jet. I have a manifest.json that I don't know if it really do anything (I read that is necesary for Chrome navigator, but no idea, I'm testing all in Chrome, Firefox and Edge).

Aditionally I'm using http in localhost to test it. I don't really know if this will work without an SSL certificate or an autosigned one.

Every help would be apreciated.

I found the solution. I don't know if is optimal, but it workks for me:

at first many configuration steps are required:

//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;

here is the nodejs controller code that receive the subscription from client and store it in DB:

//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);
});

Here is the method I found to push a message from server to client or clients:

//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);
});

here there are the methods in client view script to make a subscription wirth the necesary data:

//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;
}

Here is the woker (must be imported in view) who do the magic in client

//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!');
});

and, well, the import of the worker in view

<script type="text/javascript" src="/layout/service-worker-push.js"></script>

note: I only tested it on localhost so I don't know if an SSL certificate is required.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM