import { uploadFileToS3, uploadToS3 } from '@/lib/s3' export const PUBLIC_DISPLAY_PREFIX = 'public-display' function normalizeRelativePath(relativePath: string): string { return String(relativePath || '') .trim() .replace(/\\/g, '/') .replace(/^\/+/, '') .replace(/\/{2,}/g, '/') } function normalizeS3PublicDomain(): string | null { const rawDomain = String(process.env.S3_PUBLIC_DOMAIN || '').trim() if (!rawDomain) { return null } const normalized = rawDomain.match(/^https?:\/\//i) ? rawDomain : `https://${rawDomain}` return normalized.replace(/\/+$/, '') } export function buildPublicDisplayObjectPath(relativePath: string): string { const normalized = normalizeRelativePath(relativePath) if (!normalized) { return PUBLIC_DISPLAY_PREFIX } return `${PUBLIC_DISPLAY_PREFIX}/${normalized}` } function buildStorageObjectPath(objectPath: string): string { const normalizedPrefix = normalizeRelativePath(process.env.S3_FILE_PATH || '') const normalizedObjectPath = normalizeRelativePath(objectPath) if (!normalizedPrefix) { return normalizedObjectPath } return normalizeRelativePath(`${normalizedPrefix}/${normalizedObjectPath}`) } export function buildPublicDisplayUrl(relativePath: string): string | null { const publicDomain = normalizeS3PublicDomain() const objectPath = buildPublicDisplayObjectPath(relativePath) if (!publicDomain || !objectPath) { return null } return `${publicDomain}/${buildStorageObjectPath(objectPath)}` } export async function uploadPublicDisplayToS3( file: Buffer, relativePath: string, contentType: string, ): Promise { return uploadToS3(file, buildPublicDisplayObjectPath(relativePath), contentType) } export async function uploadPublicDisplayFileToS3( filePath: string, relativePath: string, contentType: string, expectedSize?: number, ): Promise { return uploadFileToS3( filePath, buildPublicDisplayObjectPath(relativePath), contentType, expectedSize, ) } export function buildMaterialPublicDisplayPath(fileName: string): string { return normalizeRelativePath(`materials/${fileName}`) } export function buildTaskDetailPublicDisplayPath( materialId: string, group: 'main' | 'sku' | 'detail', fileName: string, ): string { return normalizeRelativePath(`task-detail/${materialId}/${group}/${fileName}`) } export function buildCertificateReportPublicDisplayPath( materialId: string, group: 'pdf' | 'image' | 'preview', fileName: string, ): string { return normalizeRelativePath(`certificate-report/${materialId}/${group}/${fileName}`) } export function buildWhiteBackgroundPublicDisplayPath( materialId: string, skuId: string, fileName: string, ): string { return normalizeRelativePath(`white-background/${materialId}/${skuId}/${fileName}`) } export function buildWhiteBackgroundPublicDisplayPathFromAssetPath( fileUrl?: string | null, ): string | null { const normalizedFileUrl = String(fileUrl || '').trim().replace(/\\/g, '/') if (!normalizedFileUrl || /^https?:\/\//i.test(normalizedFileUrl)) { return null } const normalizedRelativePath = normalizedFileUrl.replace(/^\.?\//, '') const segments = normalizedRelativePath.split('/').filter(Boolean) const whiteBackgroundIndex = segments.indexOf('white-background') if ( whiteBackgroundIndex < 0 || whiteBackgroundIndex < 2 || !segments[whiteBackgroundIndex - 1] || !segments[whiteBackgroundIndex + 1] ) { return null } const materialId = segments[whiteBackgroundIndex - 1] const skuId = segments[whiteBackgroundIndex + 1] const fileSegments = segments.slice(whiteBackgroundIndex + 2) if (fileSegments.length === 0) { return null } if (fileSegments[0] === 'preview') { fileSegments.shift() } if (fileSegments.length === 0) { return null } return buildWhiteBackgroundPublicDisplayPath(materialId, skuId, fileSegments.join('/')) }