feat(media): add mvp1 upload pipeline baseline
This commit is contained in:
66
apps/admin/src/lib/media/local-storage.ts
Normal file
66
apps/admin/src/lib/media/local-storage.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { randomUUID } from "node:crypto"
|
||||
import { mkdir, writeFile } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
|
||||
type StoreUploadParams = {
|
||||
file: File
|
||||
mediaType: string
|
||||
}
|
||||
|
||||
type StoredUpload = {
|
||||
storageKey: string
|
||||
}
|
||||
|
||||
const FALLBACK_EXTENSION = "bin"
|
||||
|
||||
function resolveBaseDirectory(): string {
|
||||
const configured = process.env.CMS_MEDIA_LOCAL_STORAGE_DIR?.trim()
|
||||
|
||||
if (configured) {
|
||||
return path.resolve(configured)
|
||||
}
|
||||
|
||||
return path.resolve(process.cwd(), ".data", "media")
|
||||
}
|
||||
|
||||
function normalizeSegment(value: string): string {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9._-]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
}
|
||||
|
||||
function extensionFromFilename(fileName: string): string {
|
||||
const extension = path.extname(fileName).slice(1)
|
||||
|
||||
if (!extension) {
|
||||
return FALLBACK_EXTENSION
|
||||
}
|
||||
|
||||
const normalized = normalizeSegment(extension)
|
||||
|
||||
return normalized.length > 0 ? normalized : FALLBACK_EXTENSION
|
||||
}
|
||||
|
||||
function buildStorageKey(mediaType: string, fileName: string): string {
|
||||
const now = new Date()
|
||||
const year = String(now.getUTCFullYear())
|
||||
const month = String(now.getUTCMonth() + 1).padStart(2, "0")
|
||||
const normalizedType = normalizeSegment(mediaType) || "generic"
|
||||
const extension = extensionFromFilename(fileName)
|
||||
|
||||
return [normalizedType, year, month, `${randomUUID()}.${extension}`].join("/")
|
||||
}
|
||||
|
||||
export async function storeUploadLocally(params: StoreUploadParams): Promise<StoredUpload> {
|
||||
const storageKey = buildStorageKey(params.mediaType, params.file.name)
|
||||
const baseDirectory = resolveBaseDirectory()
|
||||
const outputPath = path.join(baseDirectory, storageKey)
|
||||
|
||||
await mkdir(path.dirname(outputPath), { recursive: true })
|
||||
|
||||
const bytes = new Uint8Array(await params.file.arrayBuffer())
|
||||
await writeFile(outputPath, bytes)
|
||||
|
||||
return { storageKey }
|
||||
}
|
||||
Reference in New Issue
Block a user