]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/middlewares/validators/videos/video-comments.ts
Implement video comment list in admin
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / videos / video-comments.ts
1 import * as express from 'express'
2 import { body, param, query } from 'express-validator'
3 import { MUserAccountUrl } from '@server/types/models'
4 import { UserRight } from '../../../../shared'
5 import { exists, isBooleanValid, isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
6 import {
7 doesVideoCommentExist,
8 doesVideoCommentThreadExist,
9 isValidVideoCommentText
10 } from '../../../helpers/custom-validators/video-comments'
11 import { logger } from '../../../helpers/logger'
12 import { doesVideoExist } from '../../../helpers/middlewares'
13 import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
14 import { Hooks } from '../../../lib/plugins/hooks'
15 import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
16 import { areValidationErrors } from '../utils'
17
18 const 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
45 const 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) => {
49 logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
50
51 if (areValidationErrors(req, res)) return
52 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
53
54 return next()
55 }
56 ]
57
58 const 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) => {
63 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
64
65 if (areValidationErrors(req, res)) return
66 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
67 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
68
69 return next()
70 }
71 ]
72
73 const 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) => {
78 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
79
80 if (areValidationErrors(req, res)) return
81 if (!await doesVideoExist(req.params.videoId, res)) return
82 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
83 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return
84
85 return next()
86 }
87 ]
88
89 const 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) => {
95 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body })
96
97 if (areValidationErrors(req, res)) return
98 if (!await doesVideoExist(req.params.videoId, res)) return
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
102
103 return next()
104 }
105 ]
106
107 const 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
115 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
116 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
117
118 return next()
119 }
120 ]
121
122 const 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
130 if (!await doesVideoExist(req.params.videoId, res)) return
131 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
132
133 // Check if the user who did the request is able to delete the video
134 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
135
136 return next()
137 }
138 ]
139
140 // ---------------------------------------------------------------------------
141
142 export {
143 listVideoCommentThreadsValidator,
144 listVideoThreadCommentsValidator,
145 addVideoCommentThreadValidator,
146 listVideoCommentsValidator,
147 addVideoCommentReplyValidator,
148 videoCommentGetValidator,
149 removeVideoCommentValidator
150 }
151
152 // ---------------------------------------------------------------------------
153
154 function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
155 if (video.commentsEnabled !== true) {
156 res.status(409)
157 .json({ error: 'Video comments are disabled for this video.' })
158
159 return false
160 }
161
162 return true
163 }
164
165 function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
166 if (videoComment.isDeleted()) {
167 res.status(409)
168 .json({ error: 'This comment is already deleted' })
169
170 return false
171 }
172
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 ) {
180 res.status(403)
181 .json({ error: 'Cannot remove video comment of another user' })
182
183 return false
184 }
185
186 return true
187 }
188
189 async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
190 const acceptParameters = {
191 video,
192 commentBody: req.body,
193 user: res.locals.oauth.token.User
194 }
195
196 let acceptedResult: AcceptResult
197
198 if (isReply) {
199 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
200
201 acceptedResult = await Hooks.wrapFun(
202 isLocalVideoCommentReplyAccepted,
203 acceptReplyParameters,
204 'filter:api.video-comment-reply.create.accept.result'
205 )
206 } else {
207 acceptedResult = await Hooks.wrapFun(
208 isLocalVideoThreadAccepted,
209 acceptParameters,
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)
217 .json({ error: acceptedResult.errorMessage || 'Refused local comment' })
218
219 return false
220 }
221
222 return true
223 }