将用户从OneSignal迁移到我们自己的数据库,我们从中推送通知

时间:2019-02-11 20:41:03

标签: node.js push-notification firebase-cloud-messaging

我正在尝试从OneSignal迁移我们的订户。我导出了端点,密钥(auth和P256DH),并在服务器上配置了我的OS帐户的VAPID密钥。

当我尝试从OS发送通知,然后删除OS的服务工作者并使用自己的服务时,它会发送与以前通过OS发送的通知相同的通知(很奇怪),并且当我以编程方式删除OS的服务工作者(通过控制台)并注册我自己的服务工作者,它的响应来自chrome(“未注册”)的410错误和Firefox的401错误(“请求未验证缺少的授权标头”)。
app.js文件:

let isSubscribed = false;
let swRegistration = null;
let applicationKey = "PUBLIC_VAPID_KEY_HERE";


function urlB64ToUint8Array(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;
}

if ('serviceWorker' in navigator && 'PushManager' in window) {
    console.log('Service Worker and Push is supported');

    navigator.serviceWorker.register('sw.js')
        .then(function (swReg) {
            console.log('service worker registered');

            swRegistration = swReg;

            swRegistration.pushManager.getSubscription()
                .then(function (subscription) {
                    isSubscribed = !(subscription === null);

                    if (isSubscribed) {
                        console.log('User is subscribed');
                    } else {
                        swRegistration.pushManager.subscribe({
                                userVisibleOnly: true,
                                applicationServerKey: urlB64ToUint8Array(applicationKey)
                            })
                            .then(function (subscription) {
                                console.log(subscription);
                                console.log('User is subscribed');

                                saveSubscription(subscription);

                                isSubscribed = true;
                            })
                            .catch(function (err) {
                                console.log('Failed to subscribe user: ', err);
                            })
                    }
                })
        })
        .catch(function (error) {
            console.error('Service Worker Error', error);
        });
} else {
    console.warn('Push messaging is not supported');
}

function saveSubscription(subscription) {
    let xmlHttp = new XMLHttpRequest();
    xmlHttp.open("POST", "/subscribe");
    xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState != 4) return;
        if (xmlHttp.status != 200 && xmlHttp.status != 304) {
            console.log('HTTP error ' + xmlHttp.status, null);
        } else {
            console.log("User subscribed to server");
        }
    };

    xmlHttp.send(JSON.stringify(subscription));
}

sw.js文件:

let notificationUrl = '';

self.addEventListener('push', function (event) {
    console.log('Push received: ', event);
    let _data = event.data ? JSON.parse(event.data.text()) : {};
    notificationUrl = _data.url;
    event.waitUntil(
        self.registration.showNotification(_data.title, {
            body: _data.message,
            icon: _data.icon,
            tag: _data.tag
        })
    );
});

self.addEventListener('notificationclick', function (event) {
    event.notification.close();

    event.waitUntil(
        clients.matchAll({
            type: "window"
        })
        .then(function (clientList) {
            if (clients.openWindow) {
                return clients.openWindow(notificationUrl);
            }
        })
    );
});

推送通知的push.js文件:

const express = require('express');
const router = express.Router();
const q = require('q');
const webPush = require('web-push');
const keys = require('./../config/keys');

const mysql = require("mysql");
const pool = mysql.createPool({
  connectionLimit: 10,
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'webpush',
  multipleStatements: true,
  dateStrings: true
});

router.post('/push', (req, res) => {
    const payload = {
        title: req.body.title,
        message: req.body.message,
        url: req.body.url,
        ttl: req.body.ttl,
        icon: req.body.icon,
        image: req.body.image,
        badge: req.body.badge,
        tag: req.body.tag
    };

    pool.query('SELECT * FROM subscriber', (err, subscriptions) => {
        if (err) {
            return console.log(err);
            console.error(`Error occurred while getting subscriptions`);
            return res.status(500).json({
                error: 'Technical error occurred'
            });
        }
        if (!subscriptions.length) {
            console.error(`No subscribers found`);
            return res.status(500).json({
                error: 'Subscribers not found'
            });
        }
        let parallelSubscriptionCalls = subscriptions.map(subscription => {
            return new Promise((resolve, reject) => {
                const pushSubscription = {
                    endpoint: subscription.endpoint,
                    keys: {
                        p256dh: subscription.p256dh,
                        auth: subscription.auth
                    }
                };

                const pushPayload = JSON.stringify(payload);
                const pushOptions = {
                    vapidDetails: {
                        subject: 'https://www.mydomainhere.com',
                        privateKey: keys.privateKey,
                        publicKey: keys.publicKey
                    },
                    TTL: payload.ttl,
                    headers: {}
                };
                webPush.sendNotification(pushSubscription, pushPayload, pushOptions)
                .then((value) => {
                    resolve({
                        status: true,
                        endpoint: subscription.endpoint,
                        data: value
                    });
                }).catch((err) => {
                    reject({
                        status: false,
                        endpoint: subscription.endpoint,
                        data: err
                    });
                });
            });
        });
        q.allSettled(parallelSubscriptionCalls).then((pushResults) => {
            console.info(pushResults);
        });
        res.json({
            data: 'Push triggered'
        });
    })
});

module.exports = router;

执行订阅的subscribe.js文件:

const express = require('express');
const router = express.Router();

const mysql = require("mysql");
const pool = mysql.createPool({
  connectionLimit: 10,
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'webpush',
  multipleStatements: true,
  dateStrings: true
});


router.post('/subscribe', (req, res) => {
    const endpoint = req.body.endpoint;
    const auth = req.body.keys.auth;
    const p256dh = req.body.keys.p256dh;

    const subscriptionSet = { endpoint, auth, p256dh }

    pool.getConnection((err, connection) => {
        if (err) {
            console.error(`Error occurred while saving subscription. Err: ${err}`);
            return res.status(500).json({
                error: 'Technical error occurred'
            });
        };

        connection.query('INSERT INTO subscriber SET ?', subscriptionSet, (err, subscription) => {
            if (err) {
                console.error(`Error occurred while saving subscription. Err: ${err}`);
                return res.status(500).json({
                    error: 'Technical error occurred'
                });
            }
            res.json({
                data: 'Subscription saved.'
            })
        })
    });
});

module.exports = router;

2 个答案:

答案 0 :(得分:0)

我正在尝试做同样的事情,如何从onesignal导出订户auth和P256DH?因为在csv导出中,onesignal提供了push_token(端点),而不提供auth和P256DH。我将在哪里获得所有用户的身份验证和P256DH?

答案 1 :(得分:0)

根据我的理解,除非订阅者返回并访问您的域,否则您无法重新注册服务工作者。但是,服务人员每天更新一次,因此您可以做的是编辑现有的One Signal服务人员文件-删除其内容并添加自己的代码或导入脚本。确保不要更改文件位置或重命名一个信号服务工作程序文件,该文件名必须相同。这将被视为“更新”,浏览器应自动替换内容,而无需用户采取任何措施。如果有两个OneSignal服务工作程序文件,请同时更改两个文件。