diff --git a/backend/package.json b/backend/package.json index d8a17a4..37e0aa0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,7 +7,8 @@ "dev": "DB_HOST=127.0.0.1 DB_PORT=3307 DB_USER=tier_cursor DB_PASSWORD=tier_cursor1234 DB_NAME=tier_cursor nodemon --legacy-watch --watch index.js --watch src index.js", "start": "DB_HOST=127.0.0.1 DB_PORT=3307 DB_USER=tier_cursor DB_PASSWORD=tier_cursor1234 DB_NAME=tier_cursor node index.js", "images:backfill": "DB_HOST=127.0.0.1 DB_PORT=3307 DB_USER=tier_cursor DB_PASSWORD=tier_cursor1234 DB_NAME=tier_cursor node scripts/backfill-legacy-image-assets.js", - "images:migrate-legacy": "DB_HOST=127.0.0.1 DB_PORT=3307 DB_USER=tier_cursor DB_PASSWORD=tier_cursor1234 DB_NAME=tier_cursor node scripts/migrate-legacy-uploads-to-assets.js" + "images:migrate-legacy": "DB_HOST=127.0.0.1 DB_PORT=3307 DB_USER=tier_cursor DB_PASSWORD=tier_cursor1234 DB_NAME=tier_cursor node scripts/migrate-legacy-uploads-to-assets.js", + "uploads:cleanup-legacy": "DB_HOST=127.0.0.1 DB_PORT=3307 DB_USER=tier_cursor DB_PASSWORD=tier_cursor1234 DB_NAME=tier_cursor node scripts/cleanup-unreferenced-legacy-uploads.js" }, "keywords": [], "author": "", diff --git a/backend/scripts/cleanup-unreferenced-legacy-uploads.js b/backend/scripts/cleanup-unreferenced-legacy-uploads.js new file mode 100644 index 0000000..020dcb2 --- /dev/null +++ b/backend/scripts/cleanup-unreferenced-legacy-uploads.js @@ -0,0 +1,56 @@ +const fs = require('fs/promises') +const path = require('path') +const { + ensureData, + closePool, + listReferencedUploadSources, +} = require('../src/db') + +const BACKEND_ROOT = path.join(__dirname, '..') +const TARGET_DIRS = ['avatars', 'custom', 'games', 'tierlists'] + +async function main() { + await ensureData() + + const referenced = new Set(await listReferencedUploadSources()) + const deleted = [] + const missing = [] + let scanned = 0 + + for (const dir of TARGET_DIRS) { + const absoluteDir = path.join(BACKEND_ROOT, 'uploads', dir) + let entries = [] + try { + entries = await fs.readdir(absoluteDir, { withFileTypes: true }) + } catch (error) { + if (error?.code === 'ENOENT') continue + throw error + } + + for (const entry of entries) { + if (!entry.isFile()) continue + scanned += 1 + const src = `/uploads/${dir}/${entry.name}` + if (referenced.has(src)) continue + const absolutePath = path.join(absoluteDir, entry.name) + try { + await fs.unlink(absolutePath) + deleted.push(src) + } catch (error) { + if (error?.code === 'ENOENT') missing.push(src) + else throw error + } + } + } + + console.log(JSON.stringify({ scanned, deletedCount: deleted.length, missingCount: missing.length, deleted, missing }, null, 2)) +} + +main() + .catch((error) => { + console.error(error) + process.exitCode = 1 + }) + .finally(async () => { + await closePool() + }) diff --git a/docs/todo.md b/docs/todo.md index 6f43c3d..f32490c 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -1,7 +1,9 @@ # 할 일 및 이슈 ## 즉시 확인 필요 -- 레거시 참조를 `/uploads/assets/`로 재정렬하는 마이그레이션 스크립트는 준비됐으므로, 운영 반영 후에는 더 이상 참조되지 않는 예전 업로드 파일을 안전하게 정리하는 후속 배치를 검토한다. +- 티어표 랭크부분 삭제 버튼 최소화 필요 (각 라인별 우측 상단에 absolute 방식의 x 아이콘으로 변경. 클릭시 라인 삭제 경고를 보여주고 확인후 삭제 ) +- 티어표 형식 추가 필요. 최근 게임들은 S, A, B,C 같은 랭크 뿐만 아니라 가로 열도 나누어진형태의 티어표를 원함 (공격, 방어, 지원 등 각 파트별 랭크를 보고싶어함) +- 레거시 파일 정리 스크립트는 준비됐으므로, 운영 단계에서는 cron 등으로 주기 실행할지와 삭제 전 보관 기간을 함께 정한다. - 관리자 기본 아이템 다중 업로드는 현재 파일명 기반 자동 라벨만 지원하므로, 필요하면 업로드 후 일괄 라벨 수정/정렬 UX를 추가 검토한다. - 사용자 커스텀 아이템 승격은 현재 수동 복제 방식이므로, 필요하면 중복 감지나 “비슷한 항목 추천” 같은 보조 UX를 검토한다. - 관리자 티어표 관리의 추가 아이템 승격은 현재 커스텀(origin=`custom`) 아이템 기준이므로, 필요하면 “기존 게임 아이템과 비교한 차집합” 기준으로 더 정교하게 확장할 수 있다. diff --git a/docs/update.md b/docs/update.md index 999cfa3..2d8ec5c 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,10 @@ # 업데이트 로그 +## 2026-03-31 v1.3.8 +- 홈 화면 게임 즐겨찾기 버튼은 일반 문자 별 대신 'kid_star.svg' 아이콘을 사용하도록 바꿔, 기존 아이콘 시스템과 같은 문법으로 정리함. +- 실제로 더 이상 참조되지 않는 예전 업로드 파일을 정리하는 레거시 업로드 클린업 스크립트를 추가하고, 루트/백엔드 실행 스크립트도 함께 연결함. +- todo 문서도 이제 운영 반영 후 레거시 파일 정리 배치를 주기화하는 쪽으로 기준을 갱신함. + ## 2026-03-31 v1.3.7 - 현재 참조 중인 레거시 업로드를 다시 최적화 자산 경로로 편입하고 DB 참조를 일괄 교체하는 1회 마이그레이션 스크립트를 추가함. - 아바타/썸네일/아이템 역할에 따라 기존 업로드를 512px 또는 1280px 규격으로 다시 정리해, 실제 참조 경로도 '/uploads/assets/' 체계에 점진적으로 수렴시킬 수 있게 함. diff --git a/frontend/src/assets/icons/kid_star.svg b/frontend/src/assets/icons/kid_star.svg new file mode 100644 index 0000000..646c825 --- /dev/null +++ b/frontend/src/assets/icons/kid_star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index b5d7ec5..7f48a38 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -2,6 +2,7 @@ import { computed, onMounted, ref, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' import { api } from '../lib/api' +import kidStarIcon from '../assets/icons/kid_star.svg' import { toApiUrl } from '../lib/runtime' import { useAuthStore } from '../stores/auth' @@ -91,7 +92,7 @@ function thumbUrl(g) { :disabled="loadingFavoriteId === g.id" @click.stop="toggleFavorite(g, $event)" > - {{ g.isFavorited ? '★' : '☆' }} +