106 lines
4.0 KiB
JavaScript
106 lines
4.0 KiB
JavaScript
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/generate-vapid — admin: generate (or regenerate) VAPID keys
|
|
router.post('/generate-vapid', authMiddleware, (req, res) => {
|
|
if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admins only' });
|
|
const db = getDb();
|
|
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);
|
|
// Reinitialise webpush with new keys immediately
|
|
webpush.setVapidDetails('mailto:admin@jama.local', keys.publicKey, keys.privateKey);
|
|
vapidPublicKey = keys.publicKey;
|
|
console.log('[Push] VAPID keys regenerated by admin');
|
|
res.json({ publicKey: keys.publicKey });
|
|
});
|
|
|
|
// 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 };
|