# Web Push Notifications Setup Guide This guide explains how to implement and use **Web Push notifications** in the Dmail API. Web Push allows you to send notifications directly to users' browsers, even when your web app is not open. ## Overview The Web Push notification system sends real-time notifications to users' browsers when new emails arrive. It uses the standard **Web Push API** supported by all modern browsers and works with service workers. ## Architecture ``` Email Received → EmailMonitoringService → NotificationQueue → NewEmailHandler → PushNotificationHandler → Browser Push Service → User's Browser ``` ## Setup Instructions ### 1. Generate VAPID Keys VAPID (Voluntary Application Server Identification) keys are required for Web Push notifications: ```bash # Generate VAPID keys using Node.js node -e " const webpush = require('web-push'); const vapidKeys = webpush.generateVAPIDKeys(); console.log('Public Key:', vapidKeys.publicKey); console.log('Private Key:', vapidKeys.privateKey); " ``` ### 2. Configure Environment Variables Add these environment variables to your `.env` file: ```bash # Web Push Notifications Configuration PUSH_NOTIFICATIONS_ENABLED=true VAPID_PUBLIC_KEY=your_generated_public_key_here VAPID_PRIVATE_KEY=your_generated_private_key_here VAPID_SUBJECT=mailto:admin@yourdomain.com # Optional: Push notification settings PUSH_TTL=86400 PUSH_URGENCY=normal PUSH_RETRY_ATTEMPTS=3 PUSH_RETRY_DELAY=5000 # Frontend URL for notification actions FRONTEND_URL=https://mail.yourdomain.com ``` ### 3. Install Dependencies ```bash # Install web-push library npm install web-push # or pnpm add web-push ``` ### 4. Run Database Migration ```bash # Apply the database migration for web push notification fields psql -d your_database -f database/migrations/add_push_notifications_to_users.sql ``` ### 5. Update App Module Add the push notifications config to your `app.module.ts`: ```typescript import { pushNotificationsConfig } from "./configs/push-notifications.config"; @Module({ imports: [ ConfigModule.forRoot({ load: [ // ... other configs pushNotificationsConfig, ], }), // ... other modules ], }) export class AppModule {} ``` ## Frontend Implementation ### 1. Service Worker Setup Create `public/sw.js` in your frontend project: ```javascript // Service Worker for handling push notifications self.addEventListener("push", function (event) { if (event.data) { const data = event.data.json(); const options = { body: data.body, icon: data.icon || "/assets/email-icon.png", badge: data.badge || "/assets/email-badge.png", tag: data.tag || "email-notification", requireInteraction: data.requireInteraction || false, actions: data.actions || [], data: data.data || {}, }; event.waitUntil(self.registration.showNotification(data.title, options)); } }); // Handle notification clicks self.addEventListener("notificationclick", function (event) { event.notification.close(); if (event.action === "view") { // Open the email in the app event.waitUntil(clients.openWindow(event.notification.data.url || "/")); } else if (event.action === "dismiss") { // Just close the notification return; } else { // Default action - open the app event.waitUntil(clients.openWindow(event.notification.data.url || "/")); } }); ``` ### 2. Client-side JavaScript Add this to your main JavaScript file: ```javascript class WebPushManager { constructor(apiBaseUrl, getAuthToken) { this.apiBaseUrl = apiBaseUrl; this.getAuthToken = getAuthToken; this.vapidPublicKey = null; } // Initialize and register service worker async init() { if (!("serviceWorker" in navigator) || !("PushManager" in window)) { console.warn("Push messaging is not supported"); return false; } try { // Register service worker const registration = await navigator.serviceWorker.register("/sw.js"); console.log("Service Worker registered:", registration); // Get VAPID public key await this.getVAPIDKey(); return true; } catch (error) { console.error("Service Worker registration failed:", error); return false; } } // Get VAPID public key from server async getVAPIDKey() { try { const response = await fetch(`${this.apiBaseUrl}/users/push-notifications/vapid-key`, { headers: { Authorization: `Bearer ${this.getAuthToken()}`, }, }); if (response.ok) { const data = await response.json(); this.vapidPublicKey = data.vapidPublicKey; return this.vapidPublicKey; } } catch (error) { console.error("Failed to get VAPID key:", error); } return null; } // Request permission and subscribe to push notifications async subscribeToPush() { if (!this.vapidPublicKey) { await this.getVAPIDKey(); } if (!this.vapidPublicKey) { throw new Error("VAPID public key not available"); } // Request permission const permission = await Notification.requestPermission(); if (permission !== "granted") { throw new Error("Push notification permission denied"); } // Get service worker registration const registration = await navigator.serviceWorker.ready; try { // Subscribe to push notifications const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicKey), }); // Send subscription to server await this.sendSubscriptionToServer(subscription); console.log("Successfully subscribed to push notifications"); return subscription; } catch (error) { console.error("Failed to subscribe to push notifications:", error); throw error; } } // Send subscription to server async sendSubscriptionToServer(subscription) { const response = await fetch(`${this.apiBaseUrl}/users/push-subscriptions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.getAuthToken()}`, }, body: JSON.stringify({ subscription: subscription.toJSON(), deviceType: "web", deviceName: navigator.userAgent.slice(0, 50), }), }); if (!response.ok) { throw new Error("Failed to send subscription to server"); } return response.json(); } // Test push notification async testNotification() { const response = await fetch(`${this.apiBaseUrl}/users/push-notifications/test`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.getAuthToken()}`, }, body: JSON.stringify({ message: "Test notification from Dmail Web App", }), }); if (!response.ok) { throw new Error("Failed to send test notification"); } return response.json(); } // Check if user is subscribed async isSubscribed() { if (!("serviceWorker" in navigator) || !("PushManager" in window)) { return false; } const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.getSubscription(); return !!subscription; } // Unsubscribe from push notifications async unsubscribe() { const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.getSubscription(); if (subscription) { // Unsubscribe from browser await subscription.unsubscribe(); // Remove from server await fetch(`${this.apiBaseUrl}/users/push-subscriptions`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.getAuthToken()}`, }, body: JSON.stringify({ endpoint: subscription.endpoint, }), }); console.log("Successfully unsubscribed from push notifications"); } } // Utility function to convert VAPID key urlBase64ToUint8Array(base64String) { const padding = "=".repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/"); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } } // Usage example const pushManager = new WebPushManager("https://api.yourdomain.com", () => localStorage.getItem("authToken")); // Initialize on page load pushManager.init().then((success) => { if (success) { console.log("Web Push initialized successfully"); } }); // Export for use in your app window.webPushManager = pushManager; ``` ### 3. User Interface Integration Add UI components to your app: ```html ``` ## API Endpoints ### Push Subscription Management - `POST /users/push-subscriptions` - Register a new push subscription - `PATCH /users/push-subscriptions` - Update push subscriptions - `DELETE /users/push-subscriptions` - Remove a push subscription ### Push Notification Settings - `PATCH /users/push-notifications/settings` - Enable/disable push notifications - `GET /users/push-notifications/info` - Get push notification info and settings - `POST /users/push-notifications/test` - Send a test notification - `GET /users/push-notifications/vapid-key` - Get VAPID public key ## Usage Examples ### Register Push Subscription ```bash curl -X POST https://api.yourdomain.com/users/push-subscriptions \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "subscription": { "endpoint": "https://fcm.googleapis.com/fcm/send/...", "keys": { "p256dh": "BNbxxx...", "auth": "abc123..." } }, "deviceType": "web", "deviceName": "Chrome on MacBook Pro" }' ``` ### Test Push Notification ```bash curl -X POST https://api.yourdomain.com/users/push-notifications/test \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "message": "Test notification from Dmail API" }' ``` ### Get VAPID Public Key ```bash curl -X GET https://api.yourdomain.com/users/push-notifications/vapid-key \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ## How It Works 1. **Service Worker Registration**: Your web app registers a service worker that can receive push messages 2. **User Permission**: The user grants permission for notifications 3. **Subscription Creation**: The browser creates a push subscription with a unique endpoint 4. **Server Registration**: The subscription is sent to your API and stored in the database 5. **Email Detection**: When new emails arrive, the EmailMonitoringService detects them 6. **Notification Processing**: The NewEmailHandler triggers the PushNotificationHandler 7. **Push Message**: The server sends a push message to the browser's push service 8. **Notification Display**: The service worker receives the message and shows the notification ## Browser Support Web Push is supported by: - ✅ Chrome 50+ - ✅ Firefox 44+ - ✅ Safari 16.0+ - ✅ Edge 17+ - ✅ Opera 37+ ## Notification Flow ```mermaid sequenceDiagram participant U as User's Browser participant SW as Service Worker participant API as Dmail API participant PS as Push Service participant E as Email System U->>SW: Register Service Worker U->>API: Request VAPID key API->>U: Return VAPID public key U->>U: Request notification permission U->>PS: Create push subscription PS->>U: Return subscription object U->>API: Register subscription API->>API: Store subscription E->>API: New email detected API->>PS: Send push message PS->>SW: Deliver push message SW->>U: Show notification ``` ## Security Considerations 1. **VAPID Keys**: Keep your private VAPID key secure and never expose it to the client 2. **Subscription Validation**: Always validate subscription objects before storing them 3. **Rate Limiting**: Implement rate limiting to prevent notification spam 4. **Data Privacy**: Be mindful of what data you include in push notifications 5. **User Consent**: Always request explicit user permission before subscribing to push notifications ## Troubleshooting ### Common Issues 1. **Notifications not received**: - Check if the user has granted notification permission - Verify the service worker is registered correctly - Ensure VAPID keys are properly configured - Check browser console for errors 2. **VAPID key errors**: - Ensure VAPID keys are correctly generated and configured - Verify the VAPID subject is a valid mailto: or https: URL 3. **Service worker issues**: - Check if the service worker file is accessible - Verify the service worker scope includes your web app - Ensure HTTPS is used (required for service workers) ### Debug Commands ```bash # Test VAPID key configuration curl -X GET https://api.yourdomain.com/users/push-notifications/vapid-key \ -H "Authorization: Bearer YOUR_TOKEN" # Check user's notification settings curl -X GET https://api.yourdomain.com/users/push-notifications/info \ -H "Authorization: Bearer YOUR_TOKEN" # Send test notification curl -X POST https://api.yourdomain.com/users/push-notifications/test \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"message": "Test"}' ``` ## Monitoring and Analytics - **Subscription Management**: Track active subscriptions per user - **Delivery Success**: Monitor push notification delivery rates - **User Engagement**: Track notification click-through rates - **Error Handling**: Log and monitor push notification failures ## Performance Considerations - **Payload Size**: Keep push notification payloads small (max 4KB) - **Batch Processing**: Process multiple subscriptions efficiently - **TTL Settings**: Configure appropriate Time-To-Live for notifications - **Cleanup**: Regularly remove invalid/expired subscriptions ## Future Enhancements - **Rich Notifications**: Add images and action buttons - **Notification Categories**: Different notification types for different email categories - **User Preferences**: Granular control over notification timing and content - **Analytics Dashboard**: Web interface for monitoring push notification metrics - **A/B Testing**: Test different notification strategies and content ## Support For issues and questions: 1. Check browser developer console for errors 2. Verify service worker registration and subscription 3. Test VAPID key configuration 4. Review application logs for error messages