릴리스: v0.1.23 홈 게임 정렬과 관리자 순서 관리 추가

This commit is contained in:
2026-03-26 14:59:50 +09:00
parent b58a641453
commit c1575783f0
9 changed files with 304 additions and 45 deletions

View File

@@ -46,6 +46,7 @@ function mapGameRow(row) {
id: row.id,
name: row.name,
thumbnailSrc: row.thumbnail_src || '',
displayRank: row.display_rank == null ? null : Number(row.display_rank),
createdAt: Number(row.created_at),
}
}
@@ -154,10 +155,16 @@ async function ensureSchema() {
id VARCHAR(120) PRIMARY KEY,
name VARCHAR(120) NOT NULL,
thumbnail_src VARCHAR(255) NOT NULL DEFAULT '',
display_rank INT NULL DEFAULT NULL,
created_at BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
`)
const displayRankColumns = await query("SHOW COLUMNS FROM games LIKE 'display_rank'")
if (!displayRankColumns.length) {
await query('ALTER TABLE games ADD COLUMN display_rank INT NULL DEFAULT NULL AFTER thumbnail_src')
}
await query(`
CREATE TABLE IF NOT EXISTS game_items (
id VARCHAR(64) PRIMARY KEY,
@@ -326,14 +333,23 @@ async function adminDeleteUser(id) {
async function listGames() {
const rows = await query(
'SELECT id, name, thumbnail_src, created_at FROM games WHERE id <> ? ORDER BY created_at ASC, name ASC',
`
SELECT id, name, thumbnail_src, display_rank, created_at
FROM games
WHERE id <> ?
ORDER BY
CASE WHEN display_rank IS NULL THEN 1 ELSE 0 END ASC,
display_rank ASC,
created_at DESC,
name ASC
`,
[FREEFORM_GAME_ID]
)
return rows.map(mapGameRow)
}
async function findGameById(id) {
const rows = await query('SELECT id, name, thumbnail_src, created_at FROM games WHERE id = ? LIMIT 1', [id])
const rows = await query('SELECT id, name, thumbnail_src, display_rank, created_at FROM games WHERE id = ? LIMIT 1', [id])
return mapGameRow(rows[0])
}
@@ -353,7 +369,13 @@ async function getGameDetail(gameId) {
}
async function createGame({ id, name }) {
await query('INSERT INTO games (id, name, thumbnail_src, created_at) VALUES (?, ?, ?, ?)', [id, name, '', now()])
await query('INSERT INTO games (id, name, thumbnail_src, display_rank, created_at) VALUES (?, ?, ?, ?, ?)', [
id,
name,
'',
null,
now(),
])
return findGameById(id)
}
@@ -411,6 +433,20 @@ async function deleteGame(gameId) {
await query('DELETE FROM games WHERE id = ?', [gameId])
}
async function updateGameDisplayOrder(gameIds) {
const normalizedIds = Array.from(new Set((gameIds || []).filter((id) => id && id !== FREEFORM_GAME_ID))).slice(0, 50)
await query('UPDATE games SET display_rank = NULL WHERE id <> ?', [FREEFORM_GAME_ID])
await Promise.all(
normalizedIds.map((gameId, index) =>
query('UPDATE games SET display_rank = ? WHERE id = ? AND id <> ?', [index + 1, gameId, FREEFORM_GAME_ID])
)
)
return listGames()
}
async function createCustomItem({ id, ownerId, src, label }) {
const createdAt = now()
await query('INSERT INTO custom_items (id, owner_id, src, label, created_at) VALUES (?, ?, ?, ?, ?)', [
@@ -721,6 +757,7 @@ module.exports = {
createGameItem,
deleteGameItem,
deleteGame,
updateGameDisplayOrder,
createCustomItem,
listCustomItems,
findUnusedCustomItems,

View File

@@ -9,10 +9,12 @@ const {
findUserById,
findGameById,
createGame,
listGames,
updateGameThumbnail,
createGameItem,
deleteGameItem,
deleteGame,
updateGameDisplayOrder,
listCustomItems,
findUnusedCustomItems,
findCustomItemsByIds,
@@ -50,6 +52,20 @@ router.post('/games', requireAdmin, async (req, res) => {
res.json({ game })
})
router.patch('/games/display-order', requireAdmin, async (req, res) => {
const schema = z.object({
gameIds: z.array(z.string().min(1)).max(50),
})
const parsed = schema.safeParse(req.body)
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
const games = await listGames()
const validGameIds = new Set(games.map((game) => game.id))
const filteredIds = parsed.data.gameIds.filter((gameId) => validGameIds.has(gameId))
const updatedGames = await updateGameDisplayOrder(filteredIds)
res.json({ games: updatedGames })
})
router.post('/games/:gameId/thumbnail', requireAdmin, upload.single('thumbnail'), async (req, res) => {
if (!req.file) return res.status(400).json({ error: 'file_required' })
const game = await findGameById(req.params.gameId)