Compare commits

..

1 Commits

Author SHA1 Message Date
188576f8ac 릴리스: v1.3.66 누락 썸네일 정리 조건 보정 2026-04-02 14:49:17 +09:00
4 changed files with 71 additions and 53 deletions

View File

@@ -1083,6 +1083,8 @@ async function cleanupMissingUploadReferences() {
const stats = {
clearedAvatars: 0,
clearedGameThumbnails: 0,
clearedTierListThumbnails: 0,
clearedTemplateRequestThumbnails: 0,
deletedGameItems: 0,
updatedTierLists: 0,
updatedTemplateRequests: 0,
@@ -1124,67 +1126,69 @@ async function cleanupMissingUploadReferences() {
missingCustomSrcs.add(row.src)
}
if (missingCustomItemIds.size || missingCustomSrcs.size) {
for (const row of tierListRows) {
const groups = parseJson(row.groups_json, [])
const pool = parseJson(row.pool_json, [])
let changed = false
let nextThumbnail = row.thumbnail_src || ''
for (const row of tierListRows) {
const groups = parseJson(row.groups_json, [])
const pool = parseJson(row.pool_json, [])
let changed = false
let nextThumbnail = row.thumbnail_src || ''
if (row.thumbnail_src && !(await fileExistsForUploadSrc(row.thumbnail_src))) {
nextThumbnail = ''
changed = true
}
if (row.thumbnail_src && !(await fileExistsForUploadSrc(row.thumbnail_src))) {
nextThumbnail = ''
changed = true
stats.clearedTierListThumbnails += 1
}
const strippedPool = stripMissingItems(pool, missingCustomItemIds, missingCustomSrcs)
const strippedGroups = stripItemIdsFromGroups(groups, missingCustomItemIds)
if (strippedPool.changed || strippedGroups.changed) changed = true
const strippedPool = stripMissingItems(pool, missingCustomItemIds, missingCustomSrcs)
const strippedGroups = stripItemIdsFromGroups(groups, missingCustomItemIds)
if (strippedPool.changed || strippedGroups.changed) changed = true
if (changed) {
await query('UPDATE tierlists SET thumbnail_src = ?, groups_json = ?, pool_json = ?, updated_at = ? WHERE id = ?', [
if (changed) {
await query('UPDATE tierlists SET thumbnail_src = ?, groups_json = ?, pool_json = ?, updated_at = ? WHERE id = ?', [
nextThumbnail,
serializeJson(strippedGroups.groups),
serializeJson(strippedPool.items),
now(),
row.id,
])
stats.updatedTierLists += 1
}
}
for (const row of templateRequestRows) {
const groups = parseJson(row.groups_json, [])
const items = parseJson(row.items_json, [])
const boardItems = parseJson(row.board_items_json, [])
let changed = false
let nextThumbnail = row.thumbnail_src_snapshot || ''
if (row.thumbnail_src_snapshot && !(await fileExistsForUploadSrc(row.thumbnail_src_snapshot))) {
nextThumbnail = ''
changed = true
stats.clearedTemplateRequestThumbnails += 1
}
const strippedItems = stripMissingItems(items, missingCustomItemIds, missingCustomSrcs)
const strippedBoardItems = stripMissingItems(boardItems, missingCustomItemIds, missingCustomSrcs)
const strippedGroups = stripItemIdsFromGroups(groups, missingCustomItemIds)
if (strippedItems.changed || strippedBoardItems.changed || strippedGroups.changed) changed = true
if (changed) {
await query(
'UPDATE template_requests SET thumbnail_src_snapshot = ?, groups_json = ?, items_json = ?, board_items_json = ?, updated_at = ? WHERE id = ?',
[
nextThumbnail,
serializeJson(strippedGroups.groups),
serializeJson(strippedPool.items),
serializeJson(strippedItems.items),
serializeJson(strippedBoardItems.items),
now(),
row.id,
])
stats.updatedTierLists += 1
}
}
for (const row of templateRequestRows) {
const groups = parseJson(row.groups_json, [])
const items = parseJson(row.items_json, [])
const boardItems = parseJson(row.board_items_json, [])
let changed = false
let nextThumbnail = row.thumbnail_src_snapshot || ''
if (row.thumbnail_src_snapshot && !(await fileExistsForUploadSrc(row.thumbnail_src_snapshot))) {
nextThumbnail = ''
changed = true
}
const strippedItems = stripMissingItems(items, missingCustomItemIds, missingCustomSrcs)
const strippedBoardItems = stripMissingItems(boardItems, missingCustomItemIds, missingCustomSrcs)
const strippedGroups = stripItemIdsFromGroups(groups, missingCustomItemIds)
if (strippedItems.changed || strippedBoardItems.changed || strippedGroups.changed) changed = true
if (changed) {
await query(
'UPDATE template_requests SET thumbnail_src_snapshot = ?, groups_json = ?, items_json = ?, board_items_json = ?, updated_at = ? WHERE id = ?',
[
nextThumbnail,
serializeJson(strippedGroups.groups),
serializeJson(strippedItems.items),
serializeJson(strippedBoardItems.items),
now(),
row.id,
]
)
stats.updatedTemplateRequests += 1
}
]
)
stats.updatedTemplateRequests += 1
}
}
if (missingCustomItemIds.size) {
await deleteCustomItems(Array.from(missingCustomItemIds))
stats.deletedCustomItems = missingCustomItemIds.size
}

View File

@@ -1,5 +1,8 @@
# 의사결정 이력
## 2026-04-02 v1.3.66
- 누락 참조 정리 도구는 커스텀 아이템 누락이 없어도 티어표/요청 썸네일 누락을 항상 따로 정리해야 하므로, 썸네일 정리를 커스텀 아이템 분기에 묶어두면 안 된다고 정리했다.
## 2026-04-02 v1.3.65
- 누락 파일 수치가 계속 쌓이는 상태에서는 원인 분석만으로는 운영 부담이 줄지 않으므로, 실제 파일이 없는 참조만 골라 썸네일/아바타/게임 아이템/커스텀 아이템 참조를 정리하는 관리자 액션을 제공하는 편이 맞다고 정리했다.

View File

@@ -1,5 +1,9 @@
# 업데이트 로그
## 2026-04-02 v1.3.66
- `누락 참조 정리`는 처음엔 누락 커스텀 아이템이 있을 때만 티어표/요청 썸네일까지 함께 보던 조건 때문에, 누락 썸네일만 남아 있으면 수치가 줄지 않던 문제가 있었으므로 분기를 풀어 티어표 썸네일과 요청 썸네일도 항상 실제 파일 존재 여부를 확인해 정리하도록 수정함.
- 정리 완료 메시지에도 `티어표 썸네일`, `요청 썸네일` 항목을 추가해 어떤 종류의 누락 참조가 실제로 정리됐는지 바로 알 수 있게 함.
## 2026-04-02 v1.3.65
- 관리자 이미지 최적화 패널에 `누락 참조 정리` 액션을 추가해, 실제 파일이 없는 `/uploads/...` 참조만 대상으로 썸네일/아바타는 비우고 누락된 게임 아이템·커스텀 아이템은 관련 티어표/템플릿 요청 참조와 함께 정리할 수 있게 함.
- 따라서 예전 수동 파일 정리나 레거시 데이터로 인해 쌓인 `누락 파일`은 단순 통계로만 남지 않고, 관리자 화면에서 실제로 줄일 수 있는 관리 도구를 갖게 됨.

View File

@@ -677,7 +677,14 @@ async function cleanupMissingImageReferences() {
const data = await api.cleanupAdminMissingImageReferences()
await Promise.all([refreshImageDiagnostics(), refreshGames(), refreshCustomItems(), refreshTemplateRequests()])
const result = data.result || {}
success.value = `누락 참조를 정리했어요. 아바타 ${result.clearedAvatars || 0}건, 게임 썸네일 ${result.clearedGameThumbnails || 0}건, 게임 아이템 ${result.deletedGameItems || 0}건, 커스텀 아이템 ${result.deletedCustomItems || 0}`
success.value =
`누락 참조를 정리했어요. ` +
`아바타 ${result.clearedAvatars || 0}건, ` +
`게임 썸네일 ${result.clearedGameThumbnails || 0}건, ` +
`티어표 썸네일 ${result.clearedTierListThumbnails || 0}건, ` +
`요청 썸네일 ${result.clearedTemplateRequestThumbnails || 0}건, ` +
`게임 아이템 ${result.deletedGameItems || 0}건, ` +
`커스텀 아이템 ${result.deletedCustomItems || 0}`
} catch (e) {
error.value = '누락 이미지 참조 정리에 실패했어요.'
} finally {