# VAPID Web Push Notifications - Implementation Guide ## Overview This project now has **full VAPID Web Push notifications** support for real native notifications, even when the browser is closed. Users will receive notifications like WhatsApp when new orders arrive. ## Features ✅ **Real Native Notifications** - Shows in system notification center (iOS/Android/Desktop) ✅ **Works When Browser Closed** - Notifications delivered even if browser is completely closed ✅ **No External Services Required** - Uses browser vendors' push services (Google FCM, Mozilla, Apple APNs) ✅ **SQLite Database** - Lightweight database for storing push subscriptions ✅ **Role-Based Notifications** - Only users with access to orders receive notifications ✅ **Auto Cleanup** - Removes expired subscriptions automatically ## Architecture ### Client-Side (Frontend) - **Service Worker** (`/public/sw-notifications.js`) - Handles incoming push notifications - **Composable** (`/composables/usePushNotifications.ts`) - Manages subscriptions and permissions - **UI Component** (`/pages/sales/orders/index.vue`) - Bell icon to enable notifications ### Server-Side (Backend) - **SQLite Database** (`/data/push-subscriptions.db`) - Stores user subscriptions - **Database Utility** (`/server/utils/pushDb.ts`) - CRUD operations for subscriptions - **Push Notifier** (`/server/utils/pushNotifier.ts`) - Sends push notifications via web-push - **Subscribe API** (`/server/api/push/subscribe.post.ts`) - Endpoint to save subscriptions - **SSE Integration** (`/server/api/orders/sales/stream.get.ts`) - Sends push when new orders arrive ## Configuration ### 1. Environment Variables (.env) ```env # VAPID keys for Web Push Notifications VAPID_PUBLIC_KEY=BDIb3shmxlwVCfkxrnP4nB6VusVm-O8ocEVGD0AH64gy--8LLq8V_eB4Cb6pDO0xEpXd17aEncCtRd45hfs2Y6s VAPID_PRIVATE_KEY=HRDyDYhgFbPbUIFFwbsuayli3-WFViwIwtYyifYjJ90 VAPID_SUBJECT=mailto:support@logship.de ``` **IMPORTANT:** These keys are already generated. Keep `VAPID_PRIVATE_KEY` secret! ### 2. Dependencies ```json { "better-sqlite3": "^12.4.1", // SQLite database (Node 20+ required) "web-push": "^3.6.7" // Web Push protocol implementation } ``` ### 3. Database - **Location:** `/data/push-subscriptions.db` - **Type:** SQLite (file-based, no separate service needed) - **Auto-created:** Database and tables created automatically on first run - **Backup:** Just backup the `.db` file #### Database Schema ```sql CREATE TABLE push_subscriptions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, role_id INTEGER NOT NULL, endpoint TEXT UNIQUE NOT NULL, p256dh TEXT NOT NULL, auth TEXT NOT NULL, user_agent TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_used DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` ## How It Works ### 1. User Enables Notifications ``` User taps bell icon → Browser requests permission → User grants permission → Service Worker subscribes to push → Subscription saved to SQLite database ``` ### 2. New Order Arrives ``` New order in iDempiere → SSE polling detects it → Server determines which roles have access → Server sends push to all subscribed users with those roles → Browser's push service delivers notification → Notification appears in system tray ``` ### 3. User Clicks Notification ``` User clicks notification → Service Worker opens/focuses browser tab to /sales/orders ``` ## Browser Support | Browser | Desktop | Mobile | Notes | |---------|---------|--------|-------| | Chrome | ✅ | ✅ | Full support, works great | | Edge | ✅ | ✅ | Full support (Chromium-based) | | Firefox | ✅ | ✅ | Full support | | Safari | ✅ | ⚠️ | iOS: Requires PWA (add to home screen) | | Opera | ✅ | ✅ | Full support (Chromium-based) | ## Testing ### On Production Server (Node 20) 1. **Deploy to production:** ```bash git add . git commit -m "Add VAPID Web Push notifications" git push origin dev ``` 2. **Install dependencies on server:** ```bash npm install # better-sqlite3 will compile natively on Node 20 ``` 3. **Test on mobile device:** - Open https://app.logship.de/sales/orders - Tap gray bell icon - Grant notification permission - Close browser completely - Create a test order in iDempiere - Notification should appear in system notification center! ### Testing Locally (Node 18) **Note:** better-sqlite3 won't compile on Node 18. Options: 1. Test on production server with Node 20 2. Upgrade local Node to 20+: `nvm install 20 && nvm use 20` ## API Endpoints ### POST /api/push/subscribe Save a push subscription for the current user. **Request:** ```json { "subscription": { "endpoint": "https://fcm.googleapis.com/fcm/send/...", "keys": { "p256dh": "...", "auth": "..." } } } ``` **Response:** ```json { "success": true, "message": "Push subscription saved successfully" } ``` ## Database Utilities ```typescript // Save subscription savePushSubscription(userId, roleId, subscription, userAgent) // Get subscriptions by user getPushSubscriptionsByUser(userId) // Get subscriptions by role getPushSubscriptionsByRole(roleId) // Get subscriptions by multiple roles getPushSubscriptionsByRoles([roleId1, roleId2]) // Delete subscription deletePushSubscription(endpoint) // Cleanup old subscriptions (90+ days) cleanupOldSubscriptions() // Get statistics getPushSubscriptionStats() ``` ## Push Notification Utilities ```typescript // Send push to specific roles await sendPushToRoles([102, 103], { title: '🔔 New Order', body: 'You have a new order', icon: '/favicon.ico', url: '/sales/orders' }) // Send new order notification (used by SSE) await sendNewOrderNotification([102], orderCount, orderDetails) ``` ## Maintenance ### View Database ```bash # On production server sqlite3 data/push-subscriptions.db # Query all subscriptions SELECT * FROM push_subscriptions; # Count by role SELECT role_id, COUNT(*) FROM push_subscriptions GROUP BY role_id; ``` ### Cleanup Old Subscriptions The system automatically removes expired subscriptions when push delivery fails (HTTP 410). You can also manually cleanup: ```typescript import { cleanupOldSubscriptions } from './server/utils/pushDb' cleanupOldSubscriptions() // Removes subscriptions older than 90 days ``` ## Security - ✅ **VAPID Keys** - Private key is secret, never sent to client - ✅ **User Authentication** - Subscriptions tied to authenticated users via JWT - ✅ **Role-Based** - Users only receive notifications for orders they have access to - ✅ **HTTPS Required** - Service Workers only work on HTTPS domains ## Troubleshooting ### Notifications Not Appearing 1. **Check browser console** for errors starting with `[Push]` or `[SW]` 2. **Check server logs** for `[WebPush]` messages 3. **Verify VAPID keys** are in `.env` and loaded in runtime config 4. **Check notification permission** - must be "granted" 5. **Verify subscription** saved to database: ```bash sqlite3 data/push-subscriptions.db "SELECT * FROM push_subscriptions WHERE user_id = YOUR_USER_ID" ``` ### Database Errors ```bash # Check if database file exists ls -la data/push-subscriptions.db # Check permissions chmod 644 data/push-subscriptions.db # Recreate database (will delete all subscriptions!) rm data/push-subscriptions.db # Restart server - will recreate automatically ``` ### Service Worker Issues ```javascript // In browser console: navigator.serviceWorker.getRegistration().then(reg => console.log(reg)) // Unregister and re-register: navigator.serviceWorker.getRegistration().then(reg => reg.unregister()) // Refresh page ``` ## Future Enhancements - [ ] Add notification preferences (email, push, both) - [ ] Allow users to mute notifications for X hours - [ ] Add notification for other events (shipments, returns, etc.) - [ ] Add notification history/logs - [ ] Support notification grouping (collapse multiple orders into one) ## Resources - [Web Push Protocol](https://web.dev/push-notifications-web-push-protocol/) - [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) - [VAPID Spec](https://datatracker.ietf.org/doc/html/rfc8292) - [better-sqlite3 Docs](https://github.com/WiseLibs/better-sqlite3) - [web-push Library](https://github.com/web-push-libs/web-push) ## Support For issues or questions: - Email: support@logship.de - Check server logs: `/var/log/nuxt-app.log` - Check browser console for client-side errors