diff --git a/app/media/[id]/route.ts b/app/media/[id]/route.ts index a588082..32b1d6e 100644 --- a/app/media/[id]/route.ts +++ b/app/media/[id]/route.ts @@ -22,7 +22,7 @@ async function serveMedia( return new Response("Not found", { status: 404 }); } - const file = getMediaStorage().file(record.objectKey); + const file = (await getMediaStorage()).file(record.objectKey); let stat: Awaited>; try { stat = await file.stat(); diff --git a/lib/media/repository.ts b/lib/media/repository.ts index b8857e3..c097df0 100644 --- a/lib/media/repository.ts +++ b/lib/media/repository.ts @@ -28,7 +28,7 @@ export async function createPendingMedia( status: "pending", createdById: authorId, }); - const uploadUrl = getMediaStorage().presign(objectKey, { + const uploadUrl = (await getMediaStorage()).presign(objectKey, { method: "PUT", expiresIn, type: input.mimeType, @@ -45,7 +45,7 @@ export async function completePendingMedia(mediaId: string) { .limit(1); if (!record) return { status: "not-found" as const }; - const file = getMediaStorage().file(record.objectKey); + const file = (await getMediaStorage()).file(record.objectKey); try { const stat = await file.stat(); const prefix = await file.slice(0, 16).bytes(); @@ -85,7 +85,7 @@ export async function discardUnreferencedMedia(mediaId: string): Promise undefined); + await (await getMediaStorage()).delete(record.objectKey).catch(() => undefined); await getDb().delete(media).where(eq(media.id, mediaId)); return true; } diff --git a/lib/media/storage.ts b/lib/media/storage.ts index 1e97d09..810b268 100644 --- a/lib/media/storage.ts +++ b/lib/media/storage.ts @@ -1,8 +1,9 @@ import "server-only"; -import { S3Client } from "bun"; +import type { S3Client } from "bun"; let client: S3Client | undefined; +let loading: Promise | undefined; function required(name: string): string { const value = process.env[name]; @@ -10,16 +11,18 @@ function required(name: string): string { return value; } -export function getMediaStorage(): S3Client { - if (!client) { - client = new S3Client({ +export async function getMediaStorage(): Promise { + if (client) return client; + if (!loading) { + loading = import("bun").then(({ S3Client: BunS3Client }) => new BunS3Client({ accessKeyId: required("S3_ACCESS_KEY_ID"), secretAccessKey: required("S3_SECRET_ACCESS_KEY"), bucket: required("S3_BUCKET"), endpoint: required("S3_ENDPOINT"), region: process.env.S3_REGION || "auto", virtualHostedStyle: process.env.S3_VIRTUAL_HOSTED_STYLE === "true", - }); + })); } + client = await loading; return client; }