mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
R-E C-2 server-side. Three additive columns + three new endpoints
let the iOS SyncMode store pair, drain, and reconcile against a
fresh paired-server.
Schema (migration 0062, additive):
- Measurement.sync_version (Int, default 1) for last-writer-wins
- Measurement.deleted_at (Timestamp, nullable) for soft-deletes
- User.last_synced_at (Timestamp, nullable) for the handshake
checkpoint
Endpoints:
- GET /api/sync/state — handshake response (lastSyncedAt, server
clock, live + tombstoned measurement counters); call also bumps
the checkpoint so iOS reads the OLD value then trusts subsequent
writes
- POST /api/mood-entries/bulk — UPSERT per entry keyed by
(userId, date, moodLoggedAt); probe-then-upsert so the response
reliably reports inserted vs duplicate; rate-limited at 60/min
- POST /api/medications/intake/bulk — INSERT per entry with
P2002-on-idempotencyKey → duplicate; up-front user-owns-medication
check skips foreign rows with a documented reason
iOS contract: every change is additive. Existing per-sample POSTs
keep round-tripping (sync_version defaults to 1 server-side;
deleted_at stays NULL on every existing read path; last_synced_at is
opt-in). The new endpoints are net-new — no iOS consumer yet.
Tests cover the handshake checkpoint advance, live-vs-tombstoned
counters, clean batch ingest, re-post upsert semantics, ownership
filtering, idempotency-key collision, and over-cap rejection — all
against the real-Postgres container.
32 lines
1.5 KiB
SQL
32 lines
1.5 KiB
SQL
-- v1.4.30 — iOS SyncMode foundation (R-E C-2)
|
|
--
|
|
-- Three additive columns the iOS app reads when it pairs with the
|
|
-- server:
|
|
--
|
|
-- measurements.sync_version — monotonic version counter per row.
|
|
-- Default = 1 for legacy rows; the
|
|
-- server bumps it on every update so
|
|
-- paired clients can do last-writer-
|
|
-- wins reconciliation.
|
|
-- measurements.deleted_at — soft-delete timestamp. The
|
|
-- by-external-ids DELETE path flips
|
|
-- this rather than removing rows so
|
|
-- paired iOS clients can reconcile
|
|
-- deletions without losing the audit
|
|
-- trail.
|
|
-- users.last_synced_at — last-known synchronization checkpoint
|
|
-- the iOS SyncMode store reads via
|
|
-- `GET /api/sync/state`.
|
|
--
|
|
-- All three are nullable / defaulted so existing rows + existing iOS
|
|
-- POSTs round-trip unchanged. The `IF NOT EXISTS` guards keep the
|
|
-- migration idempotent against partial historical runs (same posture
|
|
-- as 0058_user_research_mode and 0061_audit_log_carrier).
|
|
|
|
ALTER TABLE "measurements"
|
|
ADD COLUMN IF NOT EXISTS "sync_version" INTEGER NOT NULL DEFAULT 1,
|
|
ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMP(3);
|
|
|
|
ALTER TABLE "users"
|
|
ADD COLUMN IF NOT EXISTS "last_synced_at" TIMESTAMP(3);
|