| 1 | import type { UserType } from '@yikart/common' |
| 2 | import { AssetType } from '@yikart/mongodb' |
| 3 | import dayjs from 'dayjs' |
| 4 | import { extension } from 'mime-types' |
| 5 | import { nanoid } from 'nanoid' |
| 6 | |
| 7 | export interface PathGeneratorOptions { |
| 8 | userId: string |
| 9 | userType?: UserType |
| 10 | type: AssetType |
| 11 | mimeType: string |
| 12 | filename?: string |
| 13 | subPath?: string |
| 14 | } |
| 15 | |
| 16 | const TYPE_PATH_MAP: Record<AssetType, string> = { |
| 17 | [AssetType.AiImage]: 'ai/images', |
| 18 | [AssetType.AiVideo]: 'ai/video', |
| 19 | [AssetType.AiCard]: 'ai/cards', |
| 20 | [AssetType.AiChatImage]: 'ai/chat-images', |
| 21 | [AssetType.AideoOutput]: 'agent/aideo', |
| 22 | [AssetType.VideoEdit]: 'agent/video-edit', |
| 23 | [AssetType.DramaRecap]: 'agent/drama-recap', |
| 24 | [AssetType.StyleTransfer]: 'agent/style-transfer', |
| 25 | [AssetType.UserMedia]: 'user/media', |
| 26 | [AssetType.UserFile]: 'user/files', |
| 27 | [AssetType.PublishMedia]: 'publish/media', |
| 28 | [AssetType.Avatar]: 'social/avatar', |
| 29 | [AssetType.AgentSession]: 'claude-session', |
| 30 | [AssetType.VideoThumbnail]: 'ai/video-thumbnail', |
| 31 | [AssetType.GooglePlace]: 'google-maps', |
| 32 | [AssetType.Temp]: 'temp', |
| 33 | [AssetType.ImageEdit]: 'agent/image-edit', |
| 34 | [AssetType.Subtitle]: 'agent/subtitle', |
| 35 | } |
| 36 | |
| 37 | export function generateAssetPath(options: PathGeneratorOptions): string { |
| 38 | const { userId, type, mimeType, subPath } = options |
| 39 | |
| 40 | const basePath = TYPE_PATH_MAP[type] || 'misc' |
| 41 | const datePath = dayjs().format('YYYYMM') |
| 42 | const ext = extension(mimeType) || 'bin' |
| 43 | const uniqueId = nanoid() |
| 44 | |
| 45 | const parts = [userId, basePath] |
| 46 | |
| 47 | if (subPath) { |
| 48 | parts.push(subPath) |
| 49 | } |
| 50 | |
| 51 | parts.push(datePath) |
| 52 | parts.push(`${uniqueId}.${ext}`) |
| 53 | |
| 54 | return parts.join('/') |
| 55 | } |
| 56 | |
| 57 | export function generateAssetPathFromFilename(options: PathGeneratorOptions): string { |
| 58 | const { userId, type, filename, subPath } = options |
| 59 | |
| 60 | const basePath = TYPE_PATH_MAP[type] || 'misc' |
| 61 | const datePath = dayjs().format('YYYYMM') |
| 62 | const uniquePrefix = Date.now().toString(36) |
| 63 | const safeName = filename ? filename.replace(/[^a-z0-9.-]/gi, '_') : nanoid() |
| 64 | |
| 65 | const parts = [userId, basePath] |
| 66 | |
| 67 | if (subPath) { |
| 68 | parts.push(subPath) |
| 69 | } |
| 70 | |
| 71 | parts.push(datePath) |
| 72 | parts.push(`${uniquePrefix}-${safeName}`) |
| 73 | |
| 74 | return parts.join('/') |
| 75 | } |
| 76 |