관리자 이미지 최적화 대시보드 추가

This commit is contained in:
2026-03-31 17:58:21 +09:00
parent a19606c516
commit a5c632d9ae
5 changed files with 171 additions and 2 deletions

View File

@@ -705,6 +705,42 @@ async function deleteImageAssets(ids) {
await query(`DELETE FROM image_assets WHERE id IN (${placeholders})`, uniqueIds)
return rows.map(mapImageAssetRow)
}
async function getImageAssetStats() {
const [assetRows, jobRows] = await Promise.all([
query(
`SELECT COUNT(*) AS asset_count, COALESCE(SUM(byte_size), 0) AS total_byte_size, COALESCE(SUM(original_byte_size), 0) AS total_original_byte_size FROM image_assets`
),
query(
`SELECT
COALESCE(SUM(CASE WHEN status = 'queued' THEN 1 ELSE 0 END), 0) AS queued_count,
COALESCE(SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END), 0) AS processing_count,
COALESCE(SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END), 0) AS completed_count,
COALESCE(SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END), 0) AS failed_count,
COALESCE(SUM(CASE WHEN status = 'completed' AND reused_asset = 1 THEN 1 ELSE 0 END), 0) AS reused_count
FROM image_optimization_jobs`
),
])
const asset = assetRows[0] || {}
const jobs = jobRows[0] || {}
const totalByteSize = Number(asset.total_byte_size || 0)
const totalOriginalByteSize = Number(asset.total_original_byte_size || 0)
const savedByteSize = Math.max(0, totalOriginalByteSize - totalByteSize)
return {
assetCount: Number(asset.asset_count || 0),
totalByteSize,
totalOriginalByteSize,
savedByteSize,
savingsRatio: totalOriginalByteSize > 0 ? savedByteSize / totalOriginalByteSize : 0,
queuedCount: Number(jobs.queued_count || 0),
processingCount: Number(jobs.processing_count || 0),
completedCount: Number(jobs.completed_count || 0),
failedCount: Number(jobs.failed_count || 0),
reusedCount: Number(jobs.reused_count || 0),
}
}
async function createGameItem({ id, gameId, src, label }) {
const createdAt = now()
await query('INSERT INTO game_items (id, game_id, src, label, created_at) VALUES (?, ?, ?, ?, ?)', [
@@ -1595,6 +1631,7 @@ module.exports = {
listRecentImageOptimizationJobs,
listUnusedImageAssets,
deleteImageAssets,
getImageAssetStats,
createGameItem,
updateGameItemLabel,
deleteGameItem,

View File

@@ -32,9 +32,11 @@ const {
adminDeleteUser,
listUnusedImageAssets,
deleteImageAssets,
getImageAssetStats,
listRecentImageOptimizationJobs,
} = require('../db')
const { requireAdmin } = require('../middleware/auth')
const { createMemoryUpload, writeOptimizedImage } = require('../lib/image-storage')
const { createMemoryUpload, writeOptimizedImage, getImageOptimizationQueueState } = require('../lib/image-storage')
const router = express.Router()
@@ -248,6 +250,18 @@ router.post('/image-assets/cleanup', requireAdmin, async (req, res) => {
res.json({ deletedCount: deleted.length, assets: deleted })
})
router.get('/image-assets/stats', requireAdmin, async (req, res) => {
const [stats, recentJobs] = await Promise.all([
getImageAssetStats(),
listRecentImageOptimizationJobs(6),
])
res.json({
stats,
queue: getImageOptimizationQueueState(),
recentJobs,
})
})
async function removeCustomItemFiles(items) {
await Promise.all(
items.map(async (item) => {