feat: file blocks
Pull request / title (pull_request) Successful in 1s
CI / test (pull_request) Successful in 1m8s
CI / image (apps/api/Dockerfile, t5de-hub-api) (pull_request) Skipped
CI / image (apps/web/Dockerfile, t5de-hub-web) (pull_request) Skipped

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:
dhkatzandClaude Opus 5.5 committed 2026-11-05 14:32:08 -08:00
1 parent 7299e10b08
commit ba423f32d8
18 files changed
+4074 -10

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
CREATE TABLE "file_block" (
"sha256" text PRIMARY KEY NOT NULL,
"reason" text NOT NULL,
"by_id" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "file_block" ADD CONSTRAINT "file_block_by_id_user_id_fk" FOREIGN KEY ("by_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "product_revision_files" ON "product_revision" USING gin ("files");
File diff suppressed because it is too large. Load diff
+7
View File
@@ -92,6 +92,13 @@
"when": 1793888724185,
"tag": "0012_cases",
"breakpoints": true
},
{
"idx": 13,
"version": "7",
"when": 1793917069600,
"tag": "0013_file_blocks",
"breakpoints": true
}
]
}
+10 -1
View File
@@ -423,9 +423,18 @@ export const productRevision = pgTable(
retracted: boolean("retracted").notNull().default(false),
createdAt: createdAt(),
},
(t) => [primaryKey({ columns: [t.productId, t.number] })],
(t) => [primaryKey({ columns: [t.productId, t.number] }), index("product_revision_files").using("gin", t.files)],
);
// A file blocked network-wide by its sha256: revisions with it can't be registered, products using it are removed,
// and catalogs are told to delete it (docs/products.md).
export const fileBlock = pgTable("file_block", {
sha256: text("sha256").primaryKey(),
reason: text("reason").notNull(),
byId: text("by_id").references(() => user.id, { onDelete: "set null" }),
createdAt: createdAt(),
});
// Which catalogs host which revisions.
export const hosting = pgTable(
"hosting",