mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
First write endpoint for the W8d Workout + WorkoutRoute tables. Mirrors the v1.4.25 W10-hardened /api/measurements/batch in every cross-cutting concern so the iOS sync engine and the deferred Withings activity sync (W17b) can re-use the same retry / cursor plumbing: - requireAuth() — cookie + Bearer; narrow-scope-token safe per the W10 fix-C contract (a route that declares no scope admits any authenticated token, wildcard or otherwise). - withIdempotency — Idempotency-Key replays the cached envelope on retry (24h window). - checkRateLimit at 60 batches/min/user — 100 workouts/batch ceiling × 60/min = 6 000 workouts/min headroom, orders of magnitude past any healthy sync. - Content-Length pre-flight at 5 MB — anything larger returns 413 with a structured error code so the client falls back to one-workout-per-call. - Per-entry status (inserted | duplicate | skipped) so the iOS sync cursor can checkpoint accurately. Idempotency contract: - HTTP-level via Idempotency-Key. - Per-entry via @@unique([userId, source, externalId]) — Prisma createMany skipDuplicates absorbs duplicate-key conflicts. Race reconciliation mirrors the v1.4.25 W10 fix for measurements batch: under contention, createMany.count is the source of truth for how many rows THIS request wrote, and we downgrade enough per-entry inserted statuses to duplicate so the envelope sums stay consistent with the aggregate counts. The iOS sync cursor advances past both statuses identically. Two-step write: createMany the workouts, then look up the freshly written rows by composite key and createMany the routes by FK. The 1:1 WorkoutRoute.workoutId unique index means route inserts are also idempotent on re-submit.