"use strict"; const _ = require("lodash"); const log = require("../log"); const fs = require("fs"); const path = require("path"); const WebPushAPI = require("web-push"); const Config = require("../config"); class WebPush { constructor() { const vapidPath = path.join(Config.getHomePath(), "vapid.json"); 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`); } } const data = fs.readFileSync(vapidPath, "utf-8"); const parsedData = JSON.parse(data); if ( typeof parsedData.publicKey === "string" && typeof parsedData.privateKey === "string" ) { this.vapidKeys = { publicKey: parsedData.publicKey, privateKey: parsedData.privateKey, }; } } if (!this.vapidKeys) { this.vapidKeys = WebPushAPI.generateVAPIDKeys(); fs.writeFileSync(vapidPath, JSON.stringify(this.vapidKeys, null, "\t"), { mode: 0o600, }); log.info("New VAPID key pair has been generated for use with push subscription."); } WebPushAPI.setVapidDetails( "https://github.com/thelounge/thelounge", this.vapidKeys.publicKey, this.vapidKeys.privateKey ); } push(client, payload, onlyToOffline) { _.forOwn(client.config.sessions, ({pushSubscription}, token) => { if (pushSubscription) { if (onlyToOffline && _.find(client.attachedClients, {token}) !== undefined) { return; } this.pushSingle(client, pushSubscription, payload); } }); } pushSingle(client, subscription, payload) { 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` ); _.forOwn(client.config.sessions, ({pushSubscription}, token) => { if (pushSubscription && pushSubscription.endpoint === subscription.endpoint) { client.unregisterPushSubscription(token); } }); return; } log.error(`WebPush Error (${error})`); }); } } module.exports = WebPush;