MessageHttpController.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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 destroyProject(req, res) {
  63. const { projectId } = req.params
  64. logger.log({ projectId }, 'destroying project')
  65. const rooms = await ThreadManager.findAllThreadRoomsAndGlobalThread(projectId)
  66. const roomIds = rooms.map(r => r._id)
  67. logger.log({ projectId, roomIds }, 'deleting all messages in rooms')
  68. await MessageManager.deleteAllMessagesInRooms(roomIds)
  69. logger.log({ projectId }, 'deleting all threads in project')
  70. await ThreadManager.deleteAllThreadsInProject(projectId)
  71. res.sendStatus(204)
  72. }
  73. async function _sendMessage(clientThreadId, req, res) {
  74. const { user_id: userId, content } = req.body
  75. const { projectId } = req.params
  76. if (!ObjectId.isValid(userId)) {
  77. return res.status(400).send('Invalid userId')
  78. }
  79. if (!content) {
  80. return res.status(400).send('No content provided')
  81. }
  82. if (content.length > MAX_MESSAGE_LENGTH) {
  83. return res
  84. .status(400)
  85. .send(`Content too long (> ${MAX_MESSAGE_LENGTH} bytes)`)
  86. }
  87. logger.log(
  88. { clientThreadId, projectId, userId, content },
  89. 'new message received'
  90. )
  91. const thread = await ThreadManager.findOrCreateThread(
  92. projectId,
  93. clientThreadId
  94. )
  95. let message = await MessageManager.createMessage(
  96. thread._id,
  97. userId,
  98. content,
  99. Date.now()
  100. )
  101. message = MessageFormatter.formatMessageForClientSide(message)
  102. message.room_id = projectId
  103. res.status(201).send(message)
  104. }
  105. async function _getMessages(clientThreadId, req, res) {
  106. let before, limit
  107. const { projectId } = req.params
  108. if (req.query.before) {
  109. before = parseInt(req.query.before, 10)
  110. } else {
  111. before = null
  112. }
  113. if (req.query.limit) {
  114. limit = parseInt(req.query.limit, 10)
  115. } else {
  116. limit = DEFAULT_MESSAGE_LIMIT
  117. }
  118. logger.log(
  119. { limit, before, projectId, clientThreadId },
  120. 'get message request received'
  121. )
  122. const thread = await ThreadManager.findOrCreateThread(
  123. projectId,
  124. clientThreadId
  125. )
  126. const threadObjectId = thread._id
  127. logger.log(
  128. { limit, before, projectId, clientThreadId, threadObjectId },
  129. 'found or created thread'
  130. )
  131. let messages = await MessageManager.getMessages(threadObjectId, limit, before)
  132. messages = MessageFormatter.formatMessagesForClientSide(messages)
  133. logger.log({ projectId, messages }, 'got messages')
  134. res.status(200).send(messages)
  135. }
  136. module.exports = {
  137. getGlobalMessages: expressify(getGlobalMessages),
  138. sendGlobalMessage: expressify(sendGlobalMessage),
  139. sendThreadMessage: expressify(sendThreadMessage),
  140. getAllThreads: expressify(getAllThreads),
  141. resolveThread: expressify(resolveThread),
  142. reopenThread: expressify(reopenThread),
  143. deleteThread: expressify(deleteThread),
  144. editMessage: expressify(editMessage),
  145. deleteMessage: expressify(deleteMessage),
  146. destroyProject: expressify(destroyProject),
  147. }