]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/videos/comment.ts
Support studio transcoding in peertube runner
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos / comment.ts
1 import { MCommentFormattable } from '@server/types/models'
2 import express from 'express'
3
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'
13 import {
14 asyncMiddleware,
15 asyncRetryTransactionMiddleware,
16 authenticate,
17 ensureUserHasRight,
18 optionalAuthenticate,
19 paginationValidator,
20 setDefaultPagination,
21 setDefaultSort
22 } from '../../../middlewares'
23 import {
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'
35
36 const auditLogger = auditLoggerFactory('comments')
37 const videoCommentRouter = express.Router()
38
39 videoCommentRouter.get('/:videoId/comment-threads',
40 paginationValidator,
41 videoCommentThreadsSortValidator,
42 setDefaultSort,
43 setDefaultPagination,
44 asyncMiddleware(listVideoCommentThreadsValidator),
45 optionalAuthenticate,
46 asyncMiddleware(listVideoThreads)
47 )
48 videoCommentRouter.get('/:videoId/comment-threads/:threadId',
49 asyncMiddleware(listVideoThreadCommentsValidator),
50 optionalAuthenticate,
51 asyncMiddleware(listVideoThreadComments)
52 )
53
54 videoCommentRouter.post('/:videoId/comment-threads',
55 authenticate,
56 asyncMiddleware(addVideoCommentThreadValidator),
57 asyncRetryTransactionMiddleware(addVideoCommentThread)
58 )
59 videoCommentRouter.post('/:videoId/comments/:commentId',
60 authenticate,
61 asyncMiddleware(addVideoCommentReplyValidator),
62 asyncRetryTransactionMiddleware(addVideoCommentReply)
63 )
64 videoCommentRouter.delete('/:videoId/comments/:commentId',
65 authenticate,
66 asyncMiddleware(removeVideoCommentValidator),
67 asyncRetryTransactionMiddleware(removeVideoComment)
68 )
69
70 videoCommentRouter.get('/comments',
71 authenticate,
72 ensureUserHasRight(UserRight.SEE_ALL_COMMENTS),
73 paginationValidator,
74 videoCommentsValidator,
75 setDefaultSort,
76 setDefaultPagination,
77 listVideoCommentsValidator,
78 asyncMiddleware(listComments)
79 )
80
81 // ---------------------------------------------------------------------------
82
83 export {
84 videoCommentRouter
85 }
86
87 // ---------------------------------------------------------------------------
88
89 async function listComments (req: express.Request, res: express.Response) {
90 const options = {
91 start: req.query.start,
92 count: req.query.count,
93 sort: req.query.sort,
94
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
100 }
101
102 const resultList = await VideoCommentModel.listCommentsForApi(options)
103
104 return res.json({
105 total: resultList.total,
106 data: resultList.data.map(c => c.toFormattedAdminJSON())
107 })
108 }
109
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
113
114 let resultList: ThreadsResultList<MCommentFormattable>
115
116 if (video.commentsEnabled === true) {
117 const apiOptions = await Hooks.wrapObject({
118 videoId: video.id,
119 isVideoOwned: video.isOwned(),
120 start: req.query.start,
121 count: req.query.count,
122 sort: req.query.sort,
123 user
124 }, 'filter:api.video-threads.list.params')
125
126 resultList = await Hooks.wrapPromiseFun(
127 VideoCommentModel.listThreadsForApi,
128 apiOptions,
129 'filter:api.video-threads.list.result'
130 )
131 } else {
132 resultList = {
133 total: 0,
134 totalNotDeletedComments: 0,
135 data: []
136 }
137 }
138
139 return res.json({
140 ...getFormattedObjects(resultList.data, resultList.total),
141 totalNotDeletedComments: resultList.totalNotDeletedComments
142 } as VideoCommentThreads)
143 }
144
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
148
149 let resultList: ResultList<MCommentFormattable>
150
151 if (video.commentsEnabled === true) {
152 const apiOptions = await Hooks.wrapObject({
153 videoId: video.id,
154 threadId: res.locals.videoCommentThread.id,
155 user
156 }, 'filter:api.video-thread-comments.list.params')
157
158 resultList = await Hooks.wrapPromiseFun(
159 VideoCommentModel.listThreadCommentsForApi,
160 apiOptions,
161 'filter:api.video-thread-comments.list.result'
162 )
163 } else {
164 resultList = {
165 total: 0,
166 data: []
167 }
168 }
169
170 if (resultList.data.length === 0) {
171 return res.fail({
172 status: HttpStatusCode.NOT_FOUND_404,
173 message: 'No comments were found'
174 })
175 }
176
177 return res.json(buildFormattedCommentTree(resultList))
178 }
179
180 async function addVideoCommentThread (req: express.Request, res: express.Response) {
181 const videoCommentInfo: VideoCommentCreate = req.body
182
183 const comment = await sequelizeTypescript.transaction(async t => {
184 const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
185
186 return createVideoComment({
187 text: videoCommentInfo.text,
188 inReplyToComment: null,
189 video: res.locals.videoAll,
190 account
191 }, t)
192 })
193
194 Notifier.Instance.notifyOnNewComment(comment)
195 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
196
197 Hooks.runAction('action:api.video-thread.created', { comment, req, res })
198
199 return res.json({ comment: comment.toFormattedJSON() })
200 }
201
202 async function addVideoCommentReply (req: express.Request, res: express.Response) {
203 const videoCommentInfo: VideoCommentCreate = req.body
204
205 const comment = await sequelizeTypescript.transaction(async t => {
206 const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
207
208 return createVideoComment({
209 text: videoCommentInfo.text,
210 inReplyToComment: res.locals.videoCommentFull,
211 video: res.locals.videoAll,
212 account
213 }, t)
214 })
215
216 Notifier.Instance.notifyOnNewComment(comment)
217 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
218
219 Hooks.runAction('action:api.video-comment-reply.created', { comment, req, res })
220
221 return res.json({ comment: comment.toFormattedJSON() })
222 }
223
224 async function removeVideoComment (req: express.Request, res: express.Response) {
225 const videoCommentInstance = res.locals.videoCommentFull
226
227 await removeComment(videoCommentInstance, req, res)
228
229 auditLogger.delete(getAuditIdFromRes(res), new CommentAuditView(videoCommentInstance.toFormattedJSON()))
230
231 return res.type('json')
232 .status(HttpStatusCode.NO_CONTENT_204)
233 .end()
234 }