refactor(media): use asset-centric storage key layout

This commit is contained in:
2026-02-12 18:41:01 +01:00
parent 86a8af25d8
commit 3e4f0b6c75
12 changed files with 218 additions and 27 deletions

View File

@@ -5,12 +5,65 @@ export type MediaStorageProvider = "local" | "s3"
type StoreUploadParams = {
file: File
mediaType: string
assetId: string
variant: string
fileRole: string
}
type StoredUpload = {
storageKey: string
provider: MediaStorageProvider
fallbackReason?: string
}
type S3LikeError = {
name?: unknown
message?: unknown
Code?: unknown
code?: unknown
$metadata?: {
httpStatusCode?: unknown
requestId?: unknown
}
}
function resolveTenantId(): string {
return process.env.CMS_MEDIA_STORAGE_TENANT_ID?.trim() || "default"
}
function describeS3Error(error: unknown): string {
if (!error || typeof error !== "object") {
return "Unknown S3 error"
}
const err = error as S3LikeError
const details: string[] = []
if (typeof err.name === "string" && err.name.length > 0) {
details.push(`name=${err.name}`)
}
if (typeof err.message === "string" && err.message.length > 0) {
details.push(`message=${err.message}`)
}
if (typeof err.Code === "string" && err.Code.length > 0) {
details.push(`code=${err.Code}`)
} else if (typeof err.code === "string" && err.code.length > 0) {
details.push(`code=${err.code}`)
}
const status = err.$metadata?.httpStatusCode
if (typeof status === "number") {
details.push(`status=${status}`)
}
const requestId = err.$metadata?.requestId
if (typeof requestId === "string" && requestId.length > 0) {
details.push(`requestId=${requestId}`)
}
return details.length > 0 ? details.join(", ") : "Unknown S3 error"
}
export function resolveMediaStorageProvider(raw: string | undefined): MediaStorageProvider {
@@ -23,24 +76,45 @@ export function resolveMediaStorageProvider(raw: string | undefined): MediaStora
export async function storeUpload(params: StoreUploadParams): Promise<StoredUpload> {
const provider = resolveMediaStorageProvider(process.env.CMS_MEDIA_STORAGE_PROVIDER)
const tenantId = resolveTenantId()
if (provider === "s3") {
try {
const stored = await storeUploadToS3(params)
const stored = await storeUploadToS3({
file: params.file,
tenantId,
assetId: params.assetId,
fileRole: params.fileRole,
variant: params.variant,
})
return {
...stored,
provider,
}
} catch {
const fallbackStored = await storeUploadLocally(params)
} catch (error) {
const detail = describeS3Error(error)
const fallbackStored = await storeUploadLocally({
file: params.file,
tenantId,
assetId: params.assetId,
fileRole: params.fileRole,
variant: params.variant,
})
return {
...fallbackStored,
provider: "local",
fallbackReason: `S3 upload failed; file stored locally instead. ${detail}`,
}
}
}
const stored = await storeUploadLocally(params)
const stored = await storeUploadLocally({
file: params.file,
tenantId,
assetId: params.assetId,
fileRole: params.fileRole,
variant: params.variant,
})
return {
...stored,