使用 NodeJs Express 在 JavaScript 上推送通知

Push notifications on JavaScript with NodeJs Express

我想从 NodeJs 向特定用户发送通知。如果可能的话,这个想法是独立于 firebase 和任何类型的第三方服务器。我找到了很多关于如何构建服务工作者的信息和很多关于显示通知的信息,但是我找不到任何关于将推送消息发送给要显示的工作者的信息。

这里是服务-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);
}

我有这个显示通知的代码(当我可以发送通知时)

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

我的想法是在 "onpush" 事件中实现此代码 qhen 我会知道我将收到什么格式。

所以我需要一种将推送发送到这些方法的方法,但现在不知道如何发送。我阅读了一些关于带有 VAPID 密钥的节点网络推送模块的内容,但我仍然没有找到发送者 jet。我有一个 manifest.json,我不知道它是否真的有任何作用(我读到它是 Chrome 导航器所必需的,但不知道,我正在 Chrome、Firefox 中测试所有内容和边缘)。

此外,我在本地主机中使用 http 来测试它。我真的不知道如果没有 SSL 证书或自动签名证书,这是否可行。

我们将不胜感激。

我找到了解决方案。我不知道是否最优,但它对我有用:

首先需要很多配置步骤:

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

这里是在客户端做魔术的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!');
});

还有,好吧,视图中的 worker 的导入

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

注意:我只在本地主机上测试过,所以我不知道是否需要 SSL 证书。