Files
Kordant/tasks/shieldai-unified-restructure/14-notifications-router.md
2026-05-25 12:23:23 -04:00

103 lines
5.6 KiB
Markdown

# 14. Backend Router — Email, Push, and SMS Notifications
meta:
id: shieldai-unified-restructure-14
feature: shieldai-unified-restructure
priority: P1
depends_on: [shieldai-unified-restructure-11]
tags: [backend, notifications, email, push, sms, api]
objective:
- Build the tRPC router and service layer for sending notifications across all channels: email (Resend), push (FCM/APNs), and SMS (Twilio). Port logic from `packages/shared-notifications/` into a unified `notifications` router.
deliverables:
- `web/src/server/api/routers/notification.ts` — Notification router:
- `notification.sendEmail``adminProcedure` (or internal service use) sending transactional email
- `notification.sendPush``protectedProcedure` sending push to user's devices
- `notification.sendSMS``protectedProcedure` sending SMS
- `notification.registerDevice``protectedProcedure` registering FCM/APNs token
- `notification.unregisterDevice``protectedProcedure` removing device token
- `notification.listDevices``protectedProcedure` listing registered devices
- `notification.getPreferences``protectedProcedure` getting notification preferences
- `notification.updatePreferences``protectedProcedure` updating preferences
- `web/src/server/services/notification.service.ts` — Business logic:
- `sendEmail(to, subject, html, text?)` — via Resend API
- `sendPush(deviceTokens, title, body, data?)` — via FCM for Android, APNs for iOS
- `sendSMS(phoneNumber, message)` — via Twilio
- `registerDevice(userId, token, platform, deviceType)` — save to DB
- `unregisterDevice(userId, token)` — soft-delete or mark inactive
- `getPreferences(userId)` — read from DB
- `updatePreferences(userId, prefs)` — write to DB
- `web/src/server/services/email.templates.ts` — Email template renderer:
- Welcome email
- Alert notification email
- Password reset email
- Family invite email
- Billing receipt email
- Provider clients:
- `web/src/server/lib/resend.ts` — Resend client
- `web/src/server/lib/fcm.ts` — Firebase Admin SDK initialization
- `web/src/server/lib/twilio.ts` — Twilio client
steps:
1. Install dependencies in `web/`:
- `resend` (email)
- `firebase-admin` (push notifications)
- `twilio` (SMS)
2. Create provider client files:
- `resend.ts`: initialize with `RESEND_API_KEY`
- `fcm.ts`: initialize Firebase Admin with service account JSON
- `twilio.ts`: initialize with `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN`
3. Create `web/src/server/services/email.templates.ts`:
- Each template is a function returning `{ subject, html, text }`
- Use simple HTML with inline CSS (or a minimal template engine)
- Include ShieldAI branding (logo, colors)
4. Create `web/src/server/services/notification.service.ts`:
- `sendEmail`: call Resend, log result, handle errors
- `sendPush`: iterate device tokens, call FCM for Android tokens, APNs for iOS tokens (or use Firebase Admin which handles both)
- `sendSMS`: call Twilio Messages API, validate E.164 phone format
- `registerDevice`: upsert into `DeviceToken` table
- `unregisterDevice`: set `isActive = false`
- `getPreferences` / `updatePreferences`: read/write a `NotificationPreferences` table (add to schema if missing)
5. Create `web/src/server/api/routers/notification.ts`:
- Define Zod schemas for all inputs
- `sendEmail`: restricted to admin or internal service calls
- `sendPush`: accepts `title`, `body`, `data` payload; sends to all active user devices
- `sendSMS`: accepts `phoneNumber`, `message`
- Device registration/unregistration procedures
- Preference get/update procedures
6. Wire router into `web/src/server/api/root.ts`.
7. Write unit tests with mocked providers.
steps:
- Unit: `sendEmail` calls Resend with correct parameters
- Unit: `sendPush` iterates tokens and calls FCM
- Unit: `sendSMS` validates E.164 format before calling Twilio
- Unit: `registerDevice` creates DeviceToken record
- Unit: `unregisterDevice` marks token inactive
- Integration: `notification.registerDevice` persists token to DB
acceptance_criteria:
- [ ] Email can be sent via Resend with branded templates
- [ ] Push notifications can be sent to registered Android and iOS devices via FCM
- [ ] SMS can be sent via Twilio to valid E.164 numbers
- [ ] Device tokens can be registered and unregistered per user
- [ ] Notification preferences are persisted and respected (e.g., user can disable SMS alerts)
- [ ] All provider errors are caught and logged without crashing the app
- [ ] Email templates include ShieldAI branding
validation:
- Send a test email via Resend to your own address and verify receipt
- Register a test device token and send a push (use Firebase Console or API)
- Send a test SMS via Twilio (use test credentials for free)
- Run `cd web && pnpm test` for notification service tests
notes:
- Reference legacy: `packages/shared-notifications/src/`, `packages/api/src/routes/notifications.routes.ts`
- For FCM, Firebase Admin SDK requires a service account JSON. Store path in `FIREBASE_SERVICE_ACCOUNT_PATH` env var.
- For APNs, Firebase Admin can proxy to APNs if configured correctly. Alternatively, use `apn` npm package for direct APNs integration.
- Twilio test credentials (`TWILIO_TEST_*`) can be used for integration tests without sending real SMS.
- Resend has a generous free tier (100 emails/day) perfect for development.
- Consider adding a `notification.queue` table for reliable delivery (retry failed sends). This can be a follow-up task.
- The `NotificationPreferences` model may need to be added to the Drizzle schema if not already present. Add it as part of this task.