feat(backend): content packs foundation: tables, import pipeline, search, export, endpoints (#553) #557
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/553-content-packs-foundation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lane 1 of the content packs feature (#553), built to
docs/design/content-packs-spec.md. Backend only.Tables and migration.
content_packsandreference_entriesas specified: two scopes, per-pack licence and affirmation records, status, a generatedtsvectorwith weighted fields, the partial unique indexes, GIN and prefix indexes. Migration1c2d3e4f5a6bchained on0b1c2d3e4f5a, raw SQL, one head, applied end to end on a clean database; a test pins the head count and that the model's search expression matches the migration's byte for byte (the test harness usescreate_all, so drift would otherwise be invisible).Pipeline. Upload or fetch by URL (30 s, 50 MiB streamed, three redirects, every hop refused if it resolves to a private or loopback address, content-type checked); the affirmation enforced server-side with the text returned in the 422 and the version, hash, user and time recorded on the pack; format detection through a registry with a fixed detection order; the native adapter (
.qbpack, a bare JSON array, CSV); sanitisation (nh3 then markdownify), caps on summary, body and record count; stats validated against the system's schema with warnings rather than rejections; slug de-duplication; one transaction; audit rows; packs over 2 MiB through a Celery task with status polling.Search. Name-prefix first, then weighted full-text rank, campaign packs above instance packs for the same kind and slug (ranked per group so a differently titled campaign copy still wins), GM-only records only for the campaign's GM, disabled packs excluded.
Endpoints as specified for members, admins (who may act on any scope) and GMs, one router;
startersdeclared before the id route. Export writes a native zip and withholds non-redistributable records with reasons.Docs:
docs/CONTENT-PACKS.md(what a pack is, the affirmation, the three native shapes with examples, export),docs/API.md, one CHANGELOG entry naming the migration.Deviations, stated in the code:
system.jsonin a pack creates a game system but never updates an existing one (a schema is shared by every campaign; an uploaded file should not redefine it); pack assets are recorded but not stored in phase 1; a GM searching without naming a campaign sees members-only records.Verification: 2,634 passed, 13 skipped (118 new across 7 files); ruff check and format clean.
Adapter lanes (Foundry; 5etools and orcbrew; Open5e and starter scripts) follow as separate PRs that register with this registry.
🤖 Generated with Claude Code