Background
The frontend has a Web Push client (lib/push/pushManager.js) that, on enabling notifications, subscribes the browser and POSTs the subscription to POST /api/users/push-subscription. That endpoint does not exist, and the backend has no Web Push delivery at all — only a notifications.push preference boolean. So push notifications cannot work end-to-end. This issue implements the backend half. (Paired with the dnb-frontend config/UX issue.)
Scope
- Dependency + config: add
web-push; introduce VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT env vars (document in .env.example; generate with npx web-push generate-vapid-keys). The public key must equal the frontend NEXT_PUBLIC_VAPID_PUBLIC_KEY.
- Persist subscriptions per user (User model field or a
PushSubscription collection keyed by user): store endpoint, keys.{p256dh,auth}, optional userAgent, createdAt. One row per unique endpoint; upsert.
- Endpoints (authenticated, mounted under
/api/users):
POST /push-subscription — upsert the caller's subscription.
DELETE /push-subscription — remove by endpoint.
PATCH /push-subscription/preferences — update the user's push preference.
- Delivery: when a notification is created (integrate in
src/controllers/notificationController.js / the notification-send path), send a Web Push to each of the user's subscriptions via web-push, gated on the notifications.push preference. On a 404/410 from the push service, prune that dead subscription.
- Tests: subscription upsert/delete, preference PATCH, delivery (mock
web-push), preference gating, and dead-subscription pruning.
Out of scope
- The frontend config/UX (separate dnb-frontend issue).
- The separate Firebase FCM path — do not build on it here.
Acceptance criteria
Background
The frontend has a Web Push client (
lib/push/pushManager.js) that, on enabling notifications, subscribes the browser and POSTs the subscription toPOST /api/users/push-subscription. That endpoint does not exist, and the backend has no Web Push delivery at all — only anotifications.pushpreference boolean. So push notifications cannot work end-to-end. This issue implements the backend half. (Paired with the dnb-frontend config/UX issue.)Scope
web-push; introduceVAPID_PUBLIC_KEY,VAPID_PRIVATE_KEY,VAPID_SUBJECTenv vars (document in.env.example; generate withnpx web-push generate-vapid-keys). The public key must equal the frontendNEXT_PUBLIC_VAPID_PUBLIC_KEY.PushSubscriptioncollection keyed by user): storeendpoint,keys.{p256dh,auth}, optionaluserAgent,createdAt. One row per unique endpoint; upsert./api/users):POST /push-subscription— upsert the caller's subscription.DELETE /push-subscription— remove byendpoint.PATCH /push-subscription/preferences— update the user's push preference.src/controllers/notificationController.js/ the notification-send path), send a Web Push to each of the user's subscriptions viaweb-push, gated on thenotifications.pushpreference. On a404/410from the push service, prune that dead subscription.web-push), preference gating, and dead-subscription pruning.Out of scope
Acceptance criteria
web-pushadded; VAPID envs documented in.env.examplePOST/DELETE /api/users/push-subscriptionandPATCH /api/users/push-subscription/preferencesimplemented and authenticateddev; no unrelated changes