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",
+ }
}
}