简体   繁体   English

使用 NodeJs Express 在 JavaScript 上推送通知

[英]Push notifications on JavaScript with NodeJs Express

I wanted to send notifications to specific users from NodeJs.我想从 NodeJs 向特定用户发送通知。 The idea is to be independant from firebase and any kind of third party server if it's possible.如果可能的话,这个想法是独立于 firebase 和任何类型的第三方服务器。 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这是 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.这个想法是在“onpush”事件中实现这个代码,我会知道我会收到什么格式。

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.我读了一些关于带有 VAPID 键的节点 web-push 模块的内容,但我仍然没有找到发送者喷气机。 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).我有一个 manifest.json,我不知道它是否真的做任何事情(我读到这是 Chrome 导航器所必需的,但不知道,我正在 Chrome、Firefox 和 Edge 中测试所有内容)。

Aditionally I'm using http in localhost to test it.此外,我在 localhost 中使用 http 对其进行测试。 I don't really know if this will work without an SSL certificate or an autosigned one.我真的不知道如果没有 SSL 证书或自动签名证书,这是否可行。

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:这是从客户端接收订阅并将其存储在数据库中的nodejs controller 代码:

//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这是在客户端中施展魔法的woker(必须在视图中导入)

//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.注意:我只在 localhost 上测试过,所以我不知道是否需要 SSL 证书。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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