]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/videos/video-comments.ts
Merge branch 'release/3.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / videos / video-comments.ts
CommitLineData
bf1f6508 1import * as express from 'express'
0f8d00e3 2import { body, param, query } from 'express-validator'
26d6bf65 3import { MUserAccountUrl } from '@server/types/models'
6e46de09 4import { UserRight } from '../../../../shared'
c0e8b12e 5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
d4a8e7a6 6import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
10363c74 7import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
6e46de09 8import { logger } from '../../../helpers/logger'
ceba0e65
C
9import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
10import { Hooks } from '../../../lib/plugins/hooks'
57f6896f 11import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
d4a8e7a6 12import { areValidationErrors, doesVideoCommentExist, doesVideoCommentThreadExist, doesVideoExist, isValidVideoIdParam } from '../shared'
bf1f6508 13
0f8d00e3
C
14const listVideoCommentsValidator = [
15 query('isLocal')
16 .optional()
f1273314 17 .customSanitizer(toBooleanOrNull)
0f8d00e3
C
18 .custom(isBooleanValid)
19 .withMessage('Should have a valid is local boolean'),
20
21 query('search')
22 .optional()
23 .custom(exists).withMessage('Should have a valid search'),
24
25 query('searchAccount')
26 .optional()
27 .custom(exists).withMessage('Should have a valid account search'),
28
29 query('searchVideo')
30 .optional()
31 .custom(exists).withMessage('Should have a valid video search'),
32
33 (req: express.Request, res: express.Response, next: express.NextFunction) => {
34 logger.debug('Checking listVideoCommentsValidator parameters.', { parameters: req.query })
35
36 if (areValidationErrors(req, res)) return
37
38 return next()
39 }
40]
41
bf1f6508 42const listVideoCommentThreadsValidator = [
d4a8e7a6 43 isValidVideoIdParam('videoId'),
bf1f6508
C
44
45 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d3ea8975 46 logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
bf1f6508
C
47
48 if (areValidationErrors(req, res)) return
0f6acda1 49 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
bf1f6508
C
50
51 return next()
52 }
53]
54
55const listVideoThreadCommentsValidator = [
d4a8e7a6
C
56 isValidVideoIdParam('videoId'),
57
58 param('threadId')
59 .custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
bf1f6508
C
60
61 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d3ea8975 62 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
bf1f6508
C
63
64 if (areValidationErrors(req, res)) return
0f6acda1 65 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
453e83ea 66 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
bf1f6508
C
67
68 return next()
69 }
70]
71
72const addVideoCommentThreadValidator = [
d4a8e7a6
C
73 isValidVideoIdParam('videoId'),
74
75 body('text')
76 .custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
bf1f6508
C
77
78 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
5de8a55a 79 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
bf1f6508
C
80
81 if (areValidationErrors(req, res)) return
0f6acda1 82 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea 83 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
a1587156 84 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return
bf1f6508
C
85
86 return next()
87 }
88]
89
90const addVideoCommentReplyValidator = [
d4a8e7a6
C
91 isValidVideoIdParam('videoId'),
92
bf1f6508 93 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
d4a8e7a6 94
bf1f6508
C
95 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
96
97 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
5de8a55a 98 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body })
bf1f6508
C
99
100 if (areValidationErrors(req, res)) return
0f6acda1 101 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea
C
102 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
103 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
104 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return
bf1f6508
C
105
106 return next()
107 }
108]
109
da854ddd 110const videoCommentGetValidator = [
d4a8e7a6
C
111 isValidVideoIdParam('videoId'),
112
113 param('commentId')
114 .custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
da854ddd
C
115
116 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
117 logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
118
119 if (areValidationErrors(req, res)) return
0f6acda1 120 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
453e83ea 121 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
da854ddd
C
122
123 return next()
124 }
125]
126
4cb6d457 127const removeVideoCommentValidator = [
d4a8e7a6
C
128 isValidVideoIdParam('videoId'),
129
4cb6d457
C
130 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
131
132 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
133 logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params })
134
135 if (areValidationErrors(req, res)) return
0f6acda1 136 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea 137 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
4cb6d457
C
138
139 // Check if the user who did the request is able to delete the video
453e83ea 140 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
4cb6d457
C
141
142 return next()
143 }
144]
145
bf1f6508
C
146// ---------------------------------------------------------------------------
147
148export {
149 listVideoCommentThreadsValidator,
150 listVideoThreadCommentsValidator,
151 addVideoCommentThreadValidator,
0f8d00e3 152 listVideoCommentsValidator,
da854ddd 153 addVideoCommentReplyValidator,
4cb6d457
C
154 videoCommentGetValidator,
155 removeVideoCommentValidator
bf1f6508
C
156}
157
158// ---------------------------------------------------------------------------
159
453e83ea 160function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
47564bbe 161 if (video.commentsEnabled !== true) {
76148b27
RK
162 res.fail({
163 status: HttpStatusCode.CONFLICT_409,
164 message: 'Video comments are disabled for this video.'
165 })
47564bbe
C
166 return false
167 }
168
169 return true
170}
4cb6d457 171
fde37dc9 172function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
c883db6d 173 if (videoComment.isDeleted()) {
76148b27
RK
174 res.fail({
175 status: HttpStatusCode.CONFLICT_409,
176 message: 'This comment is already deleted'
177 })
c883db6d
C
178 return false
179 }
180
fde37dc9
C
181 const userAccount = user.Account
182
183 if (
184 user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && // Not a moderator
185 videoComment.accountId !== userAccount.id && // Not the comment owner
186 videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner
187 ) {
76148b27
RK
188 res.fail({
189 status: HttpStatusCode.FORBIDDEN_403,
190 message: 'Cannot remove video comment of another user'
191 })
4cb6d457
C
192 return false
193 }
194
195 return true
196}
b4055e1c 197
453e83ea 198async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
b4055e1c 199 const acceptParameters = {
453e83ea 200 video,
b4055e1c
C
201 commentBody: req.body,
202 user: res.locals.oauth.token.User
203 }
204
205 let acceptedResult: AcceptResult
206
207 if (isReply) {
453e83ea 208 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
b4055e1c 209
6691c522
C
210 acceptedResult = await Hooks.wrapFun(
211 isLocalVideoCommentReplyAccepted,
212 acceptReplyParameters,
b4055e1c
C
213 'filter:api.video-comment-reply.create.accept.result'
214 )
215 } else {
6691c522
C
216 acceptedResult = await Hooks.wrapFun(
217 isLocalVideoThreadAccepted,
218 acceptParameters,
b4055e1c
C
219 'filter:api.video-thread.create.accept.result'
220 )
221 }
222
223 if (!acceptedResult || acceptedResult.accepted !== true) {
224 logger.info('Refused local comment.', { acceptedResult, acceptParameters })
b4055e1c 225
76148b27
RK
226 res.fail({
227 status: HttpStatusCode.FORBIDDEN_403,
228 message: acceptedResult?.errorMessage || 'Refused local comment'
229 })
b4055e1c
C
230 return false
231 }
232
233 return true
234}