이미지 자산 분류와 저장 경로 분산 보정

This commit is contained in:
2026-04-03 13:53:48 +09:00
parent b758823537
commit dddb57333c
6 changed files with 53 additions and 9 deletions

View File

@@ -1829,6 +1829,32 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50, filterMod
getCustomItemUsageMeta(),
])
const [userAvatarRows, topicThumbnailRows, tierListThumbnailRows, templateRequestThumbnailRows] = await Promise.all([
query("SELECT avatar_src AS src FROM users WHERE avatar_src <> ''"),
query("SELECT thumbnail_src AS src FROM topics WHERE thumbnail_src <> ''"),
query("SELECT thumbnail_src AS src FROM tierlists WHERE thumbnail_src <> ''"),
query("SELECT thumbnail_src_snapshot AS src FROM template_requests WHERE thumbnail_src_snapshot <> ''"),
])
const avatarSrcSet = new Set(userAvatarRows.map((row) => row.src).filter(Boolean))
const thumbnailSrcSet = new Set([
...topicThumbnailRows.map((row) => row.src).filter(Boolean),
...tierListThumbnailRows.map((row) => row.src).filter(Boolean),
...templateRequestThumbnailRows.map((row) => row.src).filter(Boolean),
])
const resolveLibraryAssetKind = (src) => {
if (avatarSrcSet.has(src)) return 'avatar'
if (thumbnailSrcSet.has(src)) return 'thumbnail'
return getAssetLibraryKind(src)
}
const resolveLibraryAssetLabel = (src) => {
if (avatarSrcSet.has(src)) return '프로필 아바타'
if (thumbnailSrcSet.has(src)) return '썸네일 이미지'
return getAssetLibrarySourceLabel(src)
}
const templateLinkedBySrc = new Map()
topicItemRows.forEach((row) => {
if (!row?.src) return
@@ -1851,6 +1877,7 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50, filterMod
ownerEmail: row.email,
usageCount: usageMeta.usageMap.get(row.id) || 0,
linkedTemplates,
assetKind: resolveLibraryAssetKind(row.src),
sourceType: 'user',
sourceLabel: '사용자 아이템',
canDelete: true,
@@ -1873,8 +1900,8 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50, filterMod
usageCount: 0,
linkedTemplates: [],
sourceType: 'asset',
sourceLabel: getAssetLibrarySourceLabel(row.src),
assetKind: getAssetLibraryKind(row.src),
sourceLabel: resolveLibraryAssetLabel(row.src),
assetKind: resolveLibraryAssetKind(row.src),
canDelete: true,
sourceTopicId: '',
sourceTopicName: '',
@@ -1891,6 +1918,7 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50, filterMod
ownerEmail: '',
usageCount: (templateLinkedBySrc.get(row.src) || new Map()).size,
linkedTemplates: Array.from((templateLinkedBySrc.get(row.src) || new Map()).values()),
assetKind: resolveLibraryAssetKind(row.src),
sourceType: 'template',
sourceLabel: '템플릿 아이템',
canDelete: true,
@@ -1958,9 +1986,9 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50, filterMod
case 'asset':
return item.sourceType === 'asset' || !!item.isAssetLibraryItem
case 'thumbnail':
return item.sourceType === 'asset' && item.assetKind === 'thumbnail'
return item.assetKind === 'thumbnail'
case 'avatar':
return item.sourceType === 'asset' && item.assetKind === 'avatar'
return item.assetKind === 'avatar'
case 'library':
return item.sourceType === 'user' || (item.sourceType === 'template' && !item.isAssetLibraryItem)
case 'unused-user':

View File

@@ -75,10 +75,13 @@ async function optimizeAndPersist({ file, width, height, fit, quality }) {
}
}
const filename = nanoid() + '.webp'
const absoluteDir = path.join(UPLOAD_ROOT, OPTIMIZED_DIR)
const basename = nanoid()
const shardDirectory = basename.slice(0, 2)
const filename = basename + '.webp'
const relativeDir = path.join(OPTIMIZED_DIR, shardDirectory)
const absoluteDir = path.join(UPLOAD_ROOT, relativeDir)
const absolutePath = path.join(absoluteDir, filename)
const src = '/uploads/' + OPTIMIZED_DIR + '/' + filename
const src = '/uploads/' + relativeDir.split(path.sep).join('/') + '/' + filename
await fs.mkdir(absoluteDir, { recursive: true })
await fs.writeFile(absolutePath, data)