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