import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { enforceRateLimit, limitWrite, requestIp } from "~/server/limits";
import { actOnAbuseReport, listAbuseReports, recordAbuseReport } from "~/server/abuse";
import { requireSiteAdmin } from "~/server/metering/access";
import { ABUSE_ACTIONS, ABUSE_CATEGORIES } from "~/lib/abuse";
const status = z.enum(["open", "actioned", "dismissed"]);
export const submitAbuseReport = createServerFn({ method: "POST" })
.middleware([viewerMiddleware])
.validator(
z.object({
url: z.url({ protocol: /^https?$/ }).max(2000),
category: z.enum(ABUSE_CATEGORIES),
details: z.string().max(4000),
email: z.union([z.literal(""), z.email().max(320)]),
}),
)
.handler(async ({ data, context }) => {
const ip = requestIp();
await enforceRateLimit("REPORT_LIMITER", `abuse-report:${ip}`);
await recordAbuseReport({
source: "web",
targetUrl: data.url,
category: data.category,
details: data.details,
reporterEmail: data.email || null,
reporterKey: context.viewer ? `user:${context.viewer.id}` : `ip:${ip}`,
reporterKind: context.viewer ? "user" : "ip",
});
return { ok: true };
});
export const getAbuseQueue = createServerFn({ method: "GET" })
.middleware([viewerMiddleware])
.validator(z.object({ status }))
.handler(async ({ data, context }) => {
requireSiteAdmin(requireViewer(context.viewer));