120 lines
2.7 KiB
TypeScript

import { GetAccessToken } from "../../gcloud.js";
import { jsonError } from "../../common.js";
const allowedFileTypes = [
"image/gif",
"image/heic",
"image/heif",
"image/jpeg",
"image/png",
"video/mp4",
"video/webm",
];
export async function onRequestPost(context: RequestContext) {
if (
!context.data.current_user?.permissions ||
![1 << 0, 1 << 2, 1 << 11].find(
(p) => context.data.current_user.permissions & p,
)
)
return jsonError("Forbidden", 403);
if (
!context.request.headers
.get("content-type")
?.toLowerCase()
.startsWith("multipart/form-data;")
)
return jsonError("Invalid content type", 400);
let body: FormData;
try {
body = await context.request.formData();
} catch {
return jsonError("Invalid form data", 400);
}
if (
![
"verbal",
"warn",
"mute",
"ban_temp",
"ban_perm",
"ban_unappealable",
].includes(body.get("punishment") as string)
)
return jsonError("Invalid punishment", 400);
if (!(body.get("user") as string).match(/^\d{17,19}$/))
return jsonError("Invalid user", 400);
const fileKeys = Array.from(body.keys()).filter((key) =>
key.match(/^file\d$/),
);
if (fileKeys.length > 5)
return jsonError("Only up to five files are allowed per infraction", 400);
const files: File[] = [];
for (const fileKey of fileKeys) {
const file = body.get(fileKey);
if (!(file instanceof File))
return jsonError("File keys can only contain files", 400);
files.push(file);
}
const uploadPromises = [];
const origin = context.request.headers.get("Origin");
if (!origin) return jsonError("Origin header missing", 400);
const accessToken = await GetAccessToken(context.env);
for (const file of files) {
if (!allowedFileTypes.includes(file.type))
return jsonError(`File ${file.name} is not valid`, 415);
const attachmentKey = `${Date.now()}${Math.round(
Math.random() * 10000000,
).toString()}/${file.name}`;
uploadPromises.push(
fetch(
`https://storage.googleapis.com/upload/storage/v1/b/portal-carcrushers-cc/o?name=${encodeURIComponent(
attachmentKey,
)}&uploadType=media`,
{
headers: {
authorization: `Bearer ${accessToken}`,
"content-type": file.type,
},
},
),
);
}
await Promise.allSettled(uploadPromises);
const infractionId = `${body.get(
"user",
)}${Date.now()}${context.request.headers.get("cf-ray")?.split("-")[0]}`;
await context.env.DATA.put(
infractionId,
JSON.stringify({
created_at: Date.now(),
moderator: context.data.current_user.id,
}),
);
return new Response(null, {
status: 204,
});
}