ThreadManager.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { db, ObjectId } from '../../mongodb.js'
  2. export class MissingThreadError extends Error {}
  3. export const GLOBAL_THREAD = 'GLOBAL'
  4. /**
  5. * @param {string} sourceProjectId
  6. * @param {string} targetProjectId
  7. * @return {Promise<{from:ObjectId, to: ObjectId}[]>}
  8. */
  9. export async function cloneThreads(sourceProjectId, targetProjectId) {
  10. sourceProjectId = new ObjectId(sourceProjectId)
  11. targetProjectId = new ObjectId(targetProjectId)
  12. const rooms = await db.rooms
  13. .find({ project_id: sourceProjectId, thread_id: { $exists: true } })
  14. .toArray()
  15. const mapping = []
  16. await db.rooms.insertMany(
  17. rooms.map(room => {
  18. const from = room._id
  19. const to = new ObjectId()
  20. mapping.push({ from, to })
  21. return { ...room, _id: to, project_id: targetProjectId }
  22. })
  23. )
  24. return mapping
  25. }
  26. export async function findOrCreateThread(projectId, threadId) {
  27. let query, update
  28. projectId = new ObjectId(projectId.toString())
  29. if (threadId !== GLOBAL_THREAD) {
  30. threadId = new ObjectId(threadId.toString())
  31. }
  32. if (threadId === GLOBAL_THREAD) {
  33. query = {
  34. project_id: projectId,
  35. thread_id: { $exists: false },
  36. }
  37. update = {
  38. project_id: projectId,
  39. }
  40. } else {
  41. query = {
  42. project_id: projectId,
  43. thread_id: threadId,
  44. }
  45. update = {
  46. project_id: projectId,
  47. thread_id: threadId,
  48. }
  49. }
  50. const result = await db.rooms.findOneAndUpdate(
  51. query,
  52. { $set: update },
  53. { upsert: true, returnDocument: 'after' }
  54. )
  55. return result
  56. }
  57. export async function findAllThreadRooms(projectId) {
  58. return await db.rooms
  59. .find(
  60. {
  61. project_id: new ObjectId(projectId.toString()),
  62. thread_id: { $exists: true },
  63. },
  64. {
  65. thread_id: 1,
  66. resolved: 1,
  67. }
  68. )
  69. .toArray()
  70. }
  71. export async function findAllThreadRoomsAndGlobalThread(projectId) {
  72. return await db.rooms
  73. .find(
  74. {
  75. project_id: new ObjectId(projectId.toString()),
  76. },
  77. {
  78. thread_id: 1,
  79. resolved: 1,
  80. }
  81. )
  82. .toArray()
  83. }
  84. export async function resolveThread(projectId, threadId, userId) {
  85. await db.rooms.updateOne(
  86. {
  87. project_id: new ObjectId(projectId.toString()),
  88. thread_id: new ObjectId(threadId.toString()),
  89. },
  90. {
  91. $set: {
  92. resolved: {
  93. user_id: userId,
  94. ts: new Date(),
  95. },
  96. },
  97. }
  98. )
  99. }
  100. export async function reopenThread(projectId, threadId) {
  101. await db.rooms.updateOne(
  102. {
  103. project_id: new ObjectId(projectId.toString()),
  104. thread_id: new ObjectId(threadId.toString()),
  105. },
  106. {
  107. $unset: {
  108. resolved: true,
  109. },
  110. }
  111. )
  112. }
  113. export async function deleteThread(projectId, threadId) {
  114. const room = await findOrCreateThread(projectId, threadId)
  115. await db.rooms.deleteOne({
  116. _id: room._id,
  117. })
  118. return room._id
  119. }
  120. export async function deleteAllThreadsInProject(projectId) {
  121. await db.rooms.deleteMany({
  122. project_id: new ObjectId(projectId.toString()),
  123. })
  124. }
  125. export async function getResolvedThreadIds(projectId) {
  126. const resolvedThreadIds = await db.rooms
  127. .find(
  128. {
  129. project_id: new ObjectId(projectId),
  130. thread_id: { $exists: true },
  131. resolved: { $exists: true },
  132. },
  133. { projection: { thread_id: 1 } }
  134. )
  135. .map(record => record.thread_id.toString())
  136. .toArray()
  137. return resolvedThreadIds
  138. }
  139. export async function duplicateThread(projectId, threadId) {
  140. const room = await db.rooms.findOne({
  141. project_id: new ObjectId(projectId),
  142. thread_id: new ObjectId(threadId),
  143. })
  144. if (!room) {
  145. throw new MissingThreadError('Trying to duplicate a non-existent thread')
  146. }
  147. const newRoom = {
  148. project_id: room.project_id,
  149. thread_id: new ObjectId(),
  150. }
  151. if (room.resolved) {
  152. newRoom.resolved = room.resolved
  153. }
  154. const confirmation = await db.rooms.insertOne(newRoom)
  155. newRoom._id = confirmation.insertedId
  156. return { oldRoom: room, newRoom }
  157. }
  158. export async function findThread(projectId, threadId) {
  159. projectId = new ObjectId(projectId.toString())
  160. if (threadId !== GLOBAL_THREAD) {
  161. threadId = new ObjectId(threadId.toString())
  162. }
  163. const room = await db.rooms.findOne({
  164. project_id: projectId,
  165. thread_id: threadId === GLOBAL_THREAD ? { $exists: false } : threadId,
  166. })
  167. if (!room) {
  168. throw new MissingThreadError('Thread not found')
  169. }
  170. return room
  171. }
  172. export async function findThreadsById(projectId, threadIds) {
  173. return await db.rooms
  174. .find({
  175. project_id: new ObjectId(projectId),
  176. thread_id: { $in: threadIds.map(id => new ObjectId(id)) },
  177. })
  178. .toArray()
  179. }