gongxiangp2/lib/task-detail-preview-storage.ts

60 lines
1.6 KiB
TypeScript

import { mkdir, writeFile } from 'fs/promises'
import path from 'path'
import { randomUUID } from 'crypto'
import {
buildTaskDetailPublicDisplayPath,
uploadPublicDisplayToS3,
} from '@/lib/public-display'
import { resolveStoredUploadPath } from '@/lib/upload-paths'
export type TaskDetailPreviewGroup = 'main' | 'sku' | 'detail'
function buildPrivatePreviewFileName(
group: TaskDetailPreviewGroup,
fileName: string,
): string {
const normalizedFileName = String(fileName || '').trim().replace(/\\/g, '/')
const parsed = path.posix.parse(path.posix.basename(normalizedFileName))
const safeBaseName = (parsed.name || `${group}-preview`)
.replace(/[\/\\]/g, '_')
.replace(/\.\./g, '_')
.replace(/[\x00-\x1f]/g, '')
.replace(/[<>:"|?*]/g, '_')
.trim()
.slice(0, 80)
const uniqueBaseName = safeBaseName || `${group}-preview`
return `${Date.now()}-${randomUUID()}-${uniqueBaseName}.webp`
}
export async function saveTaskDetailPreview(
buffer: Buffer,
materialId: string,
group: TaskDetailPreviewGroup,
fileName: string,
isPrivate: boolean,
): Promise<string> {
if (!isPrivate) {
return uploadPublicDisplayToS3(
buffer,
buildTaskDetailPublicDisplayPath(materialId, group, fileName),
'image/webp',
)
}
const storedPath = path.posix.join(
'uploads',
'materials',
materialId,
'private-previews',
group,
buildPrivatePreviewFileName(group, fileName),
)
const absolutePath = resolveStoredUploadPath(storedPath)
await mkdir(path.dirname(absolutePath), { recursive: true })
await writeFile(absolutePath, buffer)
return storedPath
}