2017-07-10 15:47:03 -04:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const _ = require("lodash");
|
2018-06-15 16:31:06 -04:00
|
|
|
const log = require("../log");
|
2017-07-10 15:47:03 -04:00
|
|
|
const fs = require("fs");
|
|
|
|
const path = require("path");
|
|
|
|
const WebPushAPI = require("web-push");
|
2022-05-01 15:12:39 -04:00
|
|
|
const Config = require("../config");
|
2017-07-10 15:47:03 -04:00
|
|
|
|
|
|
|
class WebPush {
|
|
|
|
constructor() {
|
2022-05-01 15:12:39 -04:00
|
|
|
const vapidPath = path.join(Config.getHomePath(), "vapid.json");
|
2017-07-10 15:47:03 -04:00
|
|
|
|
2021-11-23 11:53:00 -05:00
|
|
|
let vapidStat = undefined;
|
|
|
|
|
|
|
|
try {
|
|
|
|
vapidStat = fs.statSync(vapidPath);
|
|
|
|
} catch {
|
|
|
|
// ignored on purpose, node v14.17.0 will give us {throwIfNoEntry: false}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vapidStat) {
|
|
|
|
const isWorldReadable = (vapidStat.mode & 0o004) !== 0;
|
|
|
|
|
|
|
|
if (isWorldReadable) {
|
|
|
|
log.warn(
|
|
|
|
vapidPath,
|
|
|
|
"is world readable. The file contains secrets. Please fix the permissions"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (require("os").platform() !== "win32") {
|
|
|
|
log.warn(`run \`chmod o= ${vapidPath}\` to correct it`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:47:03 -04:00
|
|
|
const data = fs.readFileSync(vapidPath, "utf-8");
|
|
|
|
const parsedData = JSON.parse(data);
|
|
|
|
|
2019-07-17 05:33:59 -04:00
|
|
|
if (
|
|
|
|
typeof parsedData.publicKey === "string" &&
|
|
|
|
typeof parsedData.privateKey === "string"
|
|
|
|
) {
|
2017-07-10 15:47:03 -04:00
|
|
|
this.vapidKeys = {
|
|
|
|
publicKey: parsedData.publicKey,
|
|
|
|
privateKey: parsedData.privateKey,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.vapidKeys) {
|
|
|
|
this.vapidKeys = WebPushAPI.generateVAPIDKeys();
|
|
|
|
|
2021-11-23 11:53:00 -05:00
|
|
|
fs.writeFileSync(vapidPath, JSON.stringify(this.vapidKeys, null, "\t"), {
|
|
|
|
mode: 0o600,
|
|
|
|
});
|
2017-07-10 15:47:03 -04:00
|
|
|
|
|
|
|
log.info("New VAPID key pair has been generated for use with push subscription.");
|
|
|
|
}
|
|
|
|
|
|
|
|
WebPushAPI.setVapidDetails(
|
2018-02-21 12:48:22 -05:00
|
|
|
"https://github.com/thelounge/thelounge",
|
2017-07-10 15:47:03 -04:00
|
|
|
this.vapidKeys.publicKey,
|
|
|
|
this.vapidKeys.privateKey
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
push(client, payload, onlyToOffline) {
|
2017-11-22 01:39:32 -05:00
|
|
|
_.forOwn(client.config.sessions, ({pushSubscription}, token) => {
|
|
|
|
if (pushSubscription) {
|
|
|
|
if (onlyToOffline && _.find(client.attachedClients, {token}) !== undefined) {
|
2017-07-10 15:47:03 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-22 01:39:32 -05:00
|
|
|
this.pushSingle(client, pushSubscription, payload);
|
2017-07-10 15:47:03 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pushSingle(client, subscription, payload) {
|
2019-07-17 05:33:59 -04:00
|
|
|
WebPushAPI.sendNotification(subscription, JSON.stringify(payload)).catch((error) => {
|
|
|
|
if (error.statusCode >= 400 && error.statusCode < 500) {
|
|
|
|
log.warn(
|
|
|
|
`WebPush subscription for ${client.name} returned an error (${error.statusCode}), removing subscription`
|
|
|
|
);
|
2017-07-10 15:47:03 -04:00
|
|
|
|
2019-07-17 05:33:59 -04:00
|
|
|
_.forOwn(client.config.sessions, ({pushSubscription}, token) => {
|
|
|
|
if (pushSubscription && pushSubscription.endpoint === subscription.endpoint) {
|
|
|
|
client.unregisterPushSubscription(token);
|
|
|
|
}
|
|
|
|
});
|
2017-07-10 15:47:03 -04:00
|
|
|
|
2019-07-17 05:33:59 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-07-10 15:47:03 -04:00
|
|
|
|
2019-07-17 05:33:59 -04:00
|
|
|
log.error(`WebPush Error (${error})`);
|
|
|
|
});
|
2017-07-10 15:47:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = WebPush;
|