Files
zpan/migrations/0047_trash-via-trashedat.sql
T
Jasper Van 7b8c8c915e refactor(api)!: unify object upload + rework delete/trash lifecycle (#448) (#454)
Resolve #448 — one upload entry point and an AIP-164 soft delete.

Upload: POST /objects now returns size-decided upload instructions
{ sessionId, partSize, urls }; the server picks single PutObject (<=5 GiB)
vs 5 GiB-part multipart (>5 GiB) and rejects >5 TiB. The client PUTs each
slice, reads its ETag, then POSTs them to
POST /objects/{id}/uploads/{sid}/completions (returns the live object).
DELETE /objects/{id}/uploads/{sid} aborts and discards the draft.

Trash: matters.status drops 'trashed' (enum is {draft,active}); trash is
tracked by the existing trashedAt timestamp. DELETE /objects/{id} now
soft-deletes; the recycle bin lives under /trash/objects (list roots, get,
restorations, purge). Empty-trash is a frontend loop over roots.

BREAKING CHANGE:
- removes PUT /objects/{id}/status and POST /objects/{id}/uploads
- PUT .../uploads/{sid}/status -> POST .../uploads/{sid}/completions {parts}
- DELETE /objects/{id} flips hard-purge -> soft-delete; permanent purge
  moves to DELETE /trash/objects/{id}
- DELETE /trash removed; restore is POST /trash/objects/{id}/restorations
- matters.status enum loses 'trashed' (migration backfills to trashedAt)

The migration swaps the matters_active_name_uniq partial index to exclude
trashed rows (WHERE status='active' AND trashed_at IS NULL). The single-PUT
presign is header-free so the uniform slice uploader's raw PUT matches the
S3 signature. Go downloader client + agent reworked to the unified flow.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 21:21:43 -04:00

15 lines
832 B
SQL

-- Trash is now tracked by `trashed_at`, not the `status` value.
-- Backfill existing trashed rows to status='active' (their trashed_at is already
-- set by the old trash() path), so they surface under /trash/objects.
UPDATE `matters` SET `status` = 'active' WHERE `status` = 'trashed';
--> statement-breakpoint
-- Swap the partial unique index: trashed rows are now status='active', so the
-- "one active name per parent" constraint must also exclude trashed rows
-- (trashed_at IS NOT NULL) or a restored/recreated name would collide with a
-- trashed sibling. Replaces migration 0009's `WHERE status = 'active'`.
DROP INDEX `matters_active_name_uniq`;
--> statement-breakpoint
CREATE UNIQUE INDEX `matters_active_name_uniq`
ON `matters` (`org_id`, `parent`, LOWER(`name`))
WHERE `status` = 'active' AND `trashed_at` IS NULL;