MessageHttpController.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. const logger = require('@overleaf/logger')
  2. const MessageManager = require('./MessageManager')
  3. const MessageFormatter = require('./MessageFormatter')
  4. const ThreadManager = require('../Threads/ThreadManager')
  5. const { ObjectId } = require('../../mongodb')
  6. const { expressify } = require('../../util/promises')
  7. const DEFAULT_MESSAGE_LIMIT = 50
  8. const MAX_MESSAGE_LENGTH = 10 * 1024 // 10kb, about 1,500 words
  9. async function getGlobalMessages(req, res) {
  10. await _getMessages(ThreadManager.GLOBAL_THREAD, req, res)
  11. }
  12. async function sendGlobalMessage(req, res) {
  13. await _sendMessage(ThreadManager.GLOBAL_THREAD, req, res)
  14. }
  15. async function sendThreadMessage(req, res) {
  16. await _sendMessage(req.params.threadId, req, res)
  17. }
  18. async function getAllThreads(req, res) {
  19. const { projectId } = req.params
  20. logger.log({ projectId }, 'getting all threads')
  21. const rooms = await ThreadManager.findAllThreadRooms(projectId)
  22. const roomIds = rooms.map(r => r._id)
  23. const messages = await MessageManager.findAllMessagesInRooms(roomIds)
  24. const threads = MessageFormatter.groupMessagesByThreads(rooms, messages)
  25. res.json(threads)
  26. }
  27. async function resolveThread(req, res) {
  28. const { projectId, threadId } = req.params
  29. const { user_id: userId } = req.body
  30. logger.log({ userId, projectId, threadId }, 'marking thread as resolved')
  31. await ThreadManager.resolveThread(projectId, threadId, userId)
  32. res.sendStatus(204)
  33. }
  34. async function reopenThread(req, res) {
  35. const { projectId, threadId } = req.params
  36. logger.log({ projectId, threadId }, 'reopening thread')
  37. await ThreadManager.reopenThread(projectId, threadId)
  38. res.sendStatus(204)
  39. }
  40. async function deleteThread(req, res) {
  41. const { projectId, threadId } = req.params
  42. logger.log({ projectId, threadId }, 'deleting thread')
  43. const roomId = await ThreadManager.deleteThread(projectId, threadId)
  44. await MessageManager.deleteAllMessagesInRoom(roomId)
  45. res.sendStatus(204)
  46. }
  47. async function editMessage(req, res) {
  48. const { content } = req.body
  49. const { projectId, threadId, messageId } = req.params
  50. logger.log({ projectId, threadId, messageId, content }, 'editing message')
  51. const room = await ThreadManager.findOrCreateThread(projectId, threadId)
  52. await MessageManager.updateMessage(room._id, messageId, content, Date.now())
  53. res.sendStatus(204)
  54. }
  55. async function deleteMessage(req, res) {
  56. const { projectId, threadId, messageId } = req.params
  57. logger.log({ projectId, threadId, messageId }, 'deleting message')
  58. const room = await ThreadManager.findOrCreateThread(projectId, threadId)
  59. await MessageManager.deleteMessage(room._id, messageId)
  60. res.sendStatus(204)
  61. }
  62. async function _sendMessage(clientThreadId, req, res) {
  63. const { user_id: userId, content } = req.body
  64. const { projectId } = req.params
  65. if (!ObjectId.isValid(userId)) {
  66. return res.status(400).send('Invalid userId')
  67. }
  68. if (!content) {
  69. return res.status(400).send('No content provided')
  70. }
  71. if (content.length > MAX_MESSAGE_LENGTH) {
  72. return res
  73. .status(400)
  74. .send(`Content too long (> ${MAX_MESSAGE_LENGTH} bytes)`)
  75. }
  76. logger.log(
  77. { clientThreadId, projectId, userId, content },
  78. 'new message received'
  79. )
  80. const thread = await ThreadManager.findOrCreateThread(
  81. projectId,
  82. clientThreadId
  83. )
  84. let message = await MessageManager.createMessage(
  85. thread._id,
  86. userId,
  87. content,
  88. Date.now()
  89. )
  90. message = MessageFormatter.formatMessageForClientSide(message)
  91. message.room_id = projectId
  92. res.status(201).send(message)
  93. }
  94. async function _getMessages(clientThreadId, req, res) {
  95. let before, limit
  96. const { projectId } = req.params
  97. if (req.query.before) {
  98. before = parseInt(req.query.before, 10)
  99. } else {
  100. before = null
  101. }
  102. if (req.query.limit) {
  103. limit = parseInt(req.query.limit, 10)
  104. } else {
  105. limit = DEFAULT_MESSAGE_LIMIT
  106. }
  107. logger.log(
  108. { limit, before, projectId, clientThreadId },
  109. 'get message request received'
  110. )
  111. const thread = await ThreadManager.findOrCreateThread(
  112. projectId,
  113. clientThreadId
  114. )
  115. const threadObjectId = thread._id
  116. logger.log(
  117. { limit, before, projectId, clientThreadId, threadObjectId },
  118. 'found or created thread'
  119. )
  120. let messages = await MessageManager.getMessages(threadObjectId, limit, before)
  121. messages = MessageFormatter.formatMessagesForClientSide(messages)
  122. logger.log({ projectId, messages }, 'got messages')
  123. res.status(200).send(messages)
  124. }
  125. module.exports = {
  126. getGlobalMessages: expressify(getGlobalMessages),
  127. sendGlobalMessage: expressify(sendGlobalMessage),
  128. sendThreadMessage: expressify(sendThreadMessage),
  129. getAllThreads: expressify(getAllThreads),
  130. resolveThread: expressify(resolveThread),
  131. reopenThread: expressify(reopenThread),
  132. deleteThread: expressify(deleteThread),
  133. editMessage: expressify(editMessage),
  134. deleteMessage: expressify(deleteMessage),
  135. }