mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
fix(measurements-route,series,timeline): exclude soft-deleted in list reads
`GET /api/measurements` has five distinct read shapes (per-day drill-down, group-by-day, rollup-source coverage-fallback probe + fold re-read, live `date_trunc` aggregate, default findMany + count). Each relied on a shared `where` builder or a local `findMany` clause; none filtered `deletedAt`. The `GET /api/measurements/series` adapter for iOS read BP-SYS / BP-DIA / single-type series without the filter too. Pin `deletedAt: null` on the shared `where` so the count + raw findMany + group-by-day mode all inherit it; add the filter to the dayKey drill-down findMany, the coverage-fallback day-count probe, and the live `date_trunc` aggregate; thread the filter through the three series findMany calls. The `source=rollup` branch reads from `measurement_rollups` so the writer-side filter covers it. No `measurements/timeline` route exists yet — the file-set name was prospective.
This commit is contained in:
1 parent
1dd1a9a7a5
commit
97920f0f59
2 files changed
+17
-1
No files matched your search
@@ -64,6 +64,12 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
|
||||
const where = {
|
||||
userId: user.id,
|
||||
// v1.4.40 W-DELETED — soft-deleted readings are invisible to every
|
||||
// list / count / aggregate read. The dedicated `sync/state` route
|
||||
// keeps surfacing them via its own filter so iOS can reconcile
|
||||
// tombstones; every other consumer (list view, analytics fan-out,
|
||||
// dashboard read paths) must never include them.
|
||||
deletedAt: null,
|
||||
...(type && { type: type as MeasurementType }),
|
||||
...(from || to
|
||||
? {
|
||||
@@ -99,6 +105,7 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
userId: user.id,
|
||||
type: type as MeasurementType,
|
||||
measuredAt: { gte: dayStart, lt: dayEnd },
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: { measuredAt: sortDir },
|
||||
// v1.4.38 — the 1000-row cap now lives on the validator (refine
|
||||
@@ -347,6 +354,7 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
AND m."type" = ${type}::measurement_type
|
||||
AND m."measured_at" >= ${from}
|
||||
AND m."measured_at" <= ${to}
|
||||
AND m."deleted_at" IS NULL
|
||||
`;
|
||||
const liveDays = Number(liveDayCountRows[0]?.days ?? BigInt(0));
|
||||
if (liveDays > rollupRows.length) {
|
||||
@@ -484,6 +492,7 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
WHERE m."user_id" = ${user.id}
|
||||
AND m."measured_at" >= ${from}
|
||||
AND m."measured_at" <= ${to}
|
||||
AND m."deleted_at" IS NULL
|
||||
${type ? Prisma.sql`AND m."type" = ${type}::measurement_type` : Prisma.empty}
|
||||
GROUP BY m."type", bucket_start
|
||||
ORDER BY bucket_start ASC
|
||||
|
||||
@@ -84,6 +84,7 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
userId: user.id,
|
||||
type: "BLOOD_PRESSURE_SYS",
|
||||
measuredAt: { gte: since },
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: { measuredAt: "asc" },
|
||||
select: { id: true, value: true, measuredAt: true },
|
||||
@@ -93,6 +94,7 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
userId: user.id,
|
||||
type: "BLOOD_PRESSURE_DIA",
|
||||
measuredAt: { gte: since },
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: { measuredAt: "asc" },
|
||||
select: { id: true, value: true, measuredAt: true },
|
||||
@@ -120,7 +122,12 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
} else {
|
||||
const type = KIND_TO_TYPE[kind];
|
||||
const rows = await prisma.measurement.findMany({
|
||||
where: { userId: user.id, type, measuredAt: { gte: since } },
|
||||
where: {
|
||||
userId: user.id,
|
||||
type,
|
||||
measuredAt: { gte: since },
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: { measuredAt: "asc" },
|
||||
select: { id: true, value: true, measuredAt: true },
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user