chore: document project-wide soft delete pattern and add deleted_by column to all tables

This commit is contained in:
2026-06-11 20:36:47 +00:00
parent d0e2075e6d
commit 9a93debfc9
37 changed files with 1690 additions and 266 deletions
+5
View File
@@ -66,6 +66,7 @@ devplacepy/
| `/profile` | Profile view, editing, and a public **Media** tab (`?tab=media`) showing every attachment a user uploaded, newest first |
| `/media` | Per-attachment soft delete and restore: `POST /media/{uid}/delete` (owner or admin), `POST /media/{uid}/restore` (admin) |
| `/uploads` | File upload endpoints: `POST /uploads/upload` (multipart), `POST /uploads/upload-url` (from URL); served at `/static/uploads/` |
| `/admin/trash` | Admin **Trash**: review, restore, and permanently purge soft-deleted content (posts, comments, gists, projects, news, project files, attachments) across the platform |
| `/notifications` | Notification list, mark read, live unread counts (`/notifications/counts`) |
| `/messages` | Direct messaging |
| `/votes` | Upvote/downvote on posts, comments, projects |
@@ -622,6 +623,10 @@ All indexes are created via `CREATE INDEX IF NOT EXISTS` wrapped in try/except -
SQLite is synchronous by design and will never be made async. It is more than fast enough for this platform: the database is a local file with WAL, a 30s busy timeout, and a 256MB memory map, so queries are sub-millisecond. The synchronous database layer is a deliberate architectural decision, not a limitation, and is not subject to change.
### Soft delete and data retention
Removing a record is a **soft delete**, not a physical one: it stamps `deleted_at` (timestamp) and `deleted_by` (actor) instead of erasing the row, and every list/count read filters `deleted_at IS NULL`, so the item disappears from the product while remaining recoverable. This covers user content (posts, comments, gists, projects, news, project files, attachments), engagement toggles (votes, reactions, bookmarks, follows, poll votes - which revive the same row when re-toggled), sessions (logout), container instances, and per-owner Devii/customization data. Deletions cascade with a shared timestamp so the whole event restores or purges as a unit. Garbage-collection operations (job retention, metrics ring buffers, usage-ledger pruning, expired-session cleanup) stay hard deletes - that is the stage that frees storage. Administrators review, restore, and permanently purge soft-deleted content from **Trash** at `/admin/trash`; the columns are indexed (`idx_<table>_deleted`).
## Testing
- **849+ tests** across 63 files: Playwright integration + in-process unit tests