feat(core-shared): grace-purge soft-deleted rows + boot registration
The retention purge job gated its whole body on activeRetention while every collection declares only postDeletion, and no app ever called registerRetentionPurgeJobs — retention was dead end to end (audit findings A2 + A3). The DSR soft delete now stamps a deletedAt tombstone on postDeletion collections (kept distinct from processingRestrictedAt so an Art. 18 restriction never feeds the purge), the job grace-purges tombstoned rows past postDeletion.duration with the declared action, core-cms injects the tombstone field + Payload task definitions, and bindAllProduction enqueues the first purge cycle at boot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
42
packages/core-shared/src/payload/retention-purge/task.ts
Normal file
42
packages/core-shared/src/payload/retention-purge/task.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { Payload } from "payload";
|
||||
import { PayloadJobQueue } from "../../jobs/payload-job-queue";
|
||||
import { buildPurgeHandler, type PayloadPurgeApi } from "./retention-purge.job";
|
||||
|
||||
/**
|
||||
* Minimal shape of a Payload job-task definition — enough for
|
||||
* `payload.config.ts` `jobs.tasks` composition without dragging the full
|
||||
* generated TaskConfig generics through core-shared.
|
||||
*/
|
||||
export type RetentionPurgeTask = {
|
||||
slug: string;
|
||||
handler: (args: { req: { payload: Payload } }) => Promise<{
|
||||
output: Record<string, never>;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the Payload job-task definition for one collection's retention purge
|
||||
* (audit finding A3): `registerRetentionPurgeJobs` enqueues
|
||||
* `retention-purge--<slug>` tasks at boot, and this definition is what makes
|
||||
* Payload able to RUN them. Everything the handler needs comes from the
|
||||
* running instance on `req.payload` (config, local API, job queue for the
|
||||
* self-re-enqueue), so the task can be declared at config-composition time
|
||||
* with no bootstrapping order problems.
|
||||
*/
|
||||
export function buildRetentionPurgeTask(
|
||||
collectionSlug: string,
|
||||
): RetentionPurgeTask {
|
||||
return {
|
||||
slug: `retention-purge--${collectionSlug}`,
|
||||
handler: async ({ req }) => {
|
||||
const payload = req.payload;
|
||||
const run = buildPurgeHandler(collectionSlug, {
|
||||
queue: new PayloadJobQueue(payload),
|
||||
config: payload.config,
|
||||
getPayload: async () => payload as unknown as PayloadPurgeApi,
|
||||
});
|
||||
await run();
|
||||
return { output: {} };
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user