Hallo Stroke

Notifications

Web Push (VAPID) subscriptions and delivery.

The backend sends web push notifications (VAPID) to browsers for new articles, videos, forum replies, and reminders.

Subscribe

POST /api/push/subscribe — body:

{
  "endpoint": "https://fcm.googleapis.com/...",
  "keys": { "p256dh": "...", "auth": "..." },
  "user_id": 42
}

Stored in push_subscriptions (endpoint unique, user_id nullable for anonymous). The frontend subscribes on HomeLoggedIn mount via push/registerPush.js (guarded by a push_registered localStorage flag).

Send

POST /api/push/send — body:

{
  "user_id": 42,
  "payload": { "title": "...", "body": "...", "url": "/docs/..." }
}

Uses SherClockHolmes/webpush-go with the VAPID keys from env (VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT).

When notifications fire

TriggerChannel
Reminder schedule matchWhatsApp + web push
Forum thread reply (not self-reply)WhatsApp to thread creator
New forum threadWhatsApp broadcast (preference-filtered)
New articleWhatsApp broadcast (randomized delay)
New videoWhatsApp broadcast (randomized delay)

All notification dispatches run in goroutines so the triggering HTTP response is not blocked.

Service workers unregistered on frontend

The frontend unregisters service workers on app load (src/index.js) — this is cache-clearing behavior, not a PWA. Web Push still works because the PushManager subscription persists independently of the service worker lifecycle, but this is fragile and worth revisiting.

Notification service wiring

Cross-module notification is wired via setter injection in cmd/main.go:

  • forumService.SetNotificationService(whatsappService)
  • artikelService.SetArticleNotificationService(whatsappService)
  • pengingatService.SetPushNotificationService(pushNotificationService)

The contracts live in internal/shared/interface.go (NotificationService, PengingatNotificationService, ArticleNotificationService, YouTubeNotificationService), with NoOp* implementations for tests/default wiring.

On this page