From a62b94983b57a443bc41e5c15612ff615d3a4aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 21 May 2026 17:12:31 +0200 Subject: [PATCH] fix(export): exclude soft-deleted measurements in export bundle reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v1.4.41 W-DELETED-2 — full-backup JSON, per-type CSV, and the legacy /api/export endpoint now scope reads to deletedAt: null so iOS undo or pending-sync rows never reach the user's downloaded file or the admin restore round-trip. --- src/app/api/export/full-backup/route.ts | 5 ++++- src/app/api/export/measurements/route.ts | 16 ++++++++++++++-- src/app/api/export/route.ts | 5 ++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/app/api/export/full-backup/route.ts b/src/app/api/export/full-backup/route.ts index 5cf342878..469192933 100644 --- a/src/app/api/export/full-backup/route.ts +++ b/src/app/api/export/full-backup/route.ts @@ -36,7 +36,10 @@ export const GET = apiHandler(async (request: NextRequest) => { const [measurements, medications, intakeEvents, moodEntries] = await Promise.all([ prisma.measurement.findMany({ - where: { userId: user.id }, + // v1.4.41 W-DELETED-2 — soft-deleted rows are excluded from the + // user-initiated backup bundle so the round-trip via admin + // restore does not resurrect deleted measurements. + where: { userId: user.id, deletedAt: null }, orderBy: { measuredAt: "desc" }, }), // Explicit `select` on the schedule columns — see the matching diff --git a/src/app/api/export/measurements/route.ts b/src/app/api/export/measurements/route.ts index 3eb5e4355..cd3fa45c9 100644 --- a/src/app/api/export/measurements/route.ts +++ b/src/app/api/export/measurements/route.ts @@ -89,9 +89,21 @@ function safeDate(value: string): Date | undefined { function buildWhere( userId: string, range: { since?: Date; until?: Date }, -): { userId: string; measuredAt?: { gte?: Date; lte?: Date } } { - const where: { userId: string; measuredAt?: { gte?: Date; lte?: Date } } = { +): { + userId: string; + deletedAt: null; + measuredAt?: { gte?: Date; lte?: Date }; +} { + // v1.4.41 W-DELETED-2 — soft-deleted measurements stay out of every + // per-type CSV download so an undo or pending-sync row never reaches + // the user's local file. + const where: { + userId: string; + deletedAt: null; + measuredAt?: { gte?: Date; lte?: Date }; + } = { userId, + deletedAt: null, }; if (range.since || range.until) { where.measuredAt = {}; diff --git a/src/app/api/export/route.ts b/src/app/api/export/route.ts index e1f678583..e688a44e3 100644 --- a/src/app/api/export/route.ts +++ b/src/app/api/export/route.ts @@ -55,7 +55,10 @@ export const GET = apiHandler(async (request: NextRequest) => { if (type === "measurements" || type === "all") { const measurements = await prisma.measurement.findMany({ - where: { userId }, + // v1.4.41 W-DELETED-2 — exclude soft-deleted measurements from + // the legacy /api/export endpoint so CSV + JSON downloads stay + // consistent with the live measurement reads. + where: { userId, deletedAt: null }, orderBy: { measuredAt: "desc" }, }); data.measurements =