Files
t5de-hub/apps/web/app/console/blocks/actions.ts
T
dhkatzandClaude Opus 5.5 ba423f32d8
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
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]>
2026-11-05 14:32:08 -08:00

32 lines
1.2 KiB
TypeScript

"use server";
// File blocks, from the console.
import { revalidatePath } from "next/cache";
import { api, unwrap } from "@/lib/api";
import { attempt, field, type ActionState } from "@/lib/actions";
const done = (sha256: string) => {
revalidatePath("/console/blocks");
revalidatePath(`/console/blocks/${sha256}`);
revalidatePath("/console/products");
};
/** Blocks a file network-wide, removing the products using it. */
export async function blockFile(_: ActionState, form: FormData): Promise<ActionState> {
const sha256 = field(form, "sha256");
return attempt(async () => {
const { removed } = unwrap(await api.blocks({ sha256 }).put({ reason: field(form, "reason") }));
done(sha256);
return removed.length ? `Blocked. Removed ${removed.length === 1 ? "1 product" : `${removed.length} products`}.` : "Blocked.";
});
}
/** Unblocks a file. The products it removed stay removed. */
export async function unblockFile(_: ActionState, form: FormData): Promise<ActionState> {
const sha256 = field(form, "sha256");
return attempt(async () => {
unwrap(await api.blocks({ sha256 }).delete({ reason: field(form, "reason") }));
done(sha256);
return "Unblocked.";
});
}