comment.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import express from 'express'
  2. import { ResultList, ThreadsResultList, UserRight, VideoCommentCreate } from '../../../../shared/models'
  3. import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
  4. import { VideoCommentThreads } from '../../../../shared/models/videos/comment/video-comment.model'
  5. import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger'
  6. import { getFormattedObjects } from '../../../helpers/utils'
  7. import { sequelizeTypescript } from '../../../initializers/database'
  8. import { Notifier } from '../../../lib/notifier'
  9. import { Hooks } from '../../../lib/plugins/hooks'
  10. import { buildFormattedCommentTree, createVideoComment, removeComment } from '../../../lib/video-comment'
  11. import {
  12. asyncMiddleware,
  13. asyncRetryTransactionMiddleware,
  14. authenticate,
  15. ensureUserHasRight,
  16. optionalAuthenticate,
  17. paginationValidator,
  18. setDefaultPagination,
  19. setDefaultSort
  20. } from '../../../middlewares'
  21. import {
  22. addVideoCommentReplyValidator,
  23. addVideoCommentThreadValidator,
  24. listVideoCommentsValidator,
  25. listVideoCommentThreadsValidator,
  26. listVideoThreadCommentsValidator,
  27. removeVideoCommentValidator,
  28. videoCommentsValidator,
  29. videoCommentThreadsSortValidator
  30. } from '../../../middlewares/validators'
  31. import { AccountModel } from '../../../models/account/account'
  32. import { VideoCommentModel } from '../../../models/video/video-comment'
  33. const auditLogger = auditLoggerFactory('comments')
  34. const videoCommentRouter = express.Router()
  35. videoCommentRouter.get('/:videoId/comment-threads',
  36. paginationValidator,
  37. videoCommentThreadsSortValidator,
  38. setDefaultSort,
  39. setDefaultPagination,
  40. asyncMiddleware(listVideoCommentThreadsValidator),
  41. optionalAuthenticate,
  42. asyncMiddleware(listVideoThreads)
  43. )
  44. videoCommentRouter.get('/:videoId/comment-threads/:threadId',
  45. asyncMiddleware(listVideoThreadCommentsValidator),
  46. optionalAuthenticate,
  47. asyncMiddleware(listVideoThreadComments)
  48. )
  49. videoCommentRouter.post('/:videoId/comment-threads',
  50. authenticate,
  51. asyncMiddleware(addVideoCommentThreadValidator),
  52. asyncRetryTransactionMiddleware(addVideoCommentThread)
  53. )
  54. videoCommentRouter.post('/:videoId/comments/:commentId',
  55. authenticate,
  56. asyncMiddleware(addVideoCommentReplyValidator),
  57. asyncRetryTransactionMiddleware(addVideoCommentReply)
  58. )
  59. videoCommentRouter.delete('/:videoId/comments/:commentId',
  60. authenticate,
  61. asyncMiddleware(removeVideoCommentValidator),
  62. asyncRetryTransactionMiddleware(removeVideoComment)
  63. )
  64. videoCommentRouter.get('/comments',
  65. authenticate,
  66. ensureUserHasRight(UserRight.SEE_ALL_COMMENTS),
  67. paginationValidator,
  68. videoCommentsValidator,
  69. setDefaultSort,
  70. setDefaultPagination,
  71. listVideoCommentsValidator,
  72. asyncMiddleware(listComments)
  73. )
  74. // ---------------------------------------------------------------------------
  75. export {
  76. videoCommentRouter
  77. }
  78. // ---------------------------------------------------------------------------
  79. async function listComments (req: express.Request, res: express.Response) {
  80. const options = {
  81. start: req.query.start,
  82. count: req.query.count,
  83. sort: req.query.sort,
  84. isLocal: req.query.isLocal,
  85. search: req.query.search,
  86. searchAccount: req.query.searchAccount,
  87. searchVideo: req.query.searchVideo
  88. }
  89. const resultList = await VideoCommentModel.listCommentsForApi(options)
  90. return res.json({
  91. total: resultList.total,
  92. data: resultList.data.map(c => c.toFormattedAdminJSON())
  93. })
  94. }
  95. async function listVideoThreads (req: express.Request, res: express.Response) {
  96. const video = res.locals.onlyVideo
  97. const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
  98. let resultList: ThreadsResultList<VideoCommentModel>
  99. if (video.commentsEnabled === true) {
  100. const apiOptions = await Hooks.wrapObject({
  101. videoId: video.id,
  102. isVideoOwned: video.isOwned(),
  103. start: req.query.start,
  104. count: req.query.count,
  105. sort: req.query.sort,
  106. user
  107. }, 'filter:api.video-threads.list.params')
  108. resultList = await Hooks.wrapPromiseFun(
  109. VideoCommentModel.listThreadsForApi,
  110. apiOptions,
  111. 'filter:api.video-threads.list.result'
  112. )
  113. } else {
  114. resultList = {
  115. total: 0,
  116. totalNotDeletedComments: 0,
  117. data: []
  118. }
  119. }
  120. return res.json({
  121. ...getFormattedObjects(resultList.data, resultList.total),
  122. totalNotDeletedComments: resultList.totalNotDeletedComments
  123. } as VideoCommentThreads)
  124. }
  125. async function listVideoThreadComments (req: express.Request, res: express.Response) {
  126. const video = res.locals.onlyVideo
  127. const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
  128. let resultList: ResultList<VideoCommentModel>
  129. if (video.commentsEnabled === true) {
  130. const apiOptions = await Hooks.wrapObject({
  131. videoId: video.id,
  132. isVideoOwned: video.isOwned(),
  133. threadId: res.locals.videoCommentThread.id,
  134. user
  135. }, 'filter:api.video-thread-comments.list.params')
  136. resultList = await Hooks.wrapPromiseFun(
  137. VideoCommentModel.listThreadCommentsForApi,
  138. apiOptions,
  139. 'filter:api.video-thread-comments.list.result'
  140. )
  141. } else {
  142. resultList = {
  143. total: 0,
  144. data: []
  145. }
  146. }
  147. if (resultList.data.length === 0) {
  148. return res.fail({
  149. status: HttpStatusCode.NOT_FOUND_404,
  150. message: 'No comments were found'
  151. })
  152. }
  153. return res.json(buildFormattedCommentTree(resultList))
  154. }
  155. async function addVideoCommentThread (req: express.Request, res: express.Response) {
  156. const videoCommentInfo: VideoCommentCreate = req.body
  157. const comment = await sequelizeTypescript.transaction(async t => {
  158. const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
  159. return createVideoComment({
  160. text: videoCommentInfo.text,
  161. inReplyToComment: null,
  162. video: res.locals.videoAll,
  163. account
  164. }, t)
  165. })
  166. Notifier.Instance.notifyOnNewComment(comment)
  167. auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
  168. Hooks.runAction('action:api.video-thread.created', { comment })
  169. return res.json({ comment: comment.toFormattedJSON() })
  170. }
  171. async function addVideoCommentReply (req: express.Request, res: express.Response) {
  172. const videoCommentInfo: VideoCommentCreate = req.body
  173. const comment = await sequelizeTypescript.transaction(async t => {
  174. const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
  175. return createVideoComment({
  176. text: videoCommentInfo.text,
  177. inReplyToComment: res.locals.videoCommentFull,
  178. video: res.locals.videoAll,
  179. account
  180. }, t)
  181. })
  182. Notifier.Instance.notifyOnNewComment(comment)
  183. auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
  184. Hooks.runAction('action:api.video-comment-reply.created', { comment })
  185. return res.json({ comment: comment.toFormattedJSON() })
  186. }
  187. async function removeVideoComment (req: express.Request, res: express.Response) {
  188. const videoCommentInstance = res.locals.videoCommentFull
  189. await removeComment(videoCommentInstance)
  190. auditLogger.delete(getAuditIdFromRes(res), new CommentAuditView(videoCommentInstance.toFormattedJSON()))
  191. return res.type('json')
  192. .status(HttpStatusCode.NO_CONTENT_204)
  193. .end()
  194. }