]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/videos/video-comments.ts
Implement video comment list in admin
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / videos / video-comments.ts
CommitLineData
bf1f6508 1import * as express from 'express'
0f8d00e3 2import { body, param, query } from 'express-validator'
26d6bf65 3import { MUserAccountUrl } from '@server/types/models'
6e46de09 4import { UserRight } from '../../../../shared'
0f8d00e3 5import { exists, isBooleanValid, isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
57f6896f
C
6import {
7 doesVideoCommentExist,
8 doesVideoCommentThreadExist,
9 isValidVideoCommentText
10} from '../../../helpers/custom-validators/video-comments'
6e46de09 11import { logger } from '../../../helpers/logger'
ceba0e65
C
12import { doesVideoExist } from '../../../helpers/middlewares'
13import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
14import { Hooks } from '../../../lib/plugins/hooks'
57f6896f 15import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
6e46de09 16import { areValidationErrors } from '../utils'
bf1f6508 17
0f8d00e3
C
18const listVideoCommentsValidator = [
19 query('isLocal')
20 .optional()
21 .custom(isBooleanValid)
22 .withMessage('Should have a valid is local boolean'),
23
24 query('search')
25 .optional()
26 .custom(exists).withMessage('Should have a valid search'),
27
28 query('searchAccount')
29 .optional()
30 .custom(exists).withMessage('Should have a valid account search'),
31
32 query('searchVideo')
33 .optional()
34 .custom(exists).withMessage('Should have a valid video search'),
35
36 (req: express.Request, res: express.Response, next: express.NextFunction) => {
37 logger.debug('Checking listVideoCommentsValidator parameters.', { parameters: req.query })
38
39 if (areValidationErrors(req, res)) return
40
41 return next()
42 }
43]
44
bf1f6508
C
45const listVideoCommentThreadsValidator = [
46 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
47
48 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d3ea8975 49 logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
bf1f6508
C
50
51 if (areValidationErrors(req, res)) return
0f6acda1 52 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
bf1f6508
C
53
54 return next()
55 }
56]
57
58const listVideoThreadCommentsValidator = [
59 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
60 param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
61
62 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d3ea8975 63 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
bf1f6508
C
64
65 if (areValidationErrors(req, res)) return
0f6acda1 66 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
453e83ea 67 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
bf1f6508
C
68
69 return next()
70 }
71]
72
73const addVideoCommentThreadValidator = [
74 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
75 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
76
77 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
5de8a55a 78 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
bf1f6508
C
79
80 if (areValidationErrors(req, res)) return
0f6acda1 81 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea 82 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
a1587156 83 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return
bf1f6508
C
84
85 return next()
86 }
87]
88
89const addVideoCommentReplyValidator = [
90 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
91 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
92 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
93
94 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
5de8a55a 95 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body })
bf1f6508
C
96
97 if (areValidationErrors(req, res)) return
0f6acda1 98 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea
C
99 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
100 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
101 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return
bf1f6508
C
102
103 return next()
104 }
105]
106
da854ddd
C
107const videoCommentGetValidator = [
108 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
109 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
110
111 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
112 logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
113
114 if (areValidationErrors(req, res)) return
0f6acda1 115 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
453e83ea 116 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
da854ddd
C
117
118 return next()
119 }
120]
121
4cb6d457
C
122const removeVideoCommentValidator = [
123 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
124 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
125
126 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
127 logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params })
128
129 if (areValidationErrors(req, res)) return
0f6acda1 130 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea 131 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
4cb6d457
C
132
133 // Check if the user who did the request is able to delete the video
453e83ea 134 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
4cb6d457
C
135
136 return next()
137 }
138]
139
bf1f6508
C
140// ---------------------------------------------------------------------------
141
142export {
143 listVideoCommentThreadsValidator,
144 listVideoThreadCommentsValidator,
145 addVideoCommentThreadValidator,
0f8d00e3 146 listVideoCommentsValidator,
da854ddd 147 addVideoCommentReplyValidator,
4cb6d457
C
148 videoCommentGetValidator,
149 removeVideoCommentValidator
bf1f6508
C
150}
151
152// ---------------------------------------------------------------------------
153
453e83ea 154function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
47564bbe
C
155 if (video.commentsEnabled !== true) {
156 res.status(409)
157 .json({ error: 'Video comments are disabled for this video.' })
47564bbe
C
158
159 return false
160 }
161
162 return true
163}
4cb6d457 164
fde37dc9 165function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
c883db6d
C
166 if (videoComment.isDeleted()) {
167 res.status(409)
168 .json({ error: 'This comment is already deleted' })
57f6896f 169
c883db6d
C
170 return false
171 }
172
fde37dc9
C
173 const userAccount = user.Account
174
175 if (
176 user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && // Not a moderator
177 videoComment.accountId !== userAccount.id && // Not the comment owner
178 videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner
179 ) {
4cb6d457
C
180 res.status(403)
181 .json({ error: 'Cannot remove video comment of another user' })
fde37dc9 182
4cb6d457
C
183 return false
184 }
185
186 return true
187}
b4055e1c 188
453e83ea 189async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
b4055e1c 190 const acceptParameters = {
453e83ea 191 video,
b4055e1c
C
192 commentBody: req.body,
193 user: res.locals.oauth.token.User
194 }
195
196 let acceptedResult: AcceptResult
197
198 if (isReply) {
453e83ea 199 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
b4055e1c 200
6691c522
C
201 acceptedResult = await Hooks.wrapFun(
202 isLocalVideoCommentReplyAccepted,
203 acceptReplyParameters,
b4055e1c
C
204 'filter:api.video-comment-reply.create.accept.result'
205 )
206 } else {
6691c522
C
207 acceptedResult = await Hooks.wrapFun(
208 isLocalVideoThreadAccepted,
209 acceptParameters,
b4055e1c
C
210 'filter:api.video-thread.create.accept.result'
211 )
212 }
213
214 if (!acceptedResult || acceptedResult.accepted !== true) {
215 logger.info('Refused local comment.', { acceptedResult, acceptParameters })
216 res.status(403)
57f6896f 217 .json({ error: acceptedResult.errorMessage || 'Refused local comment' })
b4055e1c
C
218
219 return false
220 }
221
222 return true
223}