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