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
@@ -0,0 +1,88 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { PageHeader } from "@/components/status";
|
||||
import { DateTime } from "@/components/time";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { api, unwrap } from "@/lib/api";
|
||||
import { ConsoleNav, requireConsole } from "../console-nav";
|
||||
|
||||
export const metadata: Metadata = { title: "Blocks" };
|
||||
|
||||
export default async function BlocksPage({ searchParams }: { searchParams: Promise<Record<string, string | string[]>> }) {
|
||||
const { console: c } = await requireConsole("/console/blocks");
|
||||
if (!c.sections.includes("blocks")) notFound();
|
||||
const params = await searchParams;
|
||||
// Looking a file up goes to its page.
|
||||
if (typeof params.sha256 === "string") {
|
||||
const sha256 = params.sha256.trim().toLowerCase();
|
||||
if (/^[0-9a-f]{64}$/.test(sha256)) redirect(`/console/blocks/${sha256}`);
|
||||
}
|
||||
const before = typeof params.before === "string" && !Number.isNaN(Date.parse(params.before)) ? params.before : undefined;
|
||||
const data = unwrap(await api.blocks.get({ query: before ? { before } : {} }));
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title="Blocks">Blocked files: uploads with them are refused, and catalogs delete them.</PageHeader>
|
||||
<ConsoleNav />
|
||||
<form action="/console/blocks" className="mb-6 flex flex-wrap gap-2">
|
||||
<Input
|
||||
name="sha256"
|
||||
required
|
||||
pattern="\s*[0-9a-fA-F]{64}\s*"
|
||||
defaultValue={typeof params.sha256 === "string" ? params.sha256 : ""}
|
||||
placeholder="A file's sha256"
|
||||
aria-label="sha256"
|
||||
className="max-w-xl font-mono"
|
||||
/>
|
||||
<Button type="submit">Look up</Button>
|
||||
</form>
|
||||
<Card>
|
||||
<CardContent>
|
||||
{data.blocks.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nothing blocked. Block a file from a product's files, or look it up above.
|
||||
</p>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>File</TableHead>
|
||||
<TableHead>Reason</TableHead>
|
||||
<TableHead>By</TableHead>
|
||||
<TableHead>When</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.blocks.map((b) => (
|
||||
<TableRow key={b.sha256}>
|
||||
<TableCell className="font-mono text-xs">
|
||||
<Link href={`/console/blocks/${b.sha256}`} className="underline-offset-4 hover:underline">
|
||||
{b.sha256.slice(0, 16)}…
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell className="whitespace-normal">{b.reason}</TableCell>
|
||||
<TableCell className="text-muted-foreground">{b.by ?? ""}</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
<DateTime iso={b.at} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
{data.before && (
|
||||
<div className="mt-4">
|
||||
<Link href={`/console/blocks?before=${encodeURIComponent(data.before)}`} className={buttonVariants({ variant: "outline" })}>
|
||||
Older
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user