Don't let a malformed VAPID_SUBJECT crash the whole app

setVapidDetails() throws synchronously on an invalid subject (e.g. missing
the mailto:/https: prefix — an easy mistake). It ran unguarded at module
load, so a bad value took down the entire server, not just push. Wrapped
in try/catch; push now degrades the same way as when it's unconfigured,
and /api/push/vapid-public-key reflects actual readiness (post-validation)
instead of just whether the env vars were present.
This commit is contained in:
ort
2026-08-15 16:18:07 -04:00
parent 2826a3819c
commit fdba8c0d1f
2 changed files with 15 additions and 4 deletions
+13 -3
View File
@@ -2,8 +2,18 @@ const webpush = require('web-push');
const db = require('../db');
const config = require('../config');
// A malformed VAPID_SUBJECT (missing "mailto:"/"https:", etc.) makes
// setVapidDetails throw synchronously — without this try/catch that would
// crash the entire app at boot, not just disable push. Bad config should
// degrade the same way as absent config: push off, everything else fine.
let pushReady = false;
if (config.pushConfigured) {
webpush.setVapidDetails(config.vapidSubject, config.vapidPublicKey, config.vapidPrivateKey);
try {
webpush.setVapidDetails(config.vapidSubject, config.vapidPublicKey, config.vapidPrivateKey);
pushReady = true;
} catch (err) {
console.error('[push] Invalid VAPID configuration — push notifications disabled:', err.message);
}
}
const listSubscriptionsForHouseholdStmt = db.prepare(`
@@ -15,7 +25,7 @@ const listSubscriptionsForHouseholdStmt = db.prepare(`
const deleteSubscriptionStmt = db.prepare('DELETE FROM push_subscriptions WHERE id = ?');
async function notifyHouseholdParents(householdId, payload) {
if (!config.pushConfigured) return;
if (!pushReady) return;
const subs = listSubscriptionsForHouseholdStmt.all(householdId);
if (subs.length === 0) return;
@@ -44,4 +54,4 @@ async function notifyHouseholdParents(householdId, payload) {
return results;
}
module.exports = { notifyHouseholdParents };
module.exports = { notifyHouseholdParents, isPushReady: () => pushReady };
+2 -1
View File
@@ -2,6 +2,7 @@ const express = require('express');
const db = require('../db');
const config = require('../config');
const requireAuth = require('../middleware/requireAuth');
const { isPushReady } = require('../lib/push');
const router = express.Router();
router.use(requireAuth);
@@ -21,7 +22,7 @@ const deleteOwnSubscriptionStmt = db.prepare(
);
router.get('/vapid-public-key', (req, res) => {
if (!config.pushConfigured) return res.json({ enabled: false });
if (!isPushReady()) return res.json({ enabled: false });
res.json({ enabled: true, publicKey: config.vapidPublicKey });
});