feat: file blocks
Moderators block a file network-wide by its sha256, from a product's files or the console's new Blocks tab, which first shows the products using it. Blocking removes those products (their creators are told and may appeal), puts a `block` change in the changes feed of every catalog hosting it, and refuses uploads with it; moderators see who tried. Unblocking lets it in again; the products it removed stay removed until restored. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
7299e10b08
commit
ba423f32d8
18 files changed
+4074
-10
No files matched your search
@@ -93,6 +93,8 @@ Sections (each shown only to those with something in it):
|
||||
- **Rooms**: their hosted rooms.
|
||||
- **Cases**: reports, appeals and reviews about what they control.
|
||||
- **People**: moderators and admins.
|
||||
- **Blocks**: those who remove products hub-wide (`product:remove`): blocked files, a file's page (the products using it;
|
||||
block, unblock). Each file in a product's revisions links to its page for them.
|
||||
- **Audit**: scoped (below).
|
||||
- **Settings**: admins (registration, announcements, roles).
|
||||
|
||||
|
||||
+2
-1
@@ -478,7 +478,8 @@ Phases for products, catalogs and imports (products.md, console.md, imports.md):
|
||||
(to look into with the importer spike).
|
||||
3. Cases (reports, appeals, reviews), file blocks and the catalogs' changes feed; notifications; API tokens;
|
||||
two-factor sign-in for sensitive permissions. Cases and notifications **done** (2026-11-05), without email, canned
|
||||
reasons, moderator notes or second approval; file blocks, API tokens and two-factor sign-in next.
|
||||
reasons, moderator notes or second approval. File blocks **done** (2026-11-05); API tokens and two-factor sign-in
|
||||
next.
|
||||
4. Imports: grants, recorded grants, requests, unclaimed creators, the job queue, `t5de-importer` (after its spike).
|
||||
5. Charts and stats; mirroring between catalogs.
|
||||
|
||||
|
||||
+5
-2
@@ -71,7 +71,7 @@ registering on the hub), `WEB_ORIGIN` (the hub website, for CORS).
|
||||
| | |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/catalog/revisions` | `{uploadToken, files, rating ("general" or "adult"), parent (IMVU product ID or 0), name (from index.xml, for a new product), notes}` → 201 `{product, revision, draft}`. The upload token must be for this catalog. Without `product` in the token, a new product is made as a **draft** (owner: the token's person, name from `name`; the creator publishes it on the website). With it, a new revision of that product (the person must own it; `notes` required). A file list identical to the latest revision's is refused (409 `unchanged`). Files the hub blocks are refused (409 `blocked`). The catalog's upload policy (`anyone`, `members`, `team`) is checked when the upload token is made (403 `forbidden`). With `importToken` instead of `uploadToken`: an imported revision (imports.md); the hub checks the job and that a grant still covers it, makes the product (published, owned by the grant's creator) or adds the revision, and marks the job done. |
|
||||
| `GET /api/v1/catalog/changes?after=<cursor>` | `{changes, cursor}`: what the catalog must act on, oldest first: `{type: "delete", product, revision, files}` (withdrawn, removed by moderation, blocked: delete those files unless another hosted revision uses them), `{type: "mirror", product, revision, files, from: [catalog URLs]}` (fetch each file from one of `from`, check its sha256, then confirm below), `{type: "status", status}` (the catalog was suspended or reinstated). Start with no `after`; keep the cursor. |
|
||||
| `GET /api/v1/catalog/changes?after=<cursor>` | `{changes, cursor}`: what the catalog must act on, oldest first: `{type: "delete", product, revision, files}` (withdrawn, removed by moderation, blocked: delete those files unless another hosted revision uses them), `{type: "mirror", product, revision, files, from: [catalog URLs]}` (fetch each file from one of `from`, check its sha256, then confirm below), `{type: "status", status}` (the catalog was suspended or reinstated), `{type: "block", sha256}` (moderation blocked the file: delete it whatever uses it, and refuse it in uploads). Start with no `after`; keep the cursor. |
|
||||
| `POST /api/v1/catalog/hosting` | `{product, revision}`: the catalog now hosts a revision it was asked to mirror (it has every file) → 204 |
|
||||
| `POST /api/v1/catalog/stats` | optional: `{day, downloads, bytes}` for a past day |
|
||||
|
||||
@@ -121,7 +121,10 @@ catalog's). Only approved catalogs with imports on and not paused.
|
||||
| `POST /api/v1/products/:id/revisions/:n/retract` | (creator) |
|
||||
| `DELETE /api/v1/products/:id` | (creator) deletes it: no longer listed, and nobody new can get it; people who have it keep it (its page, files and library entries stay, and rooms keep showing it). Its creator can't edit it or add revisions after (until restored). 204 |
|
||||
| `POST /api/v1/products/:id/remove` | (`product:remove`) `{reason}`: removes it everywhere: its page and files answer 410 `removed`, it leaves libraries and rooms, and catalogs delete its files. `.../unremove` (`product:unremove`) `{reason?}` undoes it (for now removal only hides it, so its files and library entries come back). Its creator is told both. |
|
||||
| `PUT /api/v1/blocks/:sha256` | (`product:remove`) `{reason}`: blocks a file network-wide: revisions with it can't be registered, and products using it are removed. `DELETE` unblocks. |
|
||||
| `GET /api/v1/blocks?before=` | (`product:remove` hub-wide) `{blocks: [{sha256, reason, by, at}], before}`, newest first, 50 a page |
|
||||
| `GET /api/v1/blocks/:sha256` | (`product:remove`) `{block (or null), products}`: the products with a revision using the file (`{id, name, creatorId, creatorUsername, removed, revisions}`), what blocking it removes |
|
||||
| `PUT /api/v1/blocks/:sha256` | (`product:remove`) `{reason}` → `{removed: [{id, name}]}`: blocks a file network-wide: revisions with it can't be registered (409 `blocked`; the attempt is audited as `file.blocked_upload`, with the person and catalog), products using it are removed (each audited as `product.remove` with the `file`, so its creator is told and may appeal), and every catalog hosting it gets a `block` change. 409 `blocked` if it is already. |
|
||||
| `DELETE /api/v1/blocks/:sha256` | (`product:remove`) `{reason?}` → 204: unblocks it; the products it removed stay removed until restored (`.../unremove`). |
|
||||
| `GET /api/v1/library` | `{library: [{product, revision, stayOn}]}`: the person's products that are available to them, each with the revision to use (products no longer available, e.g. after leaving a catalog, stay in the table but aren't listed until they are again) |
|
||||
| `PUT /api/v1/library/:id` | get a product (add it): it must be published and available to the person (403 `members_only`, with the catalogs they could join) |
|
||||
| `DELETE /api/v1/library/:id` | remove it |
|
||||
|
||||
Reference in new issue
Block a user