From 41fadb20fef001212df3594de8332f0354887036 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Wed, 14 Jan 2026 20:59:57 +0200 Subject: [PATCH] Add error message to user if trying to upload too big image --- app/features/art/actions/art.new.server.ts | 4 +- .../img-upload/actions/upload.server.ts | 4 +- app/utils/remix.server.ts | 46 ++++++++++++++++++- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/app/features/art/actions/art.new.server.ts b/app/features/art/actions/art.new.server.ts index 91c5108a3..8754898e9 100644 --- a/app/features/art/actions/art.new.server.ts +++ b/app/features/art/actions/art.new.server.ts @@ -1,5 +1,4 @@ import type { FileUpload } from "@remix-run/form-data-parser"; -import { parseFormData as parseMultipartFormData } from "@remix-run/form-data-parser"; import { nanoid } from "nanoid"; import type { ActionFunction } from "react-router"; import { redirect } from "react-router"; @@ -14,6 +13,7 @@ import { errorToastIfFalsy, parseFormData, parseRequestPayload, + safeParseMultipartFormData, } from "~/utils/remix.server"; import { userArtPage } from "~/utils/urls"; import { NEW_ART_EXISTING_SEARCH_PARAM_KEY } from "../art-constants"; @@ -86,7 +86,7 @@ export const action: ActionFunction = async ({ request }) => { return null; }; - const formData = await parseMultipartFormData( + const formData = await safeParseMultipartFormData( request, // 5MB { maxFileSize: 5 * 1024 * 1024 }, diff --git a/app/features/img-upload/actions/upload.server.ts b/app/features/img-upload/actions/upload.server.ts index 6b970e564..878402af9 100644 --- a/app/features/img-upload/actions/upload.server.ts +++ b/app/features/img-upload/actions/upload.server.ts @@ -1,5 +1,4 @@ import type { FileUpload } from "@remix-run/form-data-parser"; -import { parseFormData } from "@remix-run/form-data-parser"; import type { ActionFunctionArgs } from "react-router"; import { redirect } from "react-router"; import { z } from "zod"; @@ -14,6 +13,7 @@ import { badRequestIfFalsy, errorToastIfFalsy, parseSearchParams, + safeParseMultipartFormData, } from "~/utils/remix.server"; import { teamPage, tournamentOrganizationPage } from "~/utils/urls"; import * as ImageRepository from "../ImageRepository.server"; @@ -57,7 +57,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { return null; }; - const formData = await parseFormData(request, uploadHandler); + const formData = await safeParseMultipartFormData(request, uploadHandler); const imgSrc = formData.get("img") as string | null; invariant(imgSrc); diff --git a/app/utils/remix.server.ts b/app/utils/remix.server.ts index 2f2fb63d3..3c064dfd1 100644 --- a/app/utils/remix.server.ts +++ b/app/utils/remix.server.ts @@ -291,6 +291,50 @@ export function privatelyCachedJson(dataValue: T) { }); } +const MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024; + +type FileUploadHandler = ( + fileUpload: FileUpload, +) => Promise; +type ParseFormDataOptions = { maxFileSize?: number }; + +export function safeParseMultipartFormData( + request: Request, + uploadHandler?: FileUploadHandler, +): Promise; +export function safeParseMultipartFormData( + request: Request, + options?: ParseFormDataOptions, + uploadHandler?: FileUploadHandler, +): Promise; +export async function safeParseMultipartFormData( + request: Request, + optionsOrHandler?: ParseFormDataOptions | FileUploadHandler, + uploadHandler?: FileUploadHandler, +): Promise { + try { + if (typeof optionsOrHandler === "function") { + return await parseMultipartFormData(request, optionsOrHandler); + } + return await parseMultipartFormData( + request, + optionsOrHandler, + uploadHandler, + ); + } catch (err) { + if ( + err instanceof Error && + err.cause instanceof Error && + err.cause.name === "MaxFileSizeExceededError" + ) { + throw errorToastRedirect( + `File size exceeds maximum allowed size of ${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB`, + ); + } + throw err; + } +} + export async function uploadImageIfSubmitted({ request, fileNamePrefix, @@ -318,7 +362,7 @@ export async function uploadImageIfSubmitted({ let formData: FormData; try { - formData = await parseMultipartFormData(request, uploadHandler); + formData = await safeParseMultipartFormData(request, uploadHandler); const imgSrc = formData.get("img") as string | null; if (!imgSrc) { throw new TypeError("No image submitted");