]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/middlewares/validators/videos/video-comments.ts
Fix typo in error log
[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 not 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 not 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 if (videoComment.isDeleted()) {
193 res.status(409)
194 .json({ error: 'This comment is already deleted' })
195 .end()
196 return false
197 }
198
199 const account = videoComment.Account
200 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) {
201 res.status(403)
202 .json({ error: 'Cannot remove video comment of another user' })
203 .end()
204 return false
205 }
206
207 return true
208 }
209
210 async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
211 const acceptParameters = {
212 video,
213 commentBody: req.body,
214 user: res.locals.oauth.token.User
215 }
216
217 let acceptedResult: AcceptResult
218
219 if (isReply) {
220 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
221
222 acceptedResult = await Hooks.wrapFun(
223 isLocalVideoCommentReplyAccepted,
224 acceptReplyParameters,
225 'filter:api.video-comment-reply.create.accept.result'
226 )
227 } else {
228 acceptedResult = await Hooks.wrapFun(
229 isLocalVideoThreadAccepted,
230 acceptParameters,
231 'filter:api.video-thread.create.accept.result'
232 )
233 }
234
235 if (!acceptedResult || acceptedResult.accepted !== true) {
236 logger.info('Refused local comment.', { acceptedResult, acceptParameters })
237 res.status(403)
238 .json({ error: acceptedResult.errorMessage || 'Refused local comment' })
239
240 return false
241 }
242
243 return true
244 }