댓글 시스템 복구
This commit is contained in:
@@ -16,6 +16,11 @@ const {
|
||||
favoriteTierList,
|
||||
unfavoriteTierList,
|
||||
duplicateTierListForUser,
|
||||
listTierListComments,
|
||||
findTierListCommentById,
|
||||
createTierListComment,
|
||||
deleteTierListComment,
|
||||
createCommentNotificationsForComment,
|
||||
} = require('../db')
|
||||
const { requireAuth } = require('../middleware/auth')
|
||||
const { createMemoryUpload, writeOptimizedImage } = require('../lib/image-storage')
|
||||
@@ -122,6 +127,22 @@ const tierListUpsertSchema = z.object({
|
||||
}
|
||||
})
|
||||
|
||||
const tierListCommentSchema = z.object({
|
||||
content: z.string().trim().min(1).max(2000),
|
||||
parentCommentId: z.string().trim().max(64).optional().default(''),
|
||||
})
|
||||
|
||||
async function getTierListAccessContext(req, tierListId) {
|
||||
const tierList = await findTierListById(tierListId, req.session?.userId || '')
|
||||
if (!tierList) return { error: 'not_found' }
|
||||
if (tierList.isPublic) return { tierList, canRead: true, canEdit: req.session?.userId === tierList.authorId }
|
||||
if (!req.session?.userId) return { error: 'forbidden' }
|
||||
if (req.session.userId === tierList.authorId) return { tierList, canRead: true, canEdit: true }
|
||||
const currentUser = await findUserById(req.session.userId)
|
||||
if (!currentUser?.isAdmin) return { error: 'forbidden' }
|
||||
return { tierList, canRead: true, canEdit: true, isAdmin: true }
|
||||
}
|
||||
|
||||
router.get('/public', async (req, res) => {
|
||||
const topicId = typeof req.query.topicId === 'string' ? req.query.topicId : ''
|
||||
const queryText = typeof req.query.q === 'string' ? req.query.q : ''
|
||||
@@ -142,14 +163,10 @@ router.get('/favorites/me', requireAuth, async (req, res) => {
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res) => {
|
||||
const t = await findTierListById(req.params.id, req.session?.userId || '')
|
||||
if (!t) return res.status(404).json({ error: 'not_found' })
|
||||
if (!t.isPublic) {
|
||||
if (!req.session?.userId) return res.status(403).json({ error: 'forbidden' })
|
||||
const currentUser = req.session.userId === t.authorId ? { isAdmin: false } : await findUserById(req.session.userId)
|
||||
if (req.session.userId !== t.authorId && !currentUser?.isAdmin) return res.status(403).json({ error: 'forbidden' })
|
||||
}
|
||||
res.json({ tierList: normalizeTierList(t) })
|
||||
const access = await getTierListAccessContext(req, req.params.id)
|
||||
if (access.error === 'not_found') return res.status(404).json({ error: 'not_found' })
|
||||
if (access.error === 'forbidden') return res.status(403).json({ error: 'forbidden' })
|
||||
res.json({ tierList: normalizeTierList(access.tierList) })
|
||||
})
|
||||
|
||||
router.post('/:id/duplicate', requireAuth, async (req, res) => {
|
||||
@@ -189,6 +206,62 @@ router.delete('/:id/favorite', requireAuth, async (req, res) => {
|
||||
res.json({ tierList: normalizeTierList(updated) })
|
||||
})
|
||||
|
||||
router.get('/:id/comments', async (req, res) => {
|
||||
const access = await getTierListAccessContext(req, req.params.id)
|
||||
if (access.error === 'not_found') return res.status(404).json({ error: 'not_found' })
|
||||
if (access.error === 'forbidden') return res.status(403).json({ error: 'forbidden' })
|
||||
|
||||
const comments = await listTierListComments(access.tierList.id)
|
||||
res.json({ comments })
|
||||
})
|
||||
|
||||
router.post('/:id/comments', requireAuth, async (req, res) => {
|
||||
const parsed = tierListCommentSchema.safeParse(req.body)
|
||||
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
||||
|
||||
const access = await getTierListAccessContext(req, req.params.id)
|
||||
if (access.error === 'not_found') return res.status(404).json({ error: 'not_found' })
|
||||
if (access.error === 'forbidden') return res.status(403).json({ error: 'forbidden' })
|
||||
|
||||
try {
|
||||
const commentId = await createTierListComment({
|
||||
tierListId: access.tierList.id,
|
||||
authorId: req.session.userId,
|
||||
parentCommentId: parsed.data.parentCommentId,
|
||||
content: parsed.data.content,
|
||||
})
|
||||
await createCommentNotificationsForComment(commentId)
|
||||
const comments = await listTierListComments(access.tierList.id)
|
||||
res.json({ comments, createdCommentId: commentId })
|
||||
} catch (error) {
|
||||
if (error?.code === 'COMMENT_PARENT_INVALID') {
|
||||
return res.status(400).json({ error: 'comment_parent_invalid' })
|
||||
}
|
||||
if (error?.code === 'COMMENT_REPLY_DEPTH_INVALID') {
|
||||
return res.status(400).json({ error: 'comment_reply_depth_invalid' })
|
||||
}
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:id/comments/:commentId', requireAuth, async (req, res) => {
|
||||
const access = await getTierListAccessContext(req, req.params.id)
|
||||
if (access.error === 'not_found') return res.status(404).json({ error: 'not_found' })
|
||||
if (access.error === 'forbidden') return res.status(403).json({ error: 'forbidden' })
|
||||
|
||||
const comment = await findTierListCommentById(req.params.commentId)
|
||||
if (!comment || comment.tierListId !== access.tierList.id) return res.status(404).json({ error: 'not_found' })
|
||||
|
||||
const currentUser = req.session.userId === comment.authorId ? { isAdmin: false } : await findUserById(req.session.userId)
|
||||
if (req.session.userId !== comment.authorId && !currentUser?.isAdmin) {
|
||||
return res.status(403).json({ error: 'forbidden' })
|
||||
}
|
||||
|
||||
await deleteTierListComment(comment.id)
|
||||
const comments = await listTierListComments(access.tierList.id)
|
||||
res.json({ comments })
|
||||
})
|
||||
|
||||
router.post('/custom-items', requireAuth, upload.single('image'), async (req, res) => {
|
||||
if (!req.file) return res.status(400).json({ error: 'file_required' })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user