In-App Reminders
- Notification bell in the sidebar shows count badge
- Computes reminders from visits, treatments, and vaccinations with dates within the next 24 hours
- Two reminder windows:
day_before: 24 hours before the event
hour_before: 1 hour before the event
- Dismiss individual reminders (stored in
notification_dismissal table)
- Polls every 5 minutes for new reminders
- Notification panel opens with list of upcoming events and dismiss buttons
Push Notifications
- Web Push via VAPID protocol (pywebpush library)
- VAPID keys auto-generated on first backend startup, saved to
.vapid_keys.json
- Push subscription management endpoints: subscribe, unsubscribe, list
- APScheduler runs every 15 minutes with PostgreSQL-backed reliability:
- Jobs persist in
apscheduler_jobs table (SQLAlchemyJobStore) — survive restarts
- Multi-worker safe: PostgreSQL advisory locks ensure exactly-one execution per cycle
- Missed jobs after restart fire if within grace window (15 min), skip if expired
- Loads all push subscriptions grouped by user
- Calculates reminders (same logic as in-app)
- Checks
push_sent_log table for deduplication (+ IntegrityError safety net)
- Sends push via pywebpush with payload: title, body, tag, URL
- Logs sent notifications to prevent re-sending
- Scheduler abstraction: all APScheduler imports confined to
app/infrastructure/scheduler.py. Business logic has zero scheduler dependencies, enabling future migration (e.g., Celery) by changing only the scheduler module
- Auto-cleanup: expired subscriptions (HTTP 410) are automatically deleted
- Notification click opens the relevant page (visit/treatment/vaccination detail)
Push Payload
{
"title": "Reminder title",
"body": "Нагадування за 1 день" or "Нагадування за 1 годину",
"tag": "entity_type-entity_id-reminder_type",
"url": "/path/to/entity"
}