// Web Push (background / closed-tab / mobile notifications). Fully optional: // - if the `web-push` package isn't installed, or VAPID env keys aren't set, // isEnabled() is false and every call is a silent no-op (the app is unaffected). // Configure in production by setting: // VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT (e.g. mailto:admin@bizgaze.com) // Generate a key pair once with: npx web-push generate-vapid-keys const R = require('./repos'); let webpush = null; try { webpush = require('web-push'); } catch (_) { /* package not installed -> push disabled */ } const PUBLIC = process.env.VAPID_PUBLIC_KEY || ''; const PRIVATE = process.env.VAPID_PRIVATE_KEY || ''; const SUBJECT = process.env.VAPID_SUBJECT || 'mailto:support@bizgaze.com'; let ready = false; if (webpush && PUBLIC && PRIVATE) { try { webpush.setVapidDetails(SUBJECT, PUBLIC, PRIVATE); ready = true; } catch (e) { console.warn('[push] invalid VAPID config:', e.message); } } if (!ready) console.log('[push] Web Push disabled (set web-push + VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY to enable).'); function isEnabled() { return ready; } function publicKey() { return ready ? PUBLIC : ''; } // Fire-and-forget push to every device the user has subscribed. Dead subscriptions // (410 Gone / 404) are pruned. Never throws. async function sendToUser(userId, payload) { if (!ready) return; let subs = []; try { subs = R.pushSubs.byUser(userId); } catch (_) { return; } const data = JSON.stringify(payload || {}); for (const s of subs) { const sub = { endpoint: s.endpoint, keys: { p256dh: s.p256dh, auth: s.auth } }; try { await webpush.sendNotification(sub, data, { TTL: 600 }); } catch (err) { const code = err && err.statusCode; if (code === 404 || code === 410) { try { R.pushSubs.removeByEndpoint(s.endpoint); } catch (_) {} } // other errors (network, 4xx) are ignored — push is best-effort } } } module.exports = { isEnabled, publicKey, sendToUser };