feat(push): native device-token registration + FCM/APNs senders

- /api/v1/devices (register) + /api/v1/devices/remove — auth-required, validates
  platform (ios|android), upserts by token; e2e covers register/validation/auth/remove.
- db device_tokens table + deviceTokens repo.
- push.js: FCM HTTP v1 (Android) and APNs token-based over HTTP/2 (iOS) folded into
  the single push.sendToUser path alongside Web Push; each transport independently
  config-gated and a silent no-op without creds. Dead tokens pruned on 404/410.
- docs: CLIENTS.md Phase B updated; DEPLOY.md env table adds FCM/APNs vars.

e2e 117/117.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 18:23:10 +05:30
parent 593a4677b6
commit 4c75db2029
7 changed files with 183 additions and 32 deletions
+9 -3
View File
@@ -53,9 +53,15 @@ absolute API base if offline-launch or store policy requires it.
(e.g. hide the PWA "install" prompt, enable native push instead of Web Push).
### Phase B — Native capabilities
- [ ] **Push:** server device-token registration (`/api/v1/devices`) + a pluggable
`push` sender (FCM/APNs), config-gated. Mobile uses the Capacitor push plugin;
desktop keeps Web Push / in-app. *Needs Apple/Google creds to test end-to-end.*
- [x] **Push backend:** device-token registration (`POST /api/v1/devices`,
`/api/v1/devices/remove`) + native senders folded into the single `push.sendToUser`
path — **FCM v1** (Android) and **APNs** token-based over HTTP/2 (iOS), each
config-gated and a no-op until creds are set. Web Push (VAPID) unchanged. Dead
tokens are pruned on 404/410/UNREGISTERED. (db `device_tokens`, repo `deviceTokens`,
`push.js`.) *Mobile app still needs the Capacitor push plugin wired + FCM/APNs creds
to deliver end-to-end.*
- [ ] **Wire the Capacitor push plugin** in the mobile app → register the token via
`POST /api/v1/devices` on launch; handle taps (deep link to the chat/session).
- [ ] **Mobile screen capture** for "Share Screen" from a phone (ReplayKit / MediaProjection plugin).
- [ ] **Deep links / universal links** so a session/meeting link opens the app.