120 lines
2.7 KiB
TypeScript
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,
|
|
});
|
|
}
|