1 import { MCommentFormattable } from '@server/types/models'
2 import express from 'express'
4 import { ResultList, ThreadsResultList, UserRight, VideoCommentCreate } from '../../../../shared/models'
5 import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6 import { VideoCommentThreads } from '../../../../shared/models/videos/comment/video-comment.model'
7 import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger'
8 import { getFormattedObjects } from '../../../helpers/utils'
9 import { sequelizeTypescript } from '../../../initializers/database'
10 import { Notifier } from '../../../lib/notifier'
11 import { Hooks } from '../../../lib/plugins/hooks'
12 import { buildFormattedCommentTree, createVideoComment, removeComment } from '../../../lib/video-comment'
15 asyncRetryTransactionMiddleware,
22 } from '../../../middlewares'
24 addVideoCommentReplyValidator,
25 addVideoCommentThreadValidator,
26 listVideoCommentsValidator,
27 listVideoCommentThreadsValidator,
28 listVideoThreadCommentsValidator,
29 removeVideoCommentValidator,
30 videoCommentsValidator,
31 videoCommentThreadsSortValidator
32 } from '../../../middlewares/validators'
33 import { AccountModel } from '../../../models/account/account'
34 import { VideoCommentModel } from '../../../models/video/video-comment'
36 const auditLogger = auditLoggerFactory('comments')
37 const videoCommentRouter = express.Router()
39 videoCommentRouter.get('/:videoId/comment-threads',
41 videoCommentThreadsSortValidator,
44 asyncMiddleware(listVideoCommentThreadsValidator),
46 asyncMiddleware(listVideoThreads)
48 videoCommentRouter.get('/:videoId/comment-threads/:threadId',
49 asyncMiddleware(listVideoThreadCommentsValidator),
51 asyncMiddleware(listVideoThreadComments)
54 videoCommentRouter.post('/:videoId/comment-threads',
56 asyncMiddleware(addVideoCommentThreadValidator),
57 asyncRetryTransactionMiddleware(addVideoCommentThread)
59 videoCommentRouter.post('/:videoId/comments/:commentId',
61 asyncMiddleware(addVideoCommentReplyValidator),
62 asyncRetryTransactionMiddleware(addVideoCommentReply)
64 videoCommentRouter.delete('/:videoId/comments/:commentId',
66 asyncMiddleware(removeVideoCommentValidator),
67 asyncRetryTransactionMiddleware(removeVideoComment)
70 videoCommentRouter.get('/comments',
72 ensureUserHasRight(UserRight.SEE_ALL_COMMENTS),
74 videoCommentsValidator,
77 listVideoCommentsValidator,
78 asyncMiddleware(listComments)
81 // ---------------------------------------------------------------------------
87 // ---------------------------------------------------------------------------
89 async function listComments (req: express.Request, res: express.Response) {
91 start: req.query.start,
92 count: req.query.count,
95 isLocal: req.query.isLocal,
96 onLocalVideo: req.query.onLocalVideo,
97 search: req.query.search,
98 searchAccount: req.query.searchAccount,
99 searchVideo: req.query.searchVideo
102 const resultList = await VideoCommentModel.listCommentsForApi(options)
105 total: resultList.total,
106 data: resultList.data.map(c => c.toFormattedAdminJSON())
110 async function listVideoThreads (req: express.Request, res: express.Response) {
111 const video = res.locals.onlyVideo
112 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
114 let resultList: ThreadsResultList<MCommentFormattable>
116 if (video.commentsEnabled === true) {
117 const apiOptions = await Hooks.wrapObject({
119 isVideoOwned: video.isOwned(),
120 start: req.query.start,
121 count: req.query.count,
122 sort: req.query.sort,
124 }, 'filter:api.video-threads.list.params')
126 resultList = await Hooks.wrapPromiseFun(
127 VideoCommentModel.listThreadsForApi,
129 'filter:api.video-threads.list.result'
134 totalNotDeletedComments: 0,
140 ...getFormattedObjects(resultList.data, resultList.total),
141 totalNotDeletedComments: resultList.totalNotDeletedComments
142 } as VideoCommentThreads)
145 async function listVideoThreadComments (req: express.Request, res: express.Response) {
146 const video = res.locals.onlyVideo
147 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
149 let resultList: ResultList<MCommentFormattable>
151 if (video.commentsEnabled === true) {
152 const apiOptions = await Hooks.wrapObject({
154 threadId: res.locals.videoCommentThread.id,
156 }, 'filter:api.video-thread-comments.list.params')
158 resultList = await Hooks.wrapPromiseFun(
159 VideoCommentModel.listThreadCommentsForApi,
161 'filter:api.video-thread-comments.list.result'
170 if (resultList.data.length === 0) {
172 status: HttpStatusCode.NOT_FOUND_404,
173 message: 'No comments were found'
177 return res.json(buildFormattedCommentTree(resultList))
180 async function addVideoCommentThread (req: express.Request, res: express.Response) {
181 const videoCommentInfo: VideoCommentCreate = req.body
183 const comment = await sequelizeTypescript.transaction(async t => {
184 const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
186 return createVideoComment({
187 text: videoCommentInfo.text,
188 inReplyToComment: null,
189 video: res.locals.videoAll,
194 Notifier.Instance.notifyOnNewComment(comment)
195 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
197 Hooks.runAction('action:api.video-thread.created', { comment, req, res })
199 return res.json({ comment: comment.toFormattedJSON() })
202 async function addVideoCommentReply (req: express.Request, res: express.Response) {
203 const videoCommentInfo: VideoCommentCreate = req.body
205 const comment = await sequelizeTypescript.transaction(async t => {
206 const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
208 return createVideoComment({
209 text: videoCommentInfo.text,
210 inReplyToComment: res.locals.videoCommentFull,
211 video: res.locals.videoAll,
216 Notifier.Instance.notifyOnNewComment(comment)
217 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
219 Hooks.runAction('action:api.video-comment-reply.created', { comment, req, res })
221 return res.json({ comment: comment.toFormattedJSON() })
224 async function removeVideoComment (req: express.Request, res: express.Response) {
225 const videoCommentInstance = res.locals.videoCommentFull
227 await removeComment(videoCommentInstance, req, res)
229 auditLogger.delete(getAuditIdFromRes(res), new CommentAuditView(videoCommentInstance.toFormattedJSON()))
231 return res.type('json')
232 .status(HttpStatusCode.NO_CONTENT_204)