]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/videos/video-comments.ts
Fix typo in error log
[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
d5d9b6d7
C
123async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
124 const id = parseInt(idArg + '', 10)
bf1f6508
C
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
d3ea8975 135 if (videoComment.videoId !== video.id) {
bf1f6508 136 res.status(400)
5fbd08be 137 .json({ error: 'Video comment is not associated to this video.' })
bf1f6508
C
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
d5d9b6d7
C
155async function doesVideoCommentExist (idArg: number | string, video: MVideoId, res: express.Response) {
156 const id = parseInt(idArg + '', 10)
da854ddd 157 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
bf1f6508
C
158
159 if (!videoComment) {
160 res.status(404)
161 .json({ error: 'Video comment thread not found' })
162 .end()
163
164 return false
165 }
166
d3ea8975 167 if (videoComment.videoId !== video.id) {
bf1f6508 168 res.status(400)
5fbd08be 169 .json({ error: 'Video comment is not associated to this video.' })
bf1f6508
C
170 .end()
171
172 return false
173 }
174
453e83ea 175 res.locals.videoCommentFull = videoComment
bf1f6508
C
176 return true
177}
47564bbe 178
453e83ea 179function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
47564bbe
C
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}
4cb6d457 190
453e83ea 191function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) {
c883db6d
C
192 if (videoComment.isDeleted()) {
193 res.status(409)
194 .json({ error: 'This comment is already deleted' })
195 .end()
196 return false
197 }
198
4cb6d457
C
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}
b4055e1c 209
453e83ea 210async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
b4055e1c 211 const acceptParameters = {
453e83ea 212 video,
b4055e1c
C
213 commentBody: req.body,
214 user: res.locals.oauth.token.User
215 }
216
217 let acceptedResult: AcceptResult
218
219 if (isReply) {
453e83ea 220 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
b4055e1c 221
6691c522
C
222 acceptedResult = await Hooks.wrapFun(
223 isLocalVideoCommentReplyAccepted,
224 acceptReplyParameters,
b4055e1c
C
225 'filter:api.video-comment-reply.create.accept.result'
226 )
227 } else {
6691c522
C
228 acceptedResult = await Hooks.wrapFun(
229 isLocalVideoThreadAccepted,
230 acceptParameters,
b4055e1c
C
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}