From 86a8af25d8c28c2ab19039b56b1c69263c7450c5 Mon Sep 17 00:00:00 2001 From: Citali Date: Thu, 12 Feb 2026 18:16:11 +0100 Subject: [PATCH] feat(media): default to s3 with local upload fallback --- .env.example | 4 ++-- apps/admin/src/app/media/page.tsx | 3 ++- apps/admin/src/lib/media/storage.test.ts | 13 +++++++++---- apps/admin/src/lib/media/storage.ts | 22 +++++++++++++++------- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 1aa32f2..feb5f34 100644 --- a/.env.example +++ b/.env.example @@ -10,11 +10,11 @@ CMS_SUPPORT_EMAIL="support@cms.local" CMS_SUPPORT_PASSWORD="change-me-support-password" CMS_SUPPORT_NAME="Technical Support" CMS_SUPPORT_LOGIN_KEY="support-access-change-me" -CMS_MEDIA_STORAGE_PROVIDER="local" +CMS_MEDIA_STORAGE_PROVIDER="s3" CMS_MEDIA_UPLOAD_MAX_BYTES="26214400" # Optional: override local media storage directory for admin upload adapter. # CMS_MEDIA_LOCAL_STORAGE_DIR="/absolute/path/to/media-storage" -# S3/object-storage config (required when CMS_MEDIA_STORAGE_PROVIDER="s3"). +# S3/object-storage config (default provider). If unavailable, upload falls back to local storage. # CMS_MEDIA_S3_BUCKET="cms-media" # CMS_MEDIA_S3_REGION="eu-central-1" # CMS_MEDIA_S3_ACCESS_KEY_ID="" diff --git a/apps/admin/src/app/media/page.tsx b/apps/admin/src/app/media/page.tsx index ceea4ec..b0cabc4 100644 --- a/apps/admin/src/app/media/page.tsx +++ b/apps/admin/src/app/media/page.tsx @@ -81,7 +81,8 @@ export default async function MediaManagementPage({

Upload Media Asset

Upload storage provider: {activeStorageProvider}. You can switch via - `CMS_MEDIA_STORAGE_PROVIDER` (`local` or `s3`) until the admin settings toggle lands. + `CMS_MEDIA_STORAGE_PROVIDER` (`s3` default, `local` fallback) until the admin settings + toggle lands.

diff --git a/apps/admin/src/lib/media/storage.test.ts b/apps/admin/src/lib/media/storage.test.ts index e41803a..a9c97b5 100644 --- a/apps/admin/src/lib/media/storage.test.ts +++ b/apps/admin/src/lib/media/storage.test.ts @@ -3,8 +3,8 @@ import { describe, expect, it } from "vitest" import { resolveMediaStorageProvider } from "@/lib/media/storage" describe("resolveMediaStorageProvider", () => { - it("defaults to local when unset", () => { - expect(resolveMediaStorageProvider(undefined)).toBe("local") + it("defaults to s3 when unset", () => { + expect(resolveMediaStorageProvider(undefined)).toBe("s3") }) it("resolves s3", () => { @@ -12,7 +12,12 @@ describe("resolveMediaStorageProvider", () => { expect(resolveMediaStorageProvider("S3")).toBe("s3") }) - it("falls back to local for unknown values", () => { - expect(resolveMediaStorageProvider("foo")).toBe("local") + it("resolves local explicitly", () => { + expect(resolveMediaStorageProvider("local")).toBe("local") + expect(resolveMediaStorageProvider("LOCAL")).toBe("local") + }) + + it("falls back to s3 for unknown values", () => { + expect(resolveMediaStorageProvider("foo")).toBe("s3") }) }) diff --git a/apps/admin/src/lib/media/storage.ts b/apps/admin/src/lib/media/storage.ts index 2e77438..bac8764 100644 --- a/apps/admin/src/lib/media/storage.ts +++ b/apps/admin/src/lib/media/storage.ts @@ -14,21 +14,29 @@ type StoredUpload = { } export function resolveMediaStorageProvider(raw: string | undefined): MediaStorageProvider { - if (raw?.toLowerCase() === "s3") { - return "s3" + if (raw?.toLowerCase() === "local") { + return "local" } - return "local" + return "s3" } export async function storeUpload(params: StoreUploadParams): Promise { const provider = resolveMediaStorageProvider(process.env.CMS_MEDIA_STORAGE_PROVIDER) if (provider === "s3") { - const stored = await storeUploadToS3(params) - return { - ...stored, - provider, + try { + const stored = await storeUploadToS3(params) + return { + ...stored, + provider, + } + } catch { + const fallbackStored = await storeUploadLocally(params) + return { + ...fallbackStored, + provider: "local", + } } }