릴리스: v1.3.13 템플릿 요청 스냅샷과 저장 분리
This commit is contained in:
@@ -58,6 +58,33 @@ function getCustomTemplateItems(tierList) {
|
||||
const upload = createMemoryUpload(multer, { fileSize: 6 * 1024 * 1024 })
|
||||
const thumbnailUpload = createMemoryUpload(multer, { fileSize: 8 * 1024 * 1024 })
|
||||
|
||||
const templateRequestSchema = z.object({
|
||||
type: z.enum(['create', 'update']),
|
||||
sourceTierListId: z.string().max(64).optional().default(''),
|
||||
gameId: z.string().min(1).max(120),
|
||||
requestTitle: z.string().trim().min(1).max(120),
|
||||
requestDescription: z.string().trim().min(1).max(1000),
|
||||
thumbnailSrc: z.string().max(255).optional().default(''),
|
||||
isPublic: z.boolean().optional().default(false),
|
||||
showCharacterNames: z.boolean().optional().default(false),
|
||||
saveToMyTierList: z.boolean().optional().default(true),
|
||||
groups: z.array(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1).max(16),
|
||||
itemIds: z.array(z.string()),
|
||||
})
|
||||
),
|
||||
boardItems: z.array(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
src: z.string().min(1),
|
||||
label: z.string().min(1).max(60),
|
||||
origin: z.enum(['game', 'custom']).default('game'),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
const tierListUpsertSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
gameId: z.string().min(1),
|
||||
@@ -194,42 +221,64 @@ router.post('/thumbnail', requireAuth, thumbnailUpload.single('thumbnail'), asyn
|
||||
res.json({ thumbnailSrc: optimized.src })
|
||||
})
|
||||
|
||||
router.post('/:id/template-request', requireAuth, async (req, res) => {
|
||||
const schema = z.object({
|
||||
type: z.enum(['create', 'update']),
|
||||
requestTitle: z.string().trim().min(1).max(80),
|
||||
requestDescription: z.string().trim().min(1).max(240),
|
||||
})
|
||||
const parsed = schema.safeParse(req.body)
|
||||
router.post('/template-request', requireAuth, async (req, res) => {
|
||||
const parsed = templateRequestSchema.safeParse(req.body)
|
||||
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
||||
|
||||
const tierList = await findTierListById(req.params.id, req.session.userId)
|
||||
if (!tierList) return res.status(404).json({ error: 'not_found' })
|
||||
if (tierList.authorId !== req.session.userId) return res.status(403).json({ error: 'forbidden' })
|
||||
|
||||
const customItems = getCustomTemplateItems(tierList)
|
||||
const payload = parsed.data
|
||||
const normalizedBoardItems = payload.boardItems.map(normalizePoolItem)
|
||||
const customItems = normalizedBoardItems.filter((item) => item?.origin === 'custom')
|
||||
if (!customItems.length) return res.status(400).json({ error: 'custom_items_required' })
|
||||
|
||||
if (parsed.data.type === 'create') {
|
||||
if (tierList.gameId !== FREEFORM_GAME_ID) return res.status(400).json({ error: 'freeform_required' })
|
||||
} else {
|
||||
if (tierList.gameId === FREEFORM_GAME_ID) return res.status(400).json({ error: 'game_template_required' })
|
||||
if (payload.type === 'create') {
|
||||
if (payload.gameId !== FREEFORM_GAME_ID) return res.status(400).json({ error: 'freeform_required' })
|
||||
} else if (payload.gameId === FREEFORM_GAME_ID) {
|
||||
return res.status(400).json({ error: 'game_template_required' })
|
||||
}
|
||||
|
||||
let sourceTierList = null
|
||||
if (payload.sourceTierListId) {
|
||||
sourceTierList = await findTierListById(payload.sourceTierListId, req.session.userId)
|
||||
if (!sourceTierList) return res.status(404).json({ error: 'not_found' })
|
||||
if (sourceTierList.authorId !== req.session.userId) return res.status(403).json({ error: 'forbidden' })
|
||||
}
|
||||
|
||||
let savedTierList = null
|
||||
if (payload.saveToMyTierList) {
|
||||
savedTierList = await saveTierList({
|
||||
id: sourceTierList?.id || undefined,
|
||||
authorId: req.session.userId,
|
||||
gameId: payload.gameId,
|
||||
title: payload.requestTitle,
|
||||
thumbnailSrc: payload.thumbnailSrc || '',
|
||||
description: payload.requestDescription || '',
|
||||
isPublic: !!payload.isPublic,
|
||||
showCharacterNames: !!payload.showCharacterNames,
|
||||
sourceTierListId: sourceTierList?.sourceTierListId || '',
|
||||
sourceSnapshotTitle: sourceTierList?.sourceSnapshotTitle || '',
|
||||
sourceSnapshotAuthor: sourceTierList?.sourceSnapshotAuthor || '',
|
||||
groups: payload.groups,
|
||||
pool: normalizedBoardItems,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const request = await createTemplateRequest({
|
||||
id: nanoid(),
|
||||
type: parsed.data.type,
|
||||
type: payload.type,
|
||||
requesterId: req.session.userId,
|
||||
sourceTierListId: tierList.id,
|
||||
sourceGameId: tierList.gameId,
|
||||
targetGameId: parsed.data.type === 'update' ? tierList.gameId : '',
|
||||
title: parsed.data.requestTitle,
|
||||
description: parsed.data.requestDescription,
|
||||
thumbnailSrc: tierList.thumbnailSrc || '',
|
||||
sourceTierListId: savedTierList?.id || sourceTierList?.id || '',
|
||||
sourceGameId: payload.gameId,
|
||||
targetGameId: payload.type === 'update' ? payload.gameId : '',
|
||||
title: payload.requestTitle,
|
||||
description: payload.requestDescription,
|
||||
thumbnailSrc: payload.thumbnailSrc || '',
|
||||
items: customItems,
|
||||
groups: payload.groups,
|
||||
boardItems: normalizedBoardItems,
|
||||
showCharacterNames: !!payload.showCharacterNames,
|
||||
})
|
||||
return res.json({ request })
|
||||
return res.json({ request, savedTierList: savedTierList ? normalizeTierList(savedTierList) : null })
|
||||
} catch (e) {
|
||||
if (e?.code === 'TEMPLATE_REQUEST_EXISTS') {
|
||||
return res.status(409).json({ error: 'template_request_exists' })
|
||||
|
||||
Reference in New Issue
Block a user