const express = require('express'); const webpush = require('web-push'); const router = express.Router(); const { getDb } = require('../models/db'); const { authMiddleware } = require('../middleware/auth'); // Get or generate VAPID keys stored in settings function getVapidKeys() { const db = getDb(); let pub = db.prepare("SELECT value FROM settings WHERE key = 'vapid_public'").get(); let priv = db.prepare("SELECT value FROM settings WHERE key = 'vapid_private'").get(); if (!pub?.value || !priv?.value) { const keys = webpush.generateVAPIDKeys(); const ins = db.prepare("INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?"); ins.run('vapid_public', keys.publicKey, keys.publicKey); ins.run('vapid_private', keys.privateKey, keys.privateKey); console.log('[Push] Generated new VAPID keys'); return keys; } return { publicKey: pub.value, privateKey: priv.value }; } function initWebPush() { const keys = getVapidKeys(); webpush.setVapidDetails( 'mailto:admin@jama.local', keys.publicKey, keys.privateKey ); return keys.publicKey; } // Export for use in index.js let vapidPublicKey = null; function getVapidPublicKey() { if (!vapidPublicKey) vapidPublicKey = initWebPush(); return vapidPublicKey; } // Send a push notification to all subscriptions for a user async function sendPushToUser(userId, payload) { const db = getDb(); getVapidPublicKey(); // ensure webpush is configured const subs = db.prepare('SELECT * FROM push_subscriptions WHERE user_id = ?').all(userId); for (const sub of subs) { try { await webpush.sendNotification( { endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } }, JSON.stringify(payload) ); } catch (err) { if (err.statusCode === 410 || err.statusCode === 404) { // Subscription expired — remove it db.prepare('DELETE FROM push_subscriptions WHERE id = ?').run(sub.id); } } } } // GET /api/push/vapid-public — returns VAPID public key for client subscription router.get('/vapid-public', (req, res) => { res.json({ publicKey: getVapidPublicKey() }); }); // POST /api/push/subscribe — save push subscription for current user router.post('/subscribe', authMiddleware, (req, res) => { const { endpoint, keys } = req.body; if (!endpoint || !keys?.p256dh || !keys?.auth) { return res.status(400).json({ error: 'Invalid subscription' }); } const db = getDb(); db.prepare(` INSERT INTO push_subscriptions (user_id, endpoint, p256dh, auth) VALUES (?, ?, ?, ?) ON CONFLICT(endpoint) DO UPDATE SET user_id = ?, p256dh = ?, auth = ? `).run(req.user.id, endpoint, keys.p256dh, keys.auth, req.user.id, keys.p256dh, keys.auth); res.json({ success: true }); }); // POST /api/push/unsubscribe — remove subscription router.post('/unsubscribe', authMiddleware, (req, res) => { const { endpoint } = req.body; if (!endpoint) return res.status(400).json({ error: 'Endpoint required' }); const db = getDb(); db.prepare('DELETE FROM push_subscriptions WHERE user_id = ? AND endpoint = ?').run(req.user.id, endpoint); res.json({ success: true }); }); module.exports = { router, sendPushToUser, getVapidPublicKey };