업로드 이미지 WebP 최적화 1차 적용
This commit is contained in:
@@ -32,6 +32,7 @@ const {
|
||||
adminDeleteUser,
|
||||
} = require('../db')
|
||||
const { requireAdmin } = require('../middleware/auth')
|
||||
const { createMemoryUpload, writeOptimizedImage } = require('../lib/image-storage')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
@@ -53,21 +54,8 @@ function buildItemLabelFromFilename(file) {
|
||||
return normalized || 'item'
|
||||
}
|
||||
|
||||
const upload = multer({
|
||||
storage: multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, path.join(__dirname, '..', '..', 'uploads', 'games')),
|
||||
filename: (req, file, cb) => cb(null, buildUploadFilename(file)),
|
||||
}),
|
||||
limits: { fileSize: 6 * 1024 * 1024 },
|
||||
})
|
||||
|
||||
const avatarUpload = multer({
|
||||
storage: multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, path.join(__dirname, '..', '..', 'uploads', 'avatars')),
|
||||
filename: (req, file, cb) => cb(null, buildUploadFilename(file)),
|
||||
}),
|
||||
limits: { fileSize: 3 * 1024 * 1024 },
|
||||
})
|
||||
const upload = createMemoryUpload(multer, { fileSize: 8 * 1024 * 1024, maxCount: 50 })
|
||||
const avatarUpload = createMemoryUpload(multer, { fileSize: 4 * 1024 * 1024 })
|
||||
|
||||
router.post('/games', requireAdmin, async (req, res) => {
|
||||
const schema = z.object({ id: z.string().min(1), name: z.string().min(1).max(60) })
|
||||
@@ -97,7 +85,17 @@ router.post('/games/:gameId/thumbnail', requireAdmin, upload.single('thumbnail')
|
||||
if (!req.file) return res.status(400).json({ error: 'file_required' })
|
||||
const game = await findGameById(req.params.gameId)
|
||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||
const updated = await updateGameThumbnail(req.params.gameId, `/uploads/games/${req.file.filename}`)
|
||||
|
||||
const optimized = await writeOptimizedImage({
|
||||
file: req.file,
|
||||
directory: 'games',
|
||||
width: 1280,
|
||||
height: 1280,
|
||||
fit: 'inside',
|
||||
quality: 84,
|
||||
})
|
||||
|
||||
const updated = await updateGameThumbnail(req.params.gameId, optimized.src)
|
||||
res.json({ game: updated })
|
||||
})
|
||||
|
||||
@@ -113,14 +111,23 @@ router.post('/games/:gameId/images', requireAdmin, upload.array('images', 50), a
|
||||
if (normalizedLabels.some((label) => label.length > 60)) return res.status(400).json({ error: 'bad_request' })
|
||||
|
||||
const items = await Promise.all(
|
||||
files.map((file, index) =>
|
||||
createGameItem({
|
||||
files.map(async (file, index) => {
|
||||
const optimized = await writeOptimizedImage({
|
||||
file,
|
||||
directory: 'games',
|
||||
width: 512,
|
||||
height: 512,
|
||||
fit: 'inside',
|
||||
quality: 84,
|
||||
})
|
||||
|
||||
return createGameItem({
|
||||
id: nanoid(),
|
||||
gameId: game.id,
|
||||
src: `/uploads/games/${file.filename}`,
|
||||
src: optimized.src,
|
||||
label: normalizedLabels[index] || buildItemLabelFromFilename(file),
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
res.json({ item: items[0], items })
|
||||
@@ -514,8 +521,18 @@ router.post('/users/:userId/avatar', requireAdmin, avatarUpload.single('avatar')
|
||||
const user = await findUserById(req.params.userId)
|
||||
if (!user) return res.status(404).json({ error: 'not_found' })
|
||||
|
||||
const optimized = req.file
|
||||
? await writeOptimizedImage({
|
||||
file: req.file,
|
||||
directory: 'avatars',
|
||||
width: 512,
|
||||
height: 512,
|
||||
fit: 'cover',
|
||||
quality: 82,
|
||||
})
|
||||
: null
|
||||
const shouldRemoveAvatar = parsed.data.removeAvatar === '1'
|
||||
const nextAvatarSrc = shouldRemoveAvatar ? '' : req.file ? `/uploads/avatars/${req.file.filename}` : user.avatarSrc || ''
|
||||
const nextAvatarSrc = shouldRemoveAvatar ? '' : optimized?.src || user.avatarSrc || ''
|
||||
const updated = await adminUpdateUser({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
|
||||
Reference in New Issue
Block a user