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