]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/middlewares/validators/videos/video-comments.ts
Fix big play button
[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'
10363c74 5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
f1273314 6import { exists, isBooleanValid, isIdOrUUIDValid, 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'
10363c74 12import { areValidationErrors, doesVideoCommentExist, doesVideoCommentThreadExist, doesVideoExist } 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
C
42const listVideoCommentThreadsValidator = [
43 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
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 = [
56 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
57 param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
58
59 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
d3ea8975 60 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
bf1f6508
C
61
62 if (areValidationErrors(req, res)) return
0f6acda1 63 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
453e83ea 64 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
bf1f6508
C
65
66 return next()
67 }
68]
69
70const addVideoCommentThreadValidator = [
71 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
72 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
73
74 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
5de8a55a 75 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
bf1f6508
C
76
77 if (areValidationErrors(req, res)) return
0f6acda1 78 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea 79 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
a1587156 80 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return
bf1f6508
C
81
82 return next()
83 }
84]
85
86const addVideoCommentReplyValidator = [
87 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
88 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
89 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
90
91 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
5de8a55a 92 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body })
bf1f6508
C
93
94 if (areValidationErrors(req, res)) return
0f6acda1 95 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea
C
96 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
97 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
98 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return
bf1f6508
C
99
100 return next()
101 }
102]
103
da854ddd
C
104const videoCommentGetValidator = [
105 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
106 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
107
108 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
109 logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
110
111 if (areValidationErrors(req, res)) return
0f6acda1 112 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
453e83ea 113 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
da854ddd
C
114
115 return next()
116 }
117]
118
4cb6d457
C
119const removeVideoCommentValidator = [
120 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
121 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
122
123 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
124 logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params })
125
126 if (areValidationErrors(req, res)) return
0f6acda1 127 if (!await doesVideoExist(req.params.videoId, res)) return
453e83ea 128 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
4cb6d457
C
129
130 // Check if the user who did the request is able to delete the video
453e83ea 131 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
4cb6d457
C
132
133 return next()
134 }
135]
136
bf1f6508
C
137// ---------------------------------------------------------------------------
138
139export {
140 listVideoCommentThreadsValidator,
141 listVideoThreadCommentsValidator,
142 addVideoCommentThreadValidator,
0f8d00e3 143 listVideoCommentsValidator,
da854ddd 144 addVideoCommentReplyValidator,
4cb6d457
C
145 videoCommentGetValidator,
146 removeVideoCommentValidator
bf1f6508
C
147}
148
149// ---------------------------------------------------------------------------
150
453e83ea 151function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
47564bbe 152 if (video.commentsEnabled !== true) {
76148b27
RK
153 res.fail({
154 status: HttpStatusCode.CONFLICT_409,
155 message: 'Video comments are disabled for this video.'
156 })
47564bbe
C
157 return false
158 }
159
160 return true
161}
4cb6d457 162
fde37dc9 163function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
c883db6d 164 if (videoComment.isDeleted()) {
76148b27
RK
165 res.fail({
166 status: HttpStatusCode.CONFLICT_409,
167 message: 'This comment is already deleted'
168 })
c883db6d
C
169 return false
170 }
171
fde37dc9
C
172 const userAccount = user.Account
173
174 if (
175 user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && // Not a moderator
176 videoComment.accountId !== userAccount.id && // Not the comment owner
177 videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner
178 ) {
76148b27
RK
179 res.fail({
180 status: HttpStatusCode.FORBIDDEN_403,
181 message: 'Cannot remove video comment of another user'
182 })
4cb6d457
C
183 return false
184 }
185
186 return true
187}
b4055e1c 188
453e83ea 189async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
b4055e1c 190 const acceptParameters = {
453e83ea 191 video,
b4055e1c
C
192 commentBody: req.body,
193 user: res.locals.oauth.token.User
194 }
195
196 let acceptedResult: AcceptResult
197
198 if (isReply) {
453e83ea 199 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
b4055e1c 200
6691c522
C
201 acceptedResult = await Hooks.wrapFun(
202 isLocalVideoCommentReplyAccepted,
203 acceptReplyParameters,
b4055e1c
C
204 'filter:api.video-comment-reply.create.accept.result'
205 )
206 } else {
6691c522
C
207 acceptedResult = await Hooks.wrapFun(
208 isLocalVideoThreadAccepted,
209 acceptParameters,
b4055e1c
C
210 'filter:api.video-thread.create.accept.result'
211 )
212 }
213
214 if (!acceptedResult || acceptedResult.accepted !== true) {
215 logger.info('Refused local comment.', { acceptedResult, acceptParameters })
b4055e1c 216
76148b27
RK
217 res.fail({
218 status: HttpStatusCode.FORBIDDEN_403,
219 message: acceptedResult?.errorMessage || 'Refused local comment'
220 })
b4055e1c
C
221 return false
222 }
223
224 return true
225}