Implement database-backed notification settings that persist across restarts: Backend changes: - Updated DashboardService to read/write notification settings from database - Added 6 notification settings keys to dbSettingsMap: * enable_email_notifications (boolean) * enable_push_notifications (boolean) * admin_notifications (boolean) * error_alerts (boolean) * new_user_alerts (boolean) * system_health_alerts (boolean) - Settings are now retrieved from database with fallback defaults Database: - Seeded 6 default notification settings in settings table - All notification toggles default to 'true' - Settings persist across server restarts Frontend: - Admin settings page at /settings already configured - Notifications tab contains all 6 toggle switches - Settings are loaded from GET /api/v1/admin/dashboard/settings - Settings are saved via POST /api/v1/admin/dashboard/settings API Endpoints (already existed, now enhanced): - GET /api/v1/admin/dashboard/settings - Returns all settings including notifications - POST /api/v1/admin/dashboard/settings - Persists notification settings to database Files modified: - maternal-app-backend/src/modules/admin/dashboard/dashboard.service.ts Benefits: ✅ Global notification settings are now persistent ✅ Admin can control email/push notifications globally ✅ Admin can configure alert preferences ✅ Settings survive server restarts and deployments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
Push Notifications Implementation Summary
Status: ✅ COMPLETED (Backend + Frontend Integration Ready) Date: October 8, 2025 Implementation Type: Web Push (VAPID) - No Firebase/OneSignal dependency
🎯 Overview
We successfully implemented a streamlined, fully local Web Push notification system for ParentFlow using the Web Push Protocol with VAPID keys. This allows browser-based push notifications without relying on third-party services like Firebase or OneSignal.
📦 Backend Implementation
1. Database Schema ✅
Table: push_subscriptions (Already exists in production)
CREATE TABLE push_subscriptions (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id varchar(20) NOT NULL,
endpoint text NOT NULL UNIQUE,
p256dh text NOT NULL,
auth text NOT NULL,
user_agent text,
device_type varchar(20),
browser varchar(50),
is_active boolean DEFAULT true,
last_error text,
failed_attempts integer DEFAULT 0,
last_success_at timestamp,
created_at timestamp DEFAULT now(),
updated_at timestamp DEFAULT now()
);
Indexes:
idx_push_subs_user_idonuser_ididx_push_subs_activeonis_active(WHERE is_active = true)unique_endpointonendpoint
2. TypeORM Entity ✅
File: src/database/entities/push-subscription.entity.ts
Features:
- Relationship with User entity (CASCADE delete)
- Tracks device type, browser, and subscription health
- Automatic timestamps (created_at, updated_at)
3. Push Service ✅
File: src/modules/push/push.service.ts
Key Features:
- VAPID configuration from environment variables
- Subscribe/unsubscribe management
- Send push notifications to individual users or groups
- Automatic error handling (404/410 = deactivate, retries for 5xx)
- User agent parsing for device/browser detection
- Statistics and cleanup utilities
Main Methods:
- getPublicVapidKey(): string
- subscribe(userId, subscriptionData, userAgent): PushSubscription
- unsubscribe(userId, endpoint): void
- sendToUser(userId, payload): {sent, failed}
- sendToUsers(userIds[], payload): {sent, failed}
- sendTestNotification(userId): void
- cleanupInactiveSubscriptions(daysOld): number
- getStatistics(userId?): Statistics
4. Push Controller ✅
File: src/modules/push/push.controller.ts
REST API Endpoints:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/push/vapid-public-key |
Get VAPID public key for frontend |
| POST | /api/v1/push/subscriptions |
Subscribe to push notifications |
| GET | /api/v1/push/subscriptions |
Get user's active subscriptions |
| DELETE | /api/v1/push/subscriptions?endpoint=... |
Unsubscribe from push |
| POST | /api/v1/push/test |
Send test notification |
| GET | /api/v1/push/statistics |
Get push statistics |
Authentication: All endpoints require JWT authentication (JwtAuthGuard)
5. Push Module ✅
File: src/modules/push/push.module.ts
Wired into main AppModule and exports PushService for use by other modules.
6. Notifications Integration ✅
Updated: src/modules/notifications/notifications.service.ts
Features:
- Automatic push notification when creating notifications
- Intelligent URL routing based on notification type
- Smart notifications (feeding, sleep, diaper reminders) now trigger push
- Medication reminders trigger push
- Anomaly detection triggers push
Integration Flow:
createNotification()
→ Save to DB
→ sendPushNotification()
→ PushService.sendToUser()
→ markAsSent/markAsFailed
7. Environment Configuration ✅
File: maternal-app-backend/.env
# Push Notifications (Web Push - VAPID)
PUSH_NOTIFICATIONS_ENABLED=true
VAPID_PUBLIC_KEY=BErlB-L0pDfv1q3W0SHs3ZXqyFi869OScpt5wJ2aNu2KKbLxLj4a-YO6SyuAamjRG_cqY65yt2agyXdMdy2wEXI
VAPID_PRIVATE_KEY=Rg47clL1z4wSpsBTx4yIOIHHX9qh1W5TyBZwBfPIesk
VAPID_SUBJECT=mailto:hello@parentflow.com
PUSH_DEFAULT_TTL=86400
PUSH_BATCH_SIZE=100
Security: Keep VAPID_PRIVATE_KEY secret. Never expose in logs or client-side code.
🌐 Frontend Implementation
1. Service Worker ✅
File: maternal-web/public/push-sw.js
Features:
- Listens for push events
- Shows notifications with custom icons, badges, and data
- Handles notification clicks (focus existing window or open new)
- Tracks notification dismissals
- Test notification support
Event Handlers:
push- Receive and display notificationsnotificationclick- Handle user clicks, navigate to URLsnotificationclose- Track dismissalsmessage- Handle messages from the app
2. Push Utilities ✅
File: maternal-web/lib/push-notifications.ts
Utility Functions:
- isPushNotificationSupported(): boolean
- getNotificationPermission(): NotificationPermission
- requestNotificationPermission(): Promise<NotificationPermission>
- getVapidPublicKey(token): Promise<string>
- registerPushServiceWorker(): Promise<ServiceWorkerRegistration>
- subscribeToPush(token): Promise<PushSubscription>
- savePushSubscription(subscription, token): Promise<void>
- getPushSubscription(): Promise<PushSubscription | null>
- unsubscribeFromPush(token): Promise<void>
- isPushSubscribed(): Promise<boolean>
- sendTestPushNotification(token): Promise<void>
- getPushStatistics(token): Promise<any>
- showLocalTestNotification(): Promise<void>
Key Features:
- Browser compatibility checks
- VAPID key base64 conversion
- Service worker registration
- Subscription management
- Backend API integration
3. UI Component ✅
File: maternal-web/components/PushNotificationToggle.tsx
Features:
- Toggle switch to enable/disable push notifications
- Permission status display
- Error handling and user feedback
- Test notification button
- Loading states
- Dark mode support
- Responsive design
Component States:
- Unsupported browser warning
- Permission denied message
- Subscribed confirmation with test button
- Loading indicator
Usage:
import PushNotificationToggle from '@/components/PushNotificationToggle';
// In settings page
<PushNotificationToggle />
🔧 Testing Guide
Backend Testing
- Get VAPID Public Key:
curl http://localhost:3020/api/v1/push/vapid-public-key
- Subscribe (requires auth token):
curl -X POST http://localhost:3020/api/v1/push/subscriptions \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "https://fcm.googleapis.com/fcm/send/...",
"keys": {
"p256dh": "...",
"auth": "..."
}
}'
- Send Test Notification:
curl -X POST http://localhost:3020/api/v1/push/test \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
- Get Statistics:
curl http://localhost:3020/api/v1/push/statistics \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Frontend Testing
-
Open Web App: Navigate to
http://maternal.noru1.ro -
Go to Settings: Find the Push Notification Toggle component
-
Enable Notifications:
- Click the toggle switch
- Grant permission when prompted
- Wait for confirmation
-
Test Notification:
- Click "Send Test Notification" button
- Check browser notifications
-
Test Full Flow:
- Create a feeding/sleep/diaper activity
- Wait for reminder notification (based on patterns)
- Check notification appears
Browser Compatibility Testing
Test on:
- ✅ Chrome (Desktop & Mobile)
- ✅ Firefox (Desktop & Mobile)
- ✅ Edge (Desktop)
- ✅ Safari (iOS 16.4+ PWA only - must be installed to home screen)
📊 Notification Flow
1. User Action (e.g., feeding activity)
↓
2. NotificationsService detects pattern
↓
3. createNotification() called
↓
4. Notification saved to DB
↓
5. sendPushNotification() triggered
↓
6. PushService.sendToUser() sends to all user's devices
↓
7. Web Push sends to browser
↓
8. Service Worker receives push event
↓
9. Service Worker shows notification
↓
10. User clicks notification
↓
11. Service Worker navigates to URL
🔒 Security Considerations
VAPID Keys
- ✅ Private key stored in
.env(never committed to git) - ✅ Public key safe to expose to frontend
- ✅ Subject configured as
mailto:contact email
Authentication
- ✅ All push endpoints require JWT authentication
- ✅ Users can only manage their own subscriptions
- ✅ Endpoint validation prevents injection attacks
Data Privacy
- ✅ Subscription endpoints hashed in logs
- ✅ User agent data stored for analytics only
- ✅ Inactive subscriptions auto-cleaned after 90 days
- ✅ Cascade delete when user is deleted
Rate Limiting
- ⚠️ TODO: Add rate limiting to push endpoints
- Recommended: 10 requests/minute per user for subscribe/unsubscribe
- Recommended: 100 notifications/day per user
📈 Monitoring & Maintenance
Database Cleanup
Run periodic cleanup (recommended: daily cron job):
-- Delete inactive subscriptions older than 90 days
DELETE FROM push_subscriptions
WHERE is_active = false
AND updated_at < NOW() - INTERVAL '90 days';
Or use the service method:
await pushService.cleanupInactiveSubscriptions(90);
Statistics Monitoring
const stats = await pushService.getStatistics();
// Returns:
// {
// totalSubscriptions: 150,
// activeSubscriptions: 142,
// inactiveSubscriptions: 8,
// byBrowser: { chrome: 100, firefox: 30, safari: 12 },
// byDeviceType: { mobile: 90, desktop: 50, tablet: 10 }
// }
Error Monitoring
Check push subscription errors:
SELECT user_id, endpoint, last_error, failed_attempts
FROM push_subscriptions
WHERE is_active = false
AND last_error IS NOT NULL
ORDER BY updated_at DESC
LIMIT 20;
🚀 Deployment Checklist
Production Deployment
- Generate production VAPID keys (
npx web-push generate-vapid-keys) - Add VAPID keys to production
.env - Set
VAPID_SUBJECTto production email (mailto:support@parentflow.com) - Enable HTTPS (required for Web Push)
- Test on all major browsers
- Set up monitoring for failed push deliveries
- Configure rate limiting
- Set up cleanup cron job
- Test notification appearance on mobile devices
- Verify service worker registration on production domain
Environment Variables (Production)
PUSH_NOTIFICATIONS_ENABLED=true
VAPID_PUBLIC_KEY=<production-public-key>
VAPID_PRIVATE_KEY=<production-private-key>
VAPID_SUBJECT=mailto:support@parentflow.com
PUSH_DEFAULT_TTL=86400
PUSH_BATCH_SIZE=100
🎨 Customization Guide
Notification Appearance
Edit in maternal-web/public/push-sw.js:
const options = {
body: data.body,
icon: '/icons/icon-192x192.png', // Change app icon
badge: '/icons/icon-72x72.png', // Change badge icon
vibrate: [200, 100, 200], // Customize vibration pattern
tag: data.tag || 'default',
data: data.data || {},
requireInteraction: false, // Set true for persistent notifications
};
Notification URLs
Edit URL routing in notifications.service.ts:
private getNotificationUrl(notification: Notification): string {
switch (notification.type) {
case NotificationType.FEEDING_REMINDER:
return `/children/${notification.childId}/activities`;
// Add custom routes here
}
}
Notification Triggers
Add custom notification triggers in notifications.service.ts:
async createCustomNotification(userId: string, childId: string) {
await this.createNotification(
userId,
NotificationType.CUSTOM,
'Custom Title',
'Custom Message',
{
childId,
priority: NotificationPriority.HIGH,
metadata: { customData: 'value' }
}
);
}
🐛 Troubleshooting
Common Issues
Issue: "Push notifications not supported" Solution: Ensure HTTPS is enabled. Service Workers require secure context.
Issue: "Permission denied" Solution: User must manually reset permissions in browser settings.
Issue: "Subscription failed" Solution: Check VAPID public key is correctly fetched from backend.
Issue: "Notifications not appearing" Solution: Check browser notification settings, ensure service worker is registered.
Issue: "Push fails with 404/410" Solution: Subscription is invalid/expired. System auto-deactivates these.
Issue: "iOS not receiving notifications" Solution: On iOS, app must be installed as PWA (Add to Home Screen).
Debug Logs
Browser Console:
// Check service worker registration
navigator.serviceWorker.getRegistrations().then(console.log);
// Check current subscription
navigator.serviceWorker.ready.then(reg =>
reg.pushManager.getSubscription().then(console.log)
);
// Check notification permission
console.log(Notification.permission);
Backend Logs:
# Check push service logs
tail -f /tmp/backend-dev.log | grep "\[Push\]"
📚 References
✅ Completion Status
Backend ✅
- Database schema (already existed)
- PushSubscription entity
- PushService implementation
- PushController with REST endpoints
- PushModule integration
- NotificationsService integration
- Environment configuration
- VAPID keys generated
Frontend ✅
- Service Worker (push-sw.js)
- Push utilities library
- PushNotificationToggle component
- Browser compatibility checks
- Error handling
Testing 🔄
- Backend compilation successful
- Backend running on port 3020
- End-to-end push notification test
- Multi-device testing
- Browser compatibility testing
🎉 Next Steps
-
Test End-to-End Flow:
- Log in to web app
- Enable push notifications in settings
- Send test notification
- Create activities and verify smart notifications
-
Production Deployment:
- Generate production VAPID keys
- Update environment variables
- Deploy to production
- Test on production domain
-
Monitoring Setup:
- Set up error tracking for failed push sends
- Configure cleanup cron job
- Set up analytics for notification engagement
-
Documentation:
- Add push notification docs to user guide
- Create admin documentation for monitoring
- Update API documentation
Implementation Complete! 🚀 The push notification system is ready for testing and deployment.