{"size": 5511, "ext": "js", "max_stars_repo_path": "serv2/pushHandler.js", "max_stars_repo_name": "Smittyvb/ttw", "max_stars_repo_stars_event_min_datetime": "2020-08-12T04:18:55.000Z", "max_stars_repo_stars_event_max_datetime": "2022-01-04T12:23:54.000Z", "max_issues_repo_path": "serv2/pushHandler.js", "max_issues_repo_name": "Smittyvb/ttw", "max_issues_repo_issues_event_min_datetime": "2020-08-05T12:38:13.000Z", "max_issues_repo_issues_event_max_datetime": "2022-03-14T02:03:05.000Z", "max_forks_repo_path": "serv2/pushHandler.js", "max_forks_repo_name": "Smittyvb/ttw", "max_forks_repo_forks_event_min_datetime": "2020-09-20T07:13:47.000Z", "max_forks_repo_forks_event_max_datetime": "2021-03-26T12:38:43.000Z"}
{"max_stars_count": 19, "max_issues_count": 150, "max_forks_count": 2, "avg_line_length": 43.7380952381, "max_line_length": 151, "alphanum_fraction": 0.5913627291}
const express = require("express");
const bodyParser = require("body-parser");
const webPush = require("web-push");
const taglogic = require("./pkg/taglogic.js");
const setCorsHeaders = require("./setCorsHeaders");

const config = {
    ...require("../config.json"),
    ...require("../config-private.json")
};

webPush.setVapidDetails(
    "mailto:" + config["contact-email"],
    config["vapid-public"],
    config["vapid-private"]
);

module.exports = function pushHandler(globalDb) {
    const pushPreped = {
        "delete-sub": globalDb.prepare("DELETE FROM pushregs WHERE endpoint_uri = ? AND p256dh = ? AND auth = ?"),
        "get-sub": globalDb.prepare("SELECT seed, avg_interval, alg FROM pushregs WHERE endpoint_uri = ? AND p256dh = ? AND auth = ?"),
        "add-sub": globalDb.prepare("INSERT OR REPLACE INTO pushregs (endpoint_uri, p256dh, auth, seed, avg_interval, alg) VALUES (?, ?, ?, ?, ?, ?)"),
        "get-pints-subs": globalDb.prepare("SELECT p256dh, auth, endpoint_uri FROM pushregs WHERE seed = ? AND avg_interval = ? AND alg = ?"),
        "get-pints": globalDb.prepare("SELECT DISTINCT seed, avg_interval, alg FROM pushregs"),
    };

    const router = express.Router();
    let pints = [];

    function sendNotification(subscription, latestPing) {
        webPush.sendNotification(subscription, JSON.stringify({
            type: "ping",
            latestPing,
        })).then(function () {
            // console.log("Push Application Server - Notification sent to " + subscription.endpoint);
        }).catch(function (e) {
            console.log("ERROR in sending Notification, endpoint removed");
            pushPreped["delete-sub"].run(subscription.endpoint, subscription.keys.p256dh, subscription.keys.auth);
        });
    }

    let lastCheck = Math.floor(Date.now() / 1000);
    function checkPints() {
        let now = Math.floor(Date.now() / 1000);
        pints.forEach(pint => {
            const between = taglogic.pings_between_u32(lastCheck, now, pint);
            if (between.length > 0) {
                const usersToNotify = pushPreped["get-pints-subs"].all(pint.seed, pint.avg_interval, pint.alg);
                usersToNotify.forEach(user => {
                    const subscription = {
                        endpoint: user.endpoint_uri,
                        keys: {
                            p256dh: user.p256dh,
                            auth: user.auth,
                        }
                    };
                    sendNotification(subscription, between[between.length - 1]);
                });
            }
        });
        lastCheck = now;
        setTimeout(checkPints, 1750);
    }
    checkPints();

    router.get("/vapid-public", (req, res) => {
        setCorsHeaders(res);
        res.send(config["vapid-public"]);
    });

    router.post("/register", bodyParser.json({ limit: 10000 }), (req, res) => {
        setCorsHeaders(res);
        const json = req.body;
        if (typeof json !== "object") return res.status(400).send("no body");
        if (typeof json.subscription !== "object") return res.status(400).send("no subscription");
        const subscription = json.subscription;
        if (typeof subscription.endpoint !== "string") return res.status(400).send("no subscription");
        if (typeof subscription.keys.p256dh !== "string") return res.status(400).send("no p256dh");
        if (typeof subscription.keys.auth !== "string") return res.status(400).send("no auth");
        const pintData = json.pintData;
        if (typeof pintData.seed !== "number") return res.status(400).send("no seed");
        if (typeof pintData.avg_interval !== "number") return res.status(400).send("no avg_interval");
        if (!(pintData.alg === 0 || pintData.alg === 1)) pintData.alg = 0; // fallback for older clients
        pushPreped["add-sub"].run(
            subscription.endpoint,
            subscription.keys.p256dh,
            subscription.keys.auth,
            pintData.seed,
            pintData.avg_interval,
            pintData.alg,
        );
        res.status(201).send();
    });

    function updatePints() {
        pints = pushPreped["get-pints"].all()
            .filter(({ seed, avg_interval, alg }) => (alg === 0) || (seed === 1184097393 && avg_interval === 2700))
            .map(({ seed, avg_interval, alg }) => taglogic.new_ping_interval_data(seed, avg_interval, Boolean(alg)));
    }
    router.post("/unregister", bodyParser.json({ limit: 10000 }), (req, res) => {
        setCorsHeaders(res);
        const json = req.body;
        if (typeof json !== "object") return res.status(400).send("no body");
        if (typeof json.subscription !== "object") return res.status(400).send("no subscription");
        const subscription = json.subscription;
        if (typeof subscription.endpoint !== "string") return res.status(400).send("no subscription");
        if (typeof subscription.keys.p256dh !== "string") return res.status(400).send("no p256dh");
        if (typeof subscription.keys.auth !== "string") return res.status(400).send("no auth");
        pushPreped["delete-sub"].run(subscription.endpoint, subscription.keys.p256dh, subscription.keys.auth);
        res.status(201).send();
        updatePints();
    });
    updatePints();

    router.options("/register", (req, res) => {
        setCorsHeaders(res);
        res.send();
    });
    router.options("/unregister", (req, res) => {
        setCorsHeaders(res);
        res.send();
    });

    return router;
}
