1 import * as express from 'express'
2 import { body, param } from 'express-validator'
3 import { MUserAccountUrl } from '@server/types/models'
4 import { UserRight } from '../../../../shared'
5 import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
8 doesVideoCommentThreadExist,
9 isValidVideoCommentText
10 } from '../../../helpers/custom-validators/video-comments'
11 import { logger } from '../../../helpers/logger'
12 import { doesVideoExist } from '../../../helpers/middlewares'
13 import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
14 import { Hooks } from '../../../lib/plugins/hooks'
15 import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
16 import { areValidationErrors } from '../utils'
18 const listVideoCommentThreadsValidator = [
19 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
21 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
22 logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
24 if (areValidationErrors(req, res)) return
25 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
31 const listVideoThreadCommentsValidator = [
32 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
33 param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
35 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
36 logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
38 if (areValidationErrors(req, res)) return
39 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
40 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
46 const addVideoCommentThreadValidator = [
47 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
48 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
50 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
51 logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body })
53 if (areValidationErrors(req, res)) return
54 if (!await doesVideoExist(req.params.videoId, res)) return
55 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
56 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, false)) return
62 const addVideoCommentReplyValidator = [
63 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
64 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
65 body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
67 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
68 logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body })
70 if (areValidationErrors(req, res)) return
71 if (!await doesVideoExist(req.params.videoId, res)) return
72 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
73 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
74 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return
80 const videoCommentGetValidator = [
81 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
82 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
84 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
85 logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
87 if (areValidationErrors(req, res)) return
88 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
89 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
95 const removeVideoCommentValidator = [
96 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
97 param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'),
99 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
100 logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params })
102 if (areValidationErrors(req, res)) return
103 if (!await doesVideoExist(req.params.videoId, res)) return
104 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
106 // Check if the user who did the request is able to delete the video
107 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
113 // ---------------------------------------------------------------------------
116 listVideoCommentThreadsValidator,
117 listVideoThreadCommentsValidator,
118 addVideoCommentThreadValidator,
119 addVideoCommentReplyValidator,
120 videoCommentGetValidator,
121 removeVideoCommentValidator
124 // ---------------------------------------------------------------------------
126 function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
127 if (video.commentsEnabled !== true) {
129 .json({ error: 'Video comments are disabled for this video.' })
137 function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
138 if (videoComment.isDeleted()) {
140 .json({ error: 'This comment is already deleted' })
145 const userAccount = user.Account
148 user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && // Not a moderator
149 videoComment.accountId !== userAccount.id && // Not the comment owner
150 videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner
153 .json({ error: 'Cannot remove video comment of another user' })
161 async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
162 const acceptParameters = {
164 commentBody: req.body,
165 user: res.locals.oauth.token.User
168 let acceptedResult: AcceptResult
171 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
173 acceptedResult = await Hooks.wrapFun(
174 isLocalVideoCommentReplyAccepted,
175 acceptReplyParameters,
176 'filter:api.video-comment-reply.create.accept.result'
179 acceptedResult = await Hooks.wrapFun(
180 isLocalVideoThreadAccepted,
182 'filter:api.video-thread.create.accept.result'
186 if (!acceptedResult || acceptedResult.accepted !== true) {
187 logger.info('Refused local comment.', { acceptedResult, acceptParameters })
189 .json({ error: acceptedResult.errorMessage || 'Refused local comment' })