From a3a5308d816276c3825734f1aa142d9abbb8d731 Mon Sep 17 00:00:00 2001 From: regalijan Date: Thu, 19 Oct 2023 16:50:11 -0400 Subject: [PATCH] Handle new report submission process --- app/routes/report.tsx | 122 +++++++++++++++++-------- functions/api/reports/submit.ts | 154 ++++++++++++++++++++++---------- 2 files changed, 190 insertions(+), 86 deletions(-) diff --git a/app/routes/report.tsx b/app/routes/report.tsx index 275c435..828bbd4 100644 --- a/app/routes/report.tsx +++ b/app/routes/report.tsx @@ -10,6 +10,7 @@ import { Input, Link, Text, + Textarea, useToast, } from "@chakra-ui/react"; import { useEffect, useState } from "react"; @@ -96,9 +97,8 @@ export default function () { ).value .replaceAll(" ", "") .split(","); - const file = ( - document.getElementById("evidence") as HTMLInputElement - ).files?.item(0); + const files = (document.getElementById("evidence") as HTMLInputElement) + .files; if (!usernames.length) return toast({ @@ -108,9 +108,9 @@ export default function () { title: "Error", }); - if (!file) + if (!files?.length) return toast({ - description: "Must attach a file", + description: "Must attach at least one file", isClosable: true, status: "error", title: "Error", @@ -124,10 +124,29 @@ export default function () { title: "Too Many Usernames", }); + if (!logged_in && !turnstileToken) + return toast({ + description: "Please complete the captcha and try again", + isClosable: true, + status: "error", + title: "Captcha not completed", + }); + + const description = ( + document.getElementById("description") as HTMLTextAreaElement + ).value; + + const filelist = []; + + for (const file of files) { + filelist.push({ name: file.name, size: file.size }); + } + const submitReq = await fetch("/api/reports/submit", { body: JSON.stringify({ - filename: file.name, - filesize: file.size, + description: description || undefined, + files: filelist, + turnstileResponse: logged_in ? undefined : turnstileToken, usernames, }), headers: { @@ -152,42 +171,62 @@ export default function () { }); } - const { id, upload_url }: { id: string; upload_url: string } = + const { id, upload_urls }: { id: string; upload_urls: string[] } = await submitReq.json(); - setUploading(true); - const reader = file.stream().getReader(); + const totalSize = filelist.reduce((a, b) => a + b.size, 0); let bytesRead = 0; + let shouldRecall = false; - const uploadReq = await fetch(upload_url, { - body: supportsRequestStreams - ? new ReadableStream({ - async pull(controller) { - const chunk = await reader.read(); - - if (chunk.done) { - controller.close(); - setUploading(false); - return; - } - - controller.enqueue(chunk.value); - bytesRead += chunk.value.length; - setFileProgress(Math.floor((bytesRead / file.size) * 100)); - }, - }) - : file, - // @ts-expect-error - duplex: supportsRequestStreams ? "half" : undefined, - headers: { - "content-type": - file.type || - fileTypes[file.name.split(".")[file.name.split(".").length - 1]], - }, - method: "PUT", - }).catch(console.error); + setUploading(true); + + for (let i = 0; i < upload_urls.length; i++) { + const reader = files[i].stream().getReader(); - if (!uploadReq?.ok) { + try { + const uploadReq = await fetch(upload_urls[i], { + body: supportsRequestStreams + ? new ReadableStream({ + async pull(controller) { + const chunk = await reader.read(); + + if (chunk.done) { + controller.close(); + + if (i === upload_urls.length - 1) setUploading(false); + + return; + } + + controller.enqueue(chunk.value); + bytesRead += chunk.value.length; + setFileProgress(Math.floor((bytesRead / totalSize) * 100)); + }, + }) + : files[i], + // @ts-expect-error + duplex: supportsRequestStreams ? "half" : undefined, + headers: { + "content-type": + files[i].type || + fileTypes[files[i].name.split(".").at(-1) as string], + }, + method: "PUT", + }); + + if (!uploadReq.ok) { + shouldRecall = true; + break; + } + } catch (e) { + console.error(e); + + shouldRecall = true; + break; + } + } + + if (shouldRecall) { await fetch("/api/reports/recall", { body: JSON.stringify({ id }), headers: { @@ -245,7 +284,7 @@ export default function () {
- Your Evidence (Max Size: 512MB) + Your Evidence (Max size per file: 512MB)