feat(attendance): add notification delivery and NFC security foundation

This commit is contained in:
zouantchaw
2026-03-16 17:06:17 +01:00
parent 5d8240ed51
commit 73287f42bd
21 changed files with 1734 additions and 36 deletions

View File

@@ -60,6 +60,8 @@ The gateway keeps backend services separate internally, but frontend should trea
### Client writes
- `POST /client/devices/push-tokens`
- `DELETE /client/devices/push-tokens`
- `POST /client/orders/one-time`
- `POST /client/orders/recurring`
- `POST /client/orders/permanent`
@@ -113,6 +115,8 @@ The gateway keeps backend services separate internally, but frontend should trea
### Staff writes
- `POST /staff/profile/setup`
- `POST /staff/devices/push-tokens`
- `DELETE /staff/devices/push-tokens`
- `POST /staff/clock-in`
- `POST /staff/clock-out`
- `POST /staff/location-streams`
@@ -170,11 +174,40 @@ These are exposed as direct unified aliases even though they are backed by `core
- For `POST /staff/clock-in` and `POST /staff/clock-out`:
- send `nfcTagId` when clocking with NFC
- send `latitude`, `longitude`, and `accuracyMeters` when clocking with geolocation
- send `proofNonce` and `proofTimestamp` for attendance-proof logging; these are most important on NFC paths
- send `attestationProvider` and `attestationToken` only when the device has a real attestation result to forward
- send `overrideReason` only when the worker is bypassing a geofence failure and the shift/hub allows overrides
- `POST /staff/location-streams` is for the background tracking loop after a worker is already clocked in.
- `GET /client/coverage/incidents` is the review feed for geofence breaches, missing-location batches, and clock-in overrides.
- `POST /client/coverage/late-workers/:assignmentId/cancel` is the client-side recovery action when lateness is confirmed by incident evidence or elapsed grace time.
- Raw location stream payloads are stored in the private v2 bucket; SQL only stores the summary and incident index.
- Push delivery is backed by:
- SQL token registry in `device_push_tokens`
- durable queue in `notification_outbox`
- per-attempt delivery records in `notification_deliveries`
- Cloud Run job `krow-notification-dispatcher-v2`
### Push token request example
```json
{
"provider": "FCM",
"platform": "IOS",
"pushToken": "expo-or-fcm-device-token",
"deviceId": "iphone-15-pro-max",
"appVersion": "2.0.0",
"appBuild": "2000",
"locale": "en-US",
"timezone": "America/Los_Angeles"
}
```
Push-token delete requests may send `tokenId` or `pushToken` either:
- as JSON in the request body
- or as query params on the `DELETE` URL
Using query params is safer when the client stack or proxy is inconsistent about forwarding `DELETE` bodies.
### Clock-in request example
@@ -186,6 +219,8 @@ These are exposed as direct unified aliases even though they are backed by `core
"latitude": 37.4221,
"longitude": -122.0841,
"accuracyMeters": 12,
"proofNonce": "nonce-generated-on-device",
"proofTimestamp": "2026-03-16T09:00:00.000Z",
"overrideReason": "Parking garage entrance is outside the marked hub geofence",
"capturedAt": "2026-03-16T09:00:00.000Z"
}