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