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, }); }