import { randomUUID } from 'crypto' import { mkdir } from 'fs/promises' import path from 'path' import { downloadS3ObjectToFile } from '@/lib/s3' import { resolveStoredUploadPath } from '@/lib/upload-paths' import { buildPublicDisplayUrl, buildWhiteBackgroundPublicDisplayPathFromAssetPath } from '@/lib/public-display' import { existsSync } from 'fs' const MATERIAL_FILE_FIELDS = [ 'previewUrl', 'zipFilePath', 'zipFileName', 'videoSourceUrl', 'videoPreview540Url', 'videoPreviewClipUrl', 'videoCoverUrl', ] as const /** 将已公开的存储对象改为受素材访问权限保护的独立文件。 */ export async function prepareOperationsGMaterialFiles( material: Record, assets: Array<{ id: string; fileUrl: string }>, skus: Array<{ id: string; filePath: string; previewUrl: string | null }> = [], ) { const replacements = new Map() const copyUrl = async (url: string) => { if (!/^https?:\/\//i.test(url)) return url const existing = replacements.get(url) if (existing) return existing const fileName = `${randomUUID()}${path.posix.extname(new URL(url).pathname)}` const storedPath = path.posix.join('uploads', 'materials', material.id, 'protected-display', fileName) const destination = resolveStoredUploadPath(storedPath) await mkdir(path.dirname(destination), { recursive: true }) await downloadS3ObjectToFile(url, destination) replacements.set(url, storedPath) return storedPath } const materialFiles: Record = {} for (const field of MATERIAL_FILE_FIELDS) { if (material[field]) materialFiles[field] = await copyUrl(material[field]) } if (Array.isArray(material.displayImages)) { materialFiles.displayImages = [] for (const url of material.displayImages) materialFiles.displayImages.push(await copyUrl(url)) } const assetFiles = [] for (const asset of assets) { const fileUrl = await copyUrl(asset.fileUrl) if (fileUrl !== asset.fileUrl) assetFiles.push({ id: asset.id, fileUrl }) const previewPath = buildWhiteBackgroundPublicDisplayPathFromAssetPath(asset.fileUrl) const publicPreview = previewPath && /\.webp$/i.test(previewPath) ? buildPublicDisplayUrl(previewPath) : null if (previewPath && publicPreview) { const storedPath = path.posix.join('uploads', 'materials', material.id, 'protected-display', previewPath) const destination = resolveStoredUploadPath(storedPath) if (!existsSync(destination)) { await mkdir(path.dirname(destination), { recursive: true }) await downloadS3ObjectToFile(publicPreview, destination) } replacements.set(publicPreview, storedPath) } } const skuFiles = [] for (const sku of skus) { const filePath = await copyUrl(sku.filePath) const previewUrl = sku.previewUrl ? await copyUrl(sku.previewUrl) : sku.previewUrl if (filePath !== sku.filePath || previewUrl !== sku.previewUrl) skuFiles.push({ id: sku.id, filePath, previewUrl }) } return { materialFiles, assetFiles, skuFiles, publicUrls: Array.from(replacements.keys()) } }