Files
car-crushers-portal/functions/api/rtbf.ts
T
regalijan 4451c192d2
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 1m32s
Test, Build, Deploy / Create Sentry Release (push) Successful in 8s
Correct response code + user id property
2026-07-02 00:38:37 -04:00

96 lines
2.4 KiB
TypeScript

import { jsonError } from "../common.js";
import { Report } from "../../generated/prisma/client.js";
import { getBanList, setBanList } from "../roblox-open-cloud.js";
export async function onRequestPost(context: RequestContext) {
const body = await context.request.text();
const secret = context.env.ROBLOX_WEBHOOK_SECRET;
if (!secret) return jsonError("This endpoint is currently disabled", 503);
const sigHeader = context.request.headers.get("roblox-signature");
if (!sigHeader) return jsonError("Missing signature", 401);
const [timestamp, sig] = sigHeader.split(",");
if (
parseInt(timestamp.replace("t=", "")) <
Math.floor(Date.now() / 1000) - 600
)
return jsonError("This request is stale", 406);
const textEncode = (text: string) => new TextEncoder().encode(text);
const key = await crypto.subtle.importKey(
"raw",
textEncode(secret),
{
hash: "SHA-256",
name: "HMAC",
},
false,
["verify"],
);
if (
!(await crypto.subtle.verify(
"HMAC",
key,
Uint8Array.from(atob(sig.replace("v1=", "")), (c) => c.charCodeAt(0)),
textEncode(`${timestamp.replace("t=", "")}.${body}`),
))
)
return jsonError("Invalid signature", 401);
const data = JSON.parse(body);
if (data.EventType === "SampleNotification")
return new Response(null, {
status: 204,
});
if (data.EventType !== "RightToErasureRequest")
return jsonError("Invalid event type", 400);
const { prisma } = context.data;
const userId = data.EventPayload.UserId;
if (typeof userId !== "number") return jsonError("Invalid user id", 400);
await prisma.gameModLog.deleteMany({
where: {
target: userId,
},
});
const associatedReports = await prisma.$queryRaw<
Report[]
>`SELECT attachments, id FROM reports WHERE EXISTS (SELECT 1 FROM json_each(target_ids) WHERE VALUE = ${userId});`;
const idsToDelete: string[] = [];
for (const report of associatedReports) {
for (const attachment of report.attachments as string[]) {
await context.env.R2.delete(attachment);
}
idsToDelete.push(report.id);
}
await prisma.report.deleteMany({
where: {
id: {
in: idsToDelete,
},
},
});
const { etag, value } = await getBanList(context);
delete value[userId];
await setBanList(context, value, etag);
return new Response(null, {
status: 204,
});
}