릴리스: v0.1.17 티어표 삭제와 미사용 이미지 정리 추가

This commit is contained in:
2026-03-19 17:52:09 +09:00
parent 85ee6e3649
commit 8af2726574
11 changed files with 305 additions and 34 deletions

View File

@@ -404,25 +404,38 @@ async function createCustomItem({ id, ownerId, src, label }) {
return { id, ownerId, src, label, origin: 'custom', createdAt }
}
async function listCustomItems({ queryText = '', page = 1, limit = 50 } = {}) {
async function getCustomItemUsageMap() {
const rows = await query('SELECT groups_json, pool_json FROM tierlists')
const usageMap = new Map()
rows.forEach((row) => {
const groups = parseJson(row.groups_json, [])
const pool = parseJson(row.pool_json, [])
groups.forEach((group) => {
;(group?.itemIds || []).forEach((itemId) => {
usageMap.set(itemId, (usageMap.get(itemId) || 0) + 1)
})
})
pool.forEach((item) => {
if (item?.id) {
usageMap.set(item.id, (usageMap.get(item.id) || 0) + 1)
}
})
})
return usageMap
}
async function listCustomItems({ queryText = '', page = 1, limit = 50, orphanOnly = false } = {}) {
const normalizedLimit = Math.min(Math.max(Number(limit) || 50, 1), 200)
const normalizedPage = Math.max(Number(page) || 1, 1)
const offset = (normalizedPage - 1) * normalizedLimit
const hasQuery = !!(queryText || '').trim()
const search = `%${(queryText || '').trim()}%`
const whereClause = hasQuery ? 'WHERE c.label LIKE ? OR c.src LIKE ? OR u.email LIKE ? OR u.nickname LIKE ?' : ''
const params = hasQuery ? [search, search, search, search] : []
const countRows = await query(
`
SELECT COUNT(*) AS count
FROM custom_items c
INNER JOIN users u ON u.id = c.owner_id
${whereClause}
`,
params
)
const rows = await query(
`
SELECT
@@ -437,13 +450,13 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50 } = {}) {
INNER JOIN users u ON u.id = c.owner_id
${whereClause}
ORDER BY c.created_at DESC
LIMIT ? OFFSET ?
`,
[...params, normalizedLimit, offset]
params
)
return {
items: rows.map((row) => ({
const usageMap = await getCustomItemUsageMap()
const allItems = rows
.map((row) => ({
id: row.id,
ownerId: row.owner_id,
src: row.src,
@@ -451,13 +464,61 @@ async function listCustomItems({ queryText = '', page = 1, limit = 50 } = {}) {
createdAt: Number(row.created_at),
ownerName: row.nickname || row.email,
ownerEmail: row.email,
})),
total: Number(countRows[0]?.count || 0),
usageCount: usageMap.get(row.id) || 0,
}))
.filter((item) => (orphanOnly ? item.usageCount === 0 : true))
const total = allItems.length
const offset = (normalizedPage - 1) * normalizedLimit
const pagedItems = allItems.slice(offset, offset + normalizedLimit)
return {
items: pagedItems,
total,
page: normalizedPage,
limit: normalizedLimit,
}
}
async function findUnusedCustomItems({ queryText = '' } = {}) {
const hasQuery = !!(queryText || '').trim()
const search = `%${(queryText || '').trim()}%`
const whereClause = hasQuery ? 'WHERE c.label LIKE ? OR c.src LIKE ? OR u.email LIKE ? OR u.nickname LIKE ?' : ''
const params = hasQuery ? [search, search, search, search] : []
const rows = await query(
`
SELECT
c.id,
c.owner_id,
c.src,
c.label,
c.created_at,
u.nickname,
u.email
FROM custom_items c
INNER JOIN users u ON u.id = c.owner_id
${whereClause}
ORDER BY c.created_at DESC
`,
params
)
const usageMap = await getCustomItemUsageMap()
return rows
.map((row) => ({
id: row.id,
ownerId: row.owner_id,
src: row.src,
label: row.label,
createdAt: Number(row.created_at),
ownerName: row.nickname || row.email,
ownerEmail: row.email,
usageCount: usageMap.get(row.id) || 0,
}))
.filter((item) => item.usageCount === 0)
}
async function listPublicTierLists(gameId) {
const params = []
let whereClause = 'WHERE t.is_public = 1'
@@ -531,6 +592,37 @@ async function findTierListById(id) {
return mapTierListRow(rows[0])
}
async function deleteTierList(id) {
await query('DELETE FROM tierlists WHERE id = ?', [id])
}
async function findCustomItemsByIds(ids) {
if (!ids.length) return []
const placeholders = ids.map(() => '?').join(', ')
const rows = await query(
`
SELECT id, owner_id, src, label, created_at
FROM custom_items
WHERE id IN (${placeholders})
`,
ids
)
return rows.map((row) => ({
id: row.id,
ownerId: row.owner_id,
src: row.src,
label: row.label,
createdAt: Number(row.created_at),
}))
}
async function deleteCustomItems(ids) {
if (!ids.length) return
const placeholders = ids.map(() => '?').join(', ')
await query(`DELETE FROM custom_items WHERE id IN (${placeholders})`, ids)
}
async function saveTierList({ id, authorId, gameId, title, description, isPublic, groups, pool }) {
const existing = id ? await findTierListById(id) : null
@@ -582,8 +674,12 @@ module.exports = {
deleteGame,
createCustomItem,
listCustomItems,
findUnusedCustomItems,
listPublicTierLists,
listUserTierLists,
findTierListById,
deleteTierList,
findCustomItemsByIds,
deleteCustomItems,
saveTierList,
}