refactor(media): use asset-centric storage key layout
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user