From a88412fd620a50772cc5fed7da5511ed813b672b Mon Sep 17 00:00:00 2001 From: JerryVincent Date: Wed, 29 Oct 2025 16:36:48 +0100 Subject: [PATCH] Fixed the image upload problem and moved the resource routes from the resources+ folder as the react router doesn't support this. --- app/routes/resources+/file.$fileId.tsx | 22 -------------- app/routes/resources.file.$fileId.tsx | 29 +++++++++++++++++++ ...urces.measurement.$deviceId.$sensorId.tsx} | 0 ...r-avatar.tsx => resources.user-avatar.tsx} | 0 app/routes/settings.profile.photo.tsx | 28 ++++++++++-------- app/routes/settings.profile.tsx | 2 +- 6 files changed, 46 insertions(+), 35 deletions(-) delete mode 100644 app/routes/resources+/file.$fileId.tsx create mode 100644 app/routes/resources.file.$fileId.tsx rename app/routes/{resources+/measurement.$deviceId.$sensorId.tsx => resources.measurement.$deviceId.$sensorId.tsx} (100%) rename app/routes/{resources+/user-avatar.tsx => resources.user-avatar.tsx} (100%) diff --git a/app/routes/resources+/file.$fileId.tsx b/app/routes/resources+/file.$fileId.tsx deleted file mode 100644 index d5c80b92..00000000 --- a/app/routes/resources+/file.$fileId.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { type LoaderFunctionArgs } from "react-router"; -import invariant from "tiny-invariant"; -import { drizzleClient } from "~/db.server"; - -export async function loader({ params }: LoaderFunctionArgs) { - invariant(params.fileId, 'File ID is required'); - - const image = await drizzleClient.query.profileImage.findFirst({ - where: (profileImage, { eq }) => eq(profileImage.id, params.fileId as string) - }) - - if (!image) throw new Response("Not found", { status: 404 }); - - return new Response(image.blob, { - headers: { - "Content-Type": image.contentType, - "Content-Length": Buffer.byteLength(image.blob).toString(), - "Content-Disposition": `inline; filename="${params.fileId}"`, - "Cache-Control": "public, max-age=31536000, immutable", - }, - }); -} diff --git a/app/routes/resources.file.$fileId.tsx b/app/routes/resources.file.$fileId.tsx new file mode 100644 index 00000000..50374adb --- /dev/null +++ b/app/routes/resources.file.$fileId.tsx @@ -0,0 +1,29 @@ +import { type LoaderFunctionArgs } from 'react-router' +import invariant from 'tiny-invariant' +import { drizzleClient } from '~/db.server' + +export async function loader({ params }: LoaderFunctionArgs) { + invariant(params.fileId, 'File ID is required') + + // Keep it as a string - your ID column is PgText, not an integer + const image = await drizzleClient.query.profileImage.findFirst({ + where: (profileImage, { eq }) => + eq(profileImage.id, params.fileId as string), + }) + + if (!image || !image.blob) { + throw new Response('Not found', { status: 404 }) + } + + // Convert Buffer to Uint8Array + const body = new Uint8Array(image.blob) + + return new Response(body, { + headers: { + 'Content-Type': image.contentType || 'image/jpeg', + 'Content-Length': body.byteLength.toString(), + 'Content-Disposition': `inline; filename="profile-${params.fileId}"`, + 'Cache-Control': 'public, max-age=31536000, immutable', + }, + }) +} diff --git a/app/routes/resources+/measurement.$deviceId.$sensorId.tsx b/app/routes/resources.measurement.$deviceId.$sensorId.tsx similarity index 100% rename from app/routes/resources+/measurement.$deviceId.$sensorId.tsx rename to app/routes/resources.measurement.$deviceId.$sensorId.tsx diff --git a/app/routes/resources+/user-avatar.tsx b/app/routes/resources.user-avatar.tsx similarity index 100% rename from app/routes/resources+/user-avatar.tsx rename to app/routes/resources.user-avatar.tsx diff --git a/app/routes/settings.profile.photo.tsx b/app/routes/settings.profile.photo.tsx index aa326e5a..4919e905 100644 --- a/app/routes/settings.profile.photo.tsx +++ b/app/routes/settings.profile.photo.tsx @@ -93,23 +93,27 @@ export async function action({ request }: ActionFunctionArgs) { // Query user profile const previousProfileWithImage = await drizzleClient.query.profile.findFirst({ where: (profile, { eq }) => eq(profile.userId, userId), - with: { - profileImage: true, - }, + with: { profileImage: true }, }); - // Insert new profile image - await drizzleClient.insert(profileImage).values({ - blob: Buffer.from(await photoFile.arrayBuffer()), - contentType: photoFile.type, - profileId: previousProfileWithImage?.id, - }); + // Store the old image ID before inserting new one + const oldImageId = previousProfileWithImage?.profileImage?.id; + + // Insert new profile image and get the new ID back + const [newImage] = await drizzleClient + .insert(profileImage) + .values({ + blob: Buffer.from(await photoFile.arrayBuffer()), + contentType: photoFile.type, + profileId: previousProfileWithImage?.id, + }) + .returning(); - // If an old profile image exists, delete it - if (previousProfileWithImage?.profileImage?.id) { + // Delete the OLD image (not the new one) + if (oldImageId && oldImageId !== newImage.id) { await drizzleClient .delete(profileImage) - .where(eq(profileImage.id, previousProfileWithImage.profileImage.id)); + .where(eq(profileImage.id, oldImageId)); } return redirect("/settings/profile"); diff --git a/app/routes/settings.profile.tsx b/app/routes/settings.profile.tsx index b60d0096..7d996278 100644 --- a/app/routes/settings.profile.tsx +++ b/app/routes/settings.profile.tsx @@ -176,7 +176,7 @@ export default function EditUserProfilePage() {