From cd8d61e64b4888e1e9910df376526dbb04c96e9a Mon Sep 17 00:00:00 2001 From: Luke Vella Date: Sun, 8 Sep 2024 23:28:49 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Use=20search=20param=20for=20obj?= =?UTF-8?q?ect=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/app/api/storage/route.ts | 57 +++++++++++++++++++ .../src/components/optimized-avatar-image.tsx | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/app/api/storage/route.ts diff --git a/apps/web/src/app/api/storage/route.ts b/apps/web/src/app/api/storage/route.ts new file mode 100644 index 000000000..77f674664 --- /dev/null +++ b/apps/web/src/app/api/storage/route.ts @@ -0,0 +1,57 @@ +import { GetObjectCommand } from "@aws-sdk/client-s3"; +import * as Sentry from "@sentry/nextjs"; +import { NextRequest, NextResponse } from "next/server"; + +import { env } from "@/env"; +import { getS3Client } from "@/utils/s3"; + +async function getAvatar(key: string) { + const s3Client = getS3Client(); + + if (!s3Client) { + throw new Error("S3 client not initialized"); + } + + const command = new GetObjectCommand({ + Bucket: env.S3_BUCKET_NAME, + Key: key, + }); + + const response = await s3Client.send(command); + + if (!response.Body) { + throw new Error("Object not found"); + } + + const arrayBuffer = await response.Body.transformToByteArray(); + const buffer = Buffer.from(arrayBuffer); + + return { + buffer, + contentType: response.ContentType || "application/octet-stream", + }; +} + +export async function GET(req: NextRequest) { + const imageKey = req.nextUrl.searchParams.get("key"); + + if (!imageKey) { + return new Response("No key provided", { status: 400 }); + } + + try { + const { buffer, contentType } = await getAvatar(imageKey); + return new NextResponse(buffer, { + headers: { + "Content-Type": contentType, + "Cache-Control": "public, max-age=3600", + }, + }); + } catch (error) { + Sentry.captureException(error); + return NextResponse.json( + { error: "Failed to fetch object" }, + { status: 500 }, + ); + } +} diff --git a/apps/web/src/components/optimized-avatar-image.tsx b/apps/web/src/components/optimized-avatar-image.tsx index 7d472f4a2..2324acb91 100644 --- a/apps/web/src/components/optimized-avatar-image.tsx +++ b/apps/web/src/components/optimized-avatar-image.tsx @@ -8,7 +8,7 @@ function getAvatarUrl(imageKey: string) { return imageKey; } - return `/api/storage/${imageKey}`; + return `/api/storage?key=${encodeURIComponent(imageKey)}`; } export const OptimizedAvatarImage = ({