Merge branch 'fix/backup-rotation-and-deletion' into release/v1.39.3

# Conflicts:
#	docs/api/openapi.yaml
#	src/lib/jobs/apple-health-import-worker.ts
This commit is contained in:
Marc-André Bombeck committed 2026-09-26 21:48:20 +02:00
commit 4409529c89
74 files changed
+4022 -307

No files matched your search

+10
View File
@@ -8,6 +8,10 @@ import {
MFA_STEP_UP_MAX_AGE_SECONDS,
} from "@/lib/api-handler";
import { annotate } from "@/lib/logging/context";
import {
kickOffhostPurge,
requestOffhostPurge,
} from "@/lib/jobs/offhost-purge";
import {
deleteGuardianAccountWithLifecycle,
LastManagedGuardianError,
@@ -101,6 +105,10 @@ export const DELETE = apiHandler(async (request: NextRequest) => {
// genuinely complete erasure.
await tx.auditLog.deleteMany({ where: { userId } });
// The nightly off-host copies go with the account. The request commits
// with the deletion; the purge job deletes the objects.
await requestOffhostPurge(tx, userId, "account_deleted");
// Delete user — all other related data is removed via onDelete: Cascade.
await tx.user.delete({ where: { id: userId } });
});
@@ -115,6 +123,8 @@ export const DELETE = apiHandler(async (request: NextRequest) => {
throw error;
}
await kickOffhostPurge();
annotate({
action: { name: "settings.account.delete" },
meta: { username },
+10
View File
@@ -1,5 +1,9 @@
import { prisma } from "@/lib/db";
import { auditLog } from "@/lib/auth/audit";
import {
kickOffhostPurge,
requestOffhostPurge,
} from "@/lib/jobs/offhost-purge";
import { apiSuccess, apiError, getClientIp } from "@/lib/api-response";
import { NextRequest } from "next/server";
import {
@@ -101,6 +105,11 @@ export const DELETE = apiHandler(async (request: NextRequest) => {
// does not. Same classification contract as the model list.
await tx.user.update({ where: { id: userId }, data: USER_RESET });
// The nightly off-host copies of the record are part of "all your
// data". The request commits with the wipe; the purge job deletes
// the objects and removes it.
await requestOffhostPurge(tx, userId, "data_wiped");
return perModel;
},
{
@@ -144,6 +153,7 @@ export const DELETE = apiHandler(async (request: NextRequest) => {
// v1.16.9 — every cached payload was built on the rows just deleted;
// hard-evict the user's buckets so no surface serves pre-wipe data.
invalidateUserData(userId);
await kickOffhostPurge();
return apiSuccess({ cleared: true, deletedRows, models: counts });
});