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