@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4393.1.de282b5dfd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
  3. package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
  4. package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
  5. package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
  6. package/dist/generated/entities/channel_thread_token/index.js +17 -0
  7. package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
  8. package/dist/generated/entities/communication_channel/index.js +43 -0
  9. package/dist/generated/entities/communication_channel/index.js.map +7 -0
  10. package/dist/generated/entities/customer_interaction/index.js +4 -0
  11. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  12. package/dist/generated/entities/external_conversation/index.js +25 -0
  13. package/dist/generated/entities/external_conversation/index.js.map +7 -0
  14. package/dist/generated/entities/external_message/index.js +25 -0
  15. package/dist/generated/entities/external_message/index.js.map +7 -0
  16. package/dist/generated/entities/integration_credentials/index.js +3 -1
  17. package/dist/generated/entities/integration_credentials/index.js.map +2 -2
  18. package/dist/generated/entities/message/index.js +2 -0
  19. package/dist/generated/entities/message/index.js.map +2 -2
  20. package/dist/generated/entities/message_channel_link/index.js +33 -0
  21. package/dist/generated/entities/message_channel_link/index.js.map +7 -0
  22. package/dist/generated/entities/message_reaction/index.js +25 -0
  23. package/dist/generated/entities/message_reaction/index.js.map +7 -0
  24. package/dist/generated/entities.ids.generated.js +11 -0
  25. package/dist/generated/entities.ids.generated.js.map +2 -2
  26. package/dist/generated/entity-fields-registry.js +117 -0
  27. package/dist/generated/entity-fields-registry.js.map +2 -2
  28. package/dist/helpers/integration/authFixtures.js +2 -1
  29. package/dist/helpers/integration/authFixtures.js.map +2 -2
  30. package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
  31. package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
  32. package/dist/modules/communication_channels/acl.js +47 -0
  33. package/dist/modules/communication_channels/acl.js.map +7 -0
  34. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
  35. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
  36. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
  37. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
  38. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
  39. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
  40. package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
  41. package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
  42. package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
  43. package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
  44. package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
  45. package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
  46. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
  47. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
  48. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
  49. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
  50. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
  51. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
  52. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
  53. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
  54. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
  55. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
  56. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
  57. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
  58. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
  59. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
  60. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
  61. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
  62. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
  63. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
  64. package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
  65. package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
  66. package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
  67. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
  68. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
  69. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
  70. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
  71. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
  72. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
  73. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
  74. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
  75. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
  76. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
  77. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
  78. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
  79. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
  80. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
  81. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
  82. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
  83. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
  84. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
  85. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
  86. package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
  87. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
  88. package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
  89. package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
  90. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
  91. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
  92. package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
  93. package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
  94. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
  95. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
  96. package/dist/modules/communication_channels/commands/interceptors.js +68 -0
  97. package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
  98. package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
  99. package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
  100. package/dist/modules/communication_channels/commands/push-register.js +146 -0
  101. package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
  102. package/dist/modules/communication_channels/commands/push-renew.js +23 -0
  103. package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
  104. package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
  105. package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
  106. package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
  107. package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
  108. package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
  109. package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
  110. package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
  111. package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
  112. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
  113. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
  114. package/dist/modules/communication_channels/data/enrichers.js +286 -0
  115. package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
  116. package/dist/modules/communication_channels/data/entities.js +447 -0
  117. package/dist/modules/communication_channels/data/entities.js.map +7 -0
  118. package/dist/modules/communication_channels/data/extensions.js +67 -0
  119. package/dist/modules/communication_channels/data/extensions.js.map +7 -0
  120. package/dist/modules/communication_channels/data/validators.js +123 -0
  121. package/dist/modules/communication_channels/data/validators.js.map +7 -0
  122. package/dist/modules/communication_channels/di.js +35 -0
  123. package/dist/modules/communication_channels/di.js.map +7 -0
  124. package/dist/modules/communication_channels/encryption.js +12 -0
  125. package/dist/modules/communication_channels/encryption.js.map +7 -0
  126. package/dist/modules/communication_channels/events.js +124 -0
  127. package/dist/modules/communication_channels/events.js.map +7 -0
  128. package/dist/modules/communication_channels/index.js +20 -0
  129. package/dist/modules/communication_channels/index.js.map +7 -0
  130. package/dist/modules/communication_channels/lib/access-control.js +43 -0
  131. package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
  132. package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
  133. package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
  134. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
  135. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
  136. package/dist/modules/communication_channels/lib/adapter.js +1 -0
  137. package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
  138. package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
  139. package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
  140. package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
  141. package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
  142. package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
  143. package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
  144. package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
  145. package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
  146. package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
  147. package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
  148. package/dist/modules/communication_channels/lib/email-contact.js +14 -0
  149. package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
  150. package/dist/modules/communication_channels/lib/email-mime.js +259 -0
  151. package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
  152. package/dist/modules/communication_channels/lib/error-classification.js +101 -0
  153. package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
  154. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
  155. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
  156. package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
  157. package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
  158. package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
  159. package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
  160. package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
  161. package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
  162. package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
  163. package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
  164. package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
  165. package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
  166. package/dist/modules/communication_channels/lib/provider-health.js +24 -0
  167. package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
  168. package/dist/modules/communication_channels/lib/push-state.js +19 -0
  169. package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
  170. package/dist/modules/communication_channels/lib/queue.js +54 -0
  171. package/dist/modules/communication_channels/lib/queue.js.map +7 -0
  172. package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
  173. package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
  174. package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
  175. package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
  176. package/dist/modules/communication_channels/lib/registry.js +67 -0
  177. package/dist/modules/communication_channels/lib/registry.js.map +7 -0
  178. package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
  179. package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
  180. package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
  181. package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
  182. package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
  183. package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
  184. package/dist/modules/communication_channels/lib/system-user.js +22 -0
  185. package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
  186. package/dist/modules/communication_channels/lib/test-seed.js +68 -0
  187. package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
  188. package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
  189. package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
  190. package/dist/modules/communication_channels/lib/thread-token.js +219 -0
  191. package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
  192. package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
  193. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
  194. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
  195. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
  196. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
  197. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
  198. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
  199. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
  200. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
  201. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
  202. package/dist/modules/communication_channels/notifications.client.js +51 -0
  203. package/dist/modules/communication_channels/notifications.client.js.map +7 -0
  204. package/dist/modules/communication_channels/notifications.handlers.js +53 -0
  205. package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
  206. package/dist/modules/communication_channels/notifications.js +56 -0
  207. package/dist/modules/communication_channels/notifications.js.map +7 -0
  208. package/dist/modules/communication_channels/setup.js +105 -0
  209. package/dist/modules/communication_channels/setup.js.map +7 -0
  210. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
  211. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
  212. package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
  213. package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
  214. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
  215. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
  216. package/dist/modules/communication_channels/widgets/components.js +7 -0
  217. package/dist/modules/communication_channels/widgets/components.js.map +7 -0
  218. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
  219. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
  220. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
  221. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
  222. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
  223. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
  224. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
  225. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
  226. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
  227. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
  228. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
  229. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
  230. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
  231. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
  232. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
  233. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
  234. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
  235. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
  236. package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
  237. package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
  238. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
  239. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
  240. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
  241. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
  242. package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
  243. package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
  244. package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
  245. package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
  246. package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
  247. package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
  248. package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
  249. package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
  250. package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
  251. package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
  252. package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
  253. package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
  254. package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
  255. package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
  256. package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
  257. package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
  258. package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
  259. package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
  260. package/dist/modules/customers/acl.js +18 -0
  261. package/dist/modules/customers/acl.js.map +2 -2
  262. package/dist/modules/customers/api/activities/route.js +9 -0
  263. package/dist/modules/customers/api/activities/route.js.map +2 -2
  264. package/dist/modules/customers/api/companies/[id]/route.js +18 -7
  265. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  266. package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
  267. package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
  268. package/dist/modules/customers/api/interactions/counts/route.js +6 -0
  269. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  270. package/dist/modules/customers/api/interactions/route.js +26 -7
  271. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  272. package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
  273. package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
  274. package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
  275. package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
  276. package/dist/modules/customers/api/people/[id]/route.js +12 -4
  277. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  278. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
  279. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  280. package/dist/modules/customers/commands/deals.js +46 -5
  281. package/dist/modules/customers/commands/deals.js.map +2 -2
  282. package/dist/modules/customers/commands/interactions.js +16 -0
  283. package/dist/modules/customers/commands/interactions.js.map +2 -2
  284. package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
  285. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  286. package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
  287. package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
  288. package/dist/modules/customers/components/detail/DealForm.js +2 -1
  289. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  290. package/dist/modules/customers/components/detail/DealsSection.js +10 -0
  291. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  292. package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
  293. package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
  294. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
  295. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
  296. package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
  297. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  298. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
  299. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
  300. package/dist/modules/customers/data/enrichers.js +133 -2
  301. package/dist/modules/customers/data/enrichers.js.map +2 -2
  302. package/dist/modules/customers/data/entities.js +18 -0
  303. package/dist/modules/customers/data/entities.js.map +2 -2
  304. package/dist/modules/customers/data/extensions.js +16 -0
  305. package/dist/modules/customers/data/extensions.js.map +7 -0
  306. package/dist/modules/customers/encryption.js +11 -0
  307. package/dist/modules/customers/encryption.js.map +2 -2
  308. package/dist/modules/customers/events.js +4 -1
  309. package/dist/modules/customers/events.js.map +2 -2
  310. package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
  311. package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
  312. package/dist/modules/customers/lib/kysely.js.map +2 -2
  313. package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
  314. package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
  315. package/dist/modules/customers/lib/personEmailThreads.js +205 -0
  316. package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
  317. package/dist/modules/customers/lib/visibilityFilter.js +51 -0
  318. package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
  319. package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
  320. package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
  321. package/dist/modules/customers/setup.js +2 -1
  322. package/dist/modules/customers/setup.js.map +2 -2
  323. package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
  324. package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
  325. package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
  326. package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
  327. package/dist/modules/integrations/data/entities.js +8 -1
  328. package/dist/modules/integrations/data/entities.js.map +2 -2
  329. package/dist/modules/integrations/lib/credentials-service.js +29 -14
  330. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  331. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
  332. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
  333. package/dist/modules/messages/commands/messages.js +70 -8
  334. package/dist/modules/messages/commands/messages.js.map +2 -2
  335. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
  336. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  337. package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
  338. package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
  339. package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
  340. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  341. package/dist/modules/messages/data/entities.js +8 -1
  342. package/dist/modules/messages/data/entities.js.map +2 -2
  343. package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
  344. package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
  345. package/dist/modules/messages/widgets/injection-table.js +7 -0
  346. package/dist/modules/messages/widgets/injection-table.js.map +7 -0
  347. package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
  348. package/generated/entities/channel_thread_mapping/index.ts +11 -0
  349. package/generated/entities/channel_thread_token/index.ts +7 -0
  350. package/generated/entities/communication_channel/index.ts +20 -0
  351. package/generated/entities/customer_interaction/index.ts +2 -0
  352. package/generated/entities/external_conversation/index.ts +11 -0
  353. package/generated/entities/external_message/index.ts +11 -0
  354. package/generated/entities/integration_credentials/index.ts +1 -0
  355. package/generated/entities/message/index.ts +1 -0
  356. package/generated/entities/message_channel_link/index.ts +15 -0
  357. package/generated/entities/message_reaction/index.ts +11 -0
  358. package/generated/entities.ids.generated.ts +11 -0
  359. package/generated/entity-fields-registry.ts +117 -0
  360. package/package.json +9 -7
  361. package/src/helpers/integration/authFixtures.ts +4 -1
  362. package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
  363. package/src/modules/communication_channels/acl.ts +43 -0
  364. package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
  365. package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
  366. package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
  367. package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
  368. package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
  369. package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
  370. package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
  371. package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
  372. package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
  373. package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
  374. package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
  375. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
  376. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
  377. package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
  378. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
  379. package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
  380. package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
  381. package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
  382. package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
  383. package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
  384. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
  385. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
  386. package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
  387. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
  388. package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
  389. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
  390. package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
  391. package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
  392. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
  393. package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
  394. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
  395. package/src/modules/communication_channels/commands/interceptors.ts +104 -0
  396. package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
  397. package/src/modules/communication_channels/commands/push-register.ts +203 -0
  398. package/src/modules/communication_channels/commands/push-renew.ts +49 -0
  399. package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
  400. package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
  401. package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
  402. package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
  403. package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
  404. package/src/modules/communication_channels/data/enrichers.ts +413 -0
  405. package/src/modules/communication_channels/data/entities.ts +546 -0
  406. package/src/modules/communication_channels/data/extensions.ts +76 -0
  407. package/src/modules/communication_channels/data/validators.ts +138 -0
  408. package/src/modules/communication_channels/di.ts +40 -0
  409. package/src/modules/communication_channels/encryption.ts +44 -0
  410. package/src/modules/communication_channels/events.ts +122 -0
  411. package/src/modules/communication_channels/i18n/de.json +138 -0
  412. package/src/modules/communication_channels/i18n/en.json +138 -0
  413. package/src/modules/communication_channels/i18n/es.json +138 -0
  414. package/src/modules/communication_channels/i18n/pl.json +138 -0
  415. package/src/modules/communication_channels/index.ts +19 -0
  416. package/src/modules/communication_channels/lib/access-control.ts +110 -0
  417. package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
  418. package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
  419. package/src/modules/communication_channels/lib/adapter.ts +605 -0
  420. package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
  421. package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
  422. package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
  423. package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
  424. package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
  425. package/src/modules/communication_channels/lib/email-contact.ts +17 -0
  426. package/src/modules/communication_channels/lib/email-mime.ts +425 -0
  427. package/src/modules/communication_channels/lib/error-classification.ts +144 -0
  428. package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
  429. package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
  430. package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
  431. package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
  432. package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
  433. package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
  434. package/src/modules/communication_channels/lib/provider-health.ts +47 -0
  435. package/src/modules/communication_channels/lib/push-state.ts +38 -0
  436. package/src/modules/communication_channels/lib/queue.ts +66 -0
  437. package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
  438. package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
  439. package/src/modules/communication_channels/lib/registry.ts +99 -0
  440. package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
  441. package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
  442. package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
  443. package/src/modules/communication_channels/lib/system-user.ts +74 -0
  444. package/src/modules/communication_channels/lib/test-seed.ts +140 -0
  445. package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
  446. package/src/modules/communication_channels/lib/thread-token.ts +355 -0
  447. package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
  448. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
  449. package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
  450. package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
  451. package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
  452. package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
  453. package/src/modules/communication_channels/notifications.client.ts +50 -0
  454. package/src/modules/communication_channels/notifications.handlers.ts +86 -0
  455. package/src/modules/communication_channels/notifications.ts +52 -0
  456. package/src/modules/communication_channels/setup.ts +158 -0
  457. package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
  458. package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
  459. package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
  460. package/src/modules/communication_channels/widgets/components.ts +36 -0
  461. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
  462. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
  463. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
  464. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
  465. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
  466. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
  467. package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
  468. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
  469. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
  470. package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
  471. package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
  472. package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
  473. package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
  474. package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
  475. package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
  476. package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
  477. package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
  478. package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
  479. package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
  480. package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
  481. package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
  482. package/src/modules/customers/acl.ts +18 -0
  483. package/src/modules/customers/api/activities/route.ts +13 -0
  484. package/src/modules/customers/api/companies/[id]/route.ts +21 -1
  485. package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
  486. package/src/modules/customers/api/interactions/counts/route.ts +10 -0
  487. package/src/modules/customers/api/interactions/route.ts +51 -5
  488. package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
  489. package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
  490. package/src/modules/customers/api/people/[id]/route.ts +17 -2
  491. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
  492. package/src/modules/customers/commands/deals.ts +65 -6
  493. package/src/modules/customers/commands/interactions.ts +30 -0
  494. package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
  495. package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
  496. package/src/modules/customers/components/detail/DealForm.tsx +2 -1
  497. package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
  498. package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
  499. package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
  500. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
  501. package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
  502. package/src/modules/customers/data/enrichers.ts +252 -1
  503. package/src/modules/customers/data/entities.ts +46 -1
  504. package/src/modules/customers/data/extensions.ts +26 -0
  505. package/src/modules/customers/encryption.ts +11 -0
  506. package/src/modules/customers/events.ts +4 -0
  507. package/src/modules/customers/i18n/de.json +41 -0
  508. package/src/modules/customers/i18n/en.json +41 -0
  509. package/src/modules/customers/i18n/es.json +41 -0
  510. package/src/modules/customers/i18n/pl.json +41 -0
  511. package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
  512. package/src/modules/customers/lib/kysely.ts +16 -0
  513. package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
  514. package/src/modules/customers/lib/personEmailThreads.ts +325 -0
  515. package/src/modules/customers/lib/visibilityFilter.ts +152 -0
  516. package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
  517. package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
  518. package/src/modules/customers/setup.ts +1 -0
  519. package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
  520. package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
  521. package/src/modules/integrations/AGENTS.md +9 -0
  522. package/src/modules/integrations/data/entities.ts +21 -1
  523. package/src/modules/integrations/lib/credentials-service.ts +49 -13
  524. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
  525. package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
  526. package/src/modules/messages/commands/messages.ts +101 -8
  527. package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
  528. package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
  529. package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
  530. package/src/modules/messages/data/entities.ts +11 -0
  531. package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
  532. package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
  533. package/src/modules/messages/widgets/injection-table.ts +29 -0
@@ -0,0 +1,175 @@
1
+ import { NextResponse } from "next/server";
2
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
4
+ import { CommunicationChannel } from "../../../../data/entities.js";
5
+ import { getChannelAdapterRegistry } from "../../../../lib/adapter-registry-singleton.js";
6
+ import {
7
+ COMMUNICATION_CHANNELS_QUEUES,
8
+ getCommunicationChannelsQueue
9
+ } from "../../../../lib/queue.js";
10
+ const metadata = {
11
+ path: "/communication_channels/webhook/[provider]",
12
+ POST: {
13
+ requireAuth: false,
14
+ // Unauthenticated by design (signature verification IS the auth), but the
15
+ // handler fans out an O(N) cross-tenant candidate scan, so bound per-IP
16
+ // request volume to limit abuse. Generous enough for real provider traffic;
17
+ // the dedicated gmail route carries its own matching limits.
18
+ rateLimit: { points: 120, duration: 60, keyPrefix: "cc_webhook_inbound" }
19
+ }
20
+ };
21
+ async function POST(req, { params }) {
22
+ const resolvedParams = await params;
23
+ const providerKey = resolvedParams.provider;
24
+ const registry = getChannelAdapterRegistry();
25
+ const adapter = registry?.get(providerKey);
26
+ if (!adapter) {
27
+ return NextResponse.json(
28
+ { error: `No ChannelAdapter for provider: ${providerKey}` },
29
+ { status: 404 }
30
+ );
31
+ }
32
+ const rawBody = await req.text();
33
+ const headers = {};
34
+ req.headers.forEach((value, key) => {
35
+ headers[key] = value;
36
+ });
37
+ const container = await createRequestContainer();
38
+ const em = container.resolve("em").fork();
39
+ let credentialsService = null;
40
+ try {
41
+ credentialsService = container.resolve("integrationCredentialsService");
42
+ } catch {
43
+ credentialsService = null;
44
+ }
45
+ try {
46
+ const candidates = await findWithDecryption(
47
+ em,
48
+ CommunicationChannel,
49
+ {
50
+ providerKey,
51
+ isActive: true,
52
+ deletedAt: null
53
+ },
54
+ { orderBy: { createdAt: "desc" } }
55
+ );
56
+ let matchedChannel = null;
57
+ let matchedScope = null;
58
+ let event = null;
59
+ let lastVerificationError = null;
60
+ for (const candidate of candidates) {
61
+ const candidateScope = {
62
+ tenantId: candidate.tenantId,
63
+ organizationId: candidate.organizationId ?? candidate.tenantId
64
+ };
65
+ const credentialsLookupScope = {
66
+ ...candidateScope,
67
+ userId: candidate.userId ?? null
68
+ };
69
+ let credentials = {};
70
+ if (candidate.credentialsRef && credentialsService) {
71
+ try {
72
+ credentials = await credentialsService.resolve(`channel_${providerKey}`, credentialsLookupScope) ?? {};
73
+ } catch {
74
+ credentials = {};
75
+ }
76
+ }
77
+ try {
78
+ event = await adapter.verifyWebhook({
79
+ rawBody,
80
+ headers,
81
+ credentials,
82
+ scope: candidateScope
83
+ });
84
+ matchedChannel = candidate;
85
+ matchedScope = candidateScope;
86
+ break;
87
+ } catch (error) {
88
+ lastVerificationError = error;
89
+ }
90
+ }
91
+ if (!event || !matchedChannel || !matchedScope) {
92
+ throw lastVerificationError ?? new Error("Webhook verification failed: no matching channel");
93
+ }
94
+ if (event.eventType === "reaction") {
95
+ if (typeof adapter.normalizeInboundReaction !== "function") {
96
+ return NextResponse.json(
97
+ {
98
+ received: true,
99
+ queued: false,
100
+ reason: `adapter '${providerKey}' does not implement normalizeInboundReaction`
101
+ },
102
+ { status: 202 }
103
+ );
104
+ }
105
+ const reactionEvent = await adapter.normalizeInboundReaction(event);
106
+ const reactionJob = {
107
+ kind: "inbound",
108
+ providerKey,
109
+ channelId: matchedChannel.id,
110
+ channelType: matchedChannel.channelType,
111
+ event: reactionEvent,
112
+ // Use the channel's REAL org (null when null), matching the poll and
113
+ // dedicated gmail webhook path — `candidateScope` falls org
114
+ // back to tenantId for credential/verify lookups, which must not leak
115
+ // into the ingest scope (it would diverge dedup for null-org channels).
116
+ scope: { tenantId: matchedScope.tenantId, organizationId: matchedChannel.organizationId ?? null },
117
+ attempt: 1
118
+ };
119
+ const reactionsQueue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.reactions);
120
+ await reactionsQueue.enqueue(reactionJob);
121
+ return NextResponse.json({ received: true, queued: true, kind: "reaction" }, { status: 202 });
122
+ }
123
+ if (event.eventType && event.eventType !== "message") {
124
+ return NextResponse.json(
125
+ {
126
+ received: true,
127
+ queued: false,
128
+ reason: `event ${event.eventType} not yet handled`
129
+ },
130
+ { status: 202 }
131
+ );
132
+ }
133
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.inbound);
134
+ const jobPayload = {
135
+ providerKey,
136
+ channelId: matchedChannel.id,
137
+ channelType: matchedChannel.channelType,
138
+ raw: event,
139
+ // Ingest scope uses the channel's REAL org (null when null) so dedup matches
140
+ // the poll + dedicated webhook paths; see the reaction branch above.
141
+ scope: { tenantId: matchedScope.tenantId, organizationId: matchedChannel.organizationId ?? null }
142
+ };
143
+ await queue.enqueue(jobPayload);
144
+ return NextResponse.json({ received: true, queued: true, kind: "message" }, { status: 202 });
145
+ } catch (error) {
146
+ console.warn(
147
+ "[communication_channels] inbound webhook verification failed:",
148
+ error instanceof Error ? error.message : error
149
+ );
150
+ return NextResponse.json({ error: "verification_failed" }, { status: 401 });
151
+ }
152
+ }
153
+ const openApi = {
154
+ tags: ["CommunicationChannels"],
155
+ summary: "Receive a communication channel webhook",
156
+ methods: {
157
+ POST: {
158
+ summary: "Process an inbound channel webhook (Slack, WhatsApp, Email, ...)",
159
+ tags: ["CommunicationChannels"],
160
+ responses: [
161
+ { status: 202, description: "Webhook accepted for async processing" },
162
+ { status: 401, description: "Signature verification failed against every candidate channel" },
163
+ { status: 404, description: "Unknown provider" }
164
+ ]
165
+ }
166
+ }
167
+ };
168
+ var route_default = POST;
169
+ export {
170
+ POST,
171
+ route_default as default,
172
+ metadata,
173
+ openApi
174
+ };
175
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/communication_channels/api/post/webhook/%5Bprovider%5D/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CommunicationChannel } from '../../../../data/entities'\nimport { getChannelAdapterRegistry } from '../../../../lib/adapter-registry-singleton'\nimport {\n COMMUNICATION_CHANNELS_QUEUES,\n getCommunicationChannelsQueue,\n} from '../../../../lib/queue'\nimport type { InboundMessage } from '../../../../lib/adapter'\nimport type { InboundProcessorPayload } from '../../../../workers/inbound-processor'\nimport type { ReactionInboundJob } from '../../../../lib/reaction-processor-types'\n\n/**\n * Inbound webhook endpoint for the communication_channels hub.\n *\n * One endpoint per provider \u2014 provider key in the path. The hub iterates all\n * candidate `CommunicationChannel` rows for `(provider_key, is_active=true,\n * deleted_at IS NULL)` across tenants and asks each adapter to verify the\n * signature with that channel's credentials. The first successful verification\n * pins the request to that channel's tenant scope; we then enqueue an inbound\n * processor job and return 202.\n *\n * Fail-closed: if no candidate verifies, we return 401 \u2014 never 200. This mirrors\n * the per-tenant authentication model used by `shipping_carriers/api/webhook/[provider]`\n * and the security note in SPEC-045d \u00A713.\n *\n * No auth required at the route level \u2014 signature verification IS the auth.\n */\nexport const metadata = {\n path: '/communication_channels/webhook/[provider]',\n POST: {\n requireAuth: false,\n // Unauthenticated by design (signature verification IS the auth), but the\n // handler fans out an O(N) cross-tenant candidate scan, so bound per-IP\n // request volume to limit abuse. Generous enough for real provider traffic;\n // the dedicated gmail route carries its own matching limits.\n rateLimit: { points: 120, duration: 60, keyPrefix: 'cc_webhook_inbound' },\n },\n}\n\ntype RouteContext = {\n params: Promise<{ provider: string }> | { provider: string }\n}\n\nexport async function POST(req: Request, { params }: RouteContext): Promise<Response> {\n const resolvedParams = await params\n const providerKey = resolvedParams.provider\n\n const registry = getChannelAdapterRegistry()\n const adapter = registry?.get(providerKey)\n if (!adapter) {\n return NextResponse.json(\n { error: `No ChannelAdapter for provider: ${providerKey}` },\n { status: 404 },\n )\n }\n\n const rawBody = await req.text()\n const headers: Record<string, string> = {}\n req.headers.forEach((value, key) => {\n headers[key] = value\n })\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager).fork()\n\n type CredentialsServiceLike = {\n resolve: (\n providerOrIntegrationId: string,\n scope: { organizationId: string; tenantId: string; userId?: string | null },\n ) => Promise<Record<string, unknown> | null>\n }\n let credentialsService: CredentialsServiceLike | null = null\n try {\n credentialsService = container.resolve<CredentialsServiceLike>('integrationCredentialsService')\n } catch {\n credentialsService = null\n }\n\n try {\n // The webhook is unauthenticated. The TENANT MUST be derived from a channel whose\n // per-tenant credentials successfully verify the inbound signature \u2014 NEVER from\n // attacker-controlled payload headers or unsigned retries.\n const candidates = await findWithDecryption(\n em,\n CommunicationChannel,\n {\n providerKey,\n isActive: true,\n deletedAt: null,\n },\n { orderBy: { createdAt: 'desc' } },\n )\n\n let matchedChannel: CommunicationChannel | null = null\n let matchedScope: { tenantId: string; organizationId: string } | null = null\n let event: InboundMessage | null = null\n let lastVerificationError: unknown = null\n\n for (const candidate of candidates as CommunicationChannel[]) {\n const candidateScope = {\n tenantId: candidate.tenantId,\n organizationId: candidate.organizationId ?? candidate.tenantId,\n }\n // Per-user credential lookup: each candidate channel has its own user\n // and therefore its own credentials row. See review R2-C1 / N1.\n const credentialsLookupScope = {\n ...candidateScope,\n userId: candidate.userId ?? null,\n }\n let credentials: Record<string, unknown> = {}\n if (candidate.credentialsRef && credentialsService) {\n try {\n credentials =\n (await credentialsService.resolve(`channel_${providerKey}`, credentialsLookupScope)) ?? {}\n } catch {\n credentials = {}\n }\n }\n try {\n event = await adapter.verifyWebhook({\n rawBody,\n headers,\n credentials,\n scope: candidateScope,\n })\n matchedChannel = candidate\n matchedScope = candidateScope\n break\n } catch (error: unknown) {\n lastVerificationError = error\n }\n }\n\n if (!event || !matchedChannel || !matchedScope) {\n throw (\n lastVerificationError ?? new Error('Webhook verification failed: no matching channel')\n )\n }\n\n // Dispatch by event type:\n // - 'message' (default) \u2192 inbound-processor (slice 2b)\n // - 'reaction' \u2192 reaction-processor (slice 2d)\n // - 'status_update' / 'other' \u2192 202 not handled (future slice)\n if (event.eventType === 'reaction') {\n if (typeof adapter.normalizeInboundReaction !== 'function') {\n return NextResponse.json(\n {\n received: true,\n queued: false,\n reason: `adapter '${providerKey}' does not implement normalizeInboundReaction`,\n },\n { status: 202 },\n )\n }\n const reactionEvent = await adapter.normalizeInboundReaction(event)\n const reactionJob: ReactionInboundJob = {\n kind: 'inbound',\n providerKey,\n channelId: matchedChannel.id,\n channelType: matchedChannel.channelType,\n event: reactionEvent,\n // Use the channel's REAL org (null when null), matching the poll and\n // dedicated gmail webhook path \u2014 `candidateScope` falls org\n // back to tenantId for credential/verify lookups, which must not leak\n // into the ingest scope (it would diverge dedup for null-org channels).\n scope: { tenantId: matchedScope.tenantId, organizationId: matchedChannel.organizationId ?? null },\n attempt: 1,\n }\n const reactionsQueue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.reactions)\n await reactionsQueue.enqueue(reactionJob as unknown as Record<string, unknown>)\n return NextResponse.json({ received: true, queued: true, kind: 'reaction' }, { status: 202 })\n }\n\n if (event.eventType && event.eventType !== 'message') {\n return NextResponse.json(\n {\n received: true,\n queued: false,\n reason: `event ${event.eventType} not yet handled`,\n },\n { status: 202 },\n )\n }\n\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.inbound)\n const jobPayload: InboundProcessorPayload = {\n providerKey,\n channelId: matchedChannel.id,\n channelType: matchedChannel.channelType,\n raw: event,\n // Ingest scope uses the channel's REAL org (null when null) so dedup matches\n // the poll + dedicated webhook paths; see the reaction branch above.\n scope: { tenantId: matchedScope.tenantId, organizationId: matchedChannel.organizationId ?? null },\n }\n await queue.enqueue(jobPayload as unknown as Record<string, unknown>)\n\n return NextResponse.json({ received: true, queued: true, kind: 'message' }, { status: 202 })\n } catch (error: unknown) {\n // Do not echo adapter/verification internals to an unauthenticated caller;\n // log the detail server-side and return a fixed, minimal message.\n console.warn(\n '[communication_channels] inbound webhook verification failed:',\n error instanceof Error ? error.message : error,\n )\n return NextResponse.json({ error: 'verification_failed' }, { status: 401 })\n }\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n summary: 'Receive a communication channel webhook',\n methods: {\n POST: {\n summary: 'Process an inbound channel webhook (Slack, WhatsApp, Email, ...)',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 202, description: 'Webhook accepted for async processing' },\n { status: 401, description: 'Signature verification failed against every candidate channel' },\n { status: 404, description: 'Unknown provider' },\n ],\n },\n },\n}\nexport default POST\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,123 @@
1
+ import { NextResponse } from "next/server";
2
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
4
+ import { CommunicationChannel } from "../../../../data/entities.js";
5
+ import {
6
+ COMMUNICATION_CHANNELS_QUEUES,
7
+ getCommunicationChannelsQueue
8
+ } from "../../../../lib/queue.js";
9
+ import {
10
+ decodeGmailPubSubBody,
11
+ getGmailPubSubVerifier,
12
+ GmailPubSubJwtError
13
+ } from "../../../../lib/gmail-pubsub-jwt.js";
14
+ const metadata = {
15
+ path: "/communication_channels/webhooks/gmail",
16
+ // Unauthenticated at the platform layer (a Google-signed JWT is the auth).
17
+ // Rate-limited so a caller can't drive unbounded JWT-verification +
18
+ // cert-fetch work before the signature gate rejects them.
19
+ POST: { requireAuth: false, rateLimit: { points: 120, duration: 60, keyPrefix: "cc_webhook_gmail" } }
20
+ };
21
+ async function POST(req) {
22
+ const expectedAudience = process.env.OM_GMAIL_PUBSUB_AUDIENCE;
23
+ const expectedEmail = process.env.OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL;
24
+ if (!expectedAudience || !expectedEmail) {
25
+ console.error(
26
+ "[gmail-webhook] OM_GMAIL_PUBSUB_AUDIENCE / OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL not set"
27
+ );
28
+ return NextResponse.json({ error: "webhook not configured" }, { status: 503 });
29
+ }
30
+ const verifier = getGmailPubSubVerifier();
31
+ try {
32
+ await verifier.verify({
33
+ authorizationHeader: req.headers.get("authorization"),
34
+ expectedAudience,
35
+ expectedEmail
36
+ });
37
+ } catch (err) {
38
+ if (err instanceof GmailPubSubJwtError) {
39
+ const status = err.code === "wrong_audience" ? 403 : err.code === "fetch_certs_failed" ? 503 : 401;
40
+ return NextResponse.json({ error: err.code, message: err.message }, { status });
41
+ }
42
+ throw err;
43
+ }
44
+ let rawBody;
45
+ try {
46
+ rawBody = await req.text();
47
+ } catch {
48
+ return NextResponse.json({ error: "unreadable_body" }, { status: 400 });
49
+ }
50
+ let payload;
51
+ try {
52
+ payload = decodeGmailPubSubBody(rawBody);
53
+ } catch (err) {
54
+ return NextResponse.json(
55
+ { error: err instanceof Error ? err.message : "invalid_payload" },
56
+ { status: 400 }
57
+ );
58
+ }
59
+ const container = await createRequestContainer();
60
+ const em = container.resolve("em").fork();
61
+ const channels = await findWithDecryption(
62
+ em,
63
+ CommunicationChannel,
64
+ {
65
+ providerKey: "gmail",
66
+ externalIdentifier: payload.emailAddress,
67
+ isActive: true,
68
+ deletedAt: null
69
+ }
70
+ );
71
+ if (channels.length === 0) {
72
+ return new NextResponse(null, { status: 204 });
73
+ }
74
+ const queue = getCommunicationChannelsQueue(
75
+ COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync
76
+ );
77
+ for (const channel of channels) {
78
+ const job = {
79
+ channelId: channel.id,
80
+ scope: {
81
+ tenantId: channel.tenantId,
82
+ organizationId: channel.organizationId ?? null
83
+ },
84
+ notification: {
85
+ emailAddress: payload.emailAddress,
86
+ historyId: String(payload.historyId)
87
+ }
88
+ };
89
+ try {
90
+ await queue.enqueue(job);
91
+ } catch (err) {
92
+ console.error(
93
+ `[gmail-webhook] failed to enqueue history-sync for channel ${channel.id}:`,
94
+ err
95
+ );
96
+ }
97
+ }
98
+ return new NextResponse(null, { status: 204 });
99
+ }
100
+ const openApi = {
101
+ tags: ["CommunicationChannels"],
102
+ methods: {
103
+ POST: {
104
+ summary: "Gmail Pub/Sub push notification webhook (Spec C \xA7 Phase C2)",
105
+ tags: ["CommunicationChannels"],
106
+ responses: [
107
+ { status: 204, description: "Notification verified + history-sync job enqueued" },
108
+ { status: 400, description: "Body not a valid Pub/Sub envelope" },
109
+ { status: 401, description: "Invalid JWT or email claim" },
110
+ { status: 403, description: "Wrong audience" },
111
+ { status: 503, description: "Webhook not configured / Google certs unreachable" }
112
+ ]
113
+ }
114
+ }
115
+ };
116
+ var route_default = POST;
117
+ export {
118
+ POST,
119
+ route_default as default,
120
+ metadata,
121
+ openApi
122
+ };
123
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/communication_channels/api/post/webhooks/gmail/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CommunicationChannel } from '../../../../data/entities'\nimport {\n COMMUNICATION_CHANNELS_QUEUES,\n getCommunicationChannelsQueue,\n} from '../../../../lib/queue'\nimport {\n decodeGmailPubSubBody,\n getGmailPubSubVerifier,\n GmailPubSubJwtError,\n} from '../../../../lib/gmail-pubsub-jwt'\n\n/**\n * Spec C \u00A7 Phase C2 \u2014 Gmail Pub/Sub push webhook.\n *\n * Auth model: NOT authenticated via the platform's session cookie. Pub/Sub\n * authenticates with a Google-signed JWT in the `Authorization: Bearer \u2026`\n * header, which we verify against Google's public certs.\n *\n * Validation pipeline:\n * 1. Verify JWT signature, audience (`OM_GMAIL_PUBSUB_AUDIENCE`), and\n * email claim (`OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL`).\n * 2. Decode the Pub/Sub envelope \u2192 `{ emailAddress, historyId }`.\n * 3. Look up every active Gmail channel matching `emailAddress`. Multiple\n * tenants may have the same mailbox connected \u2014 we enqueue one sync\n * job per matching channel (each tenant sees their own data).\n * 4. Return `204 No Content` even when no channels match \u2014 returning 4xx\n * would cause Pub/Sub to retry forever on a permanently-orphaned\n * registration.\n */\nexport const metadata = {\n path: '/communication_channels/webhooks/gmail',\n // Unauthenticated at the platform layer (a Google-signed JWT is the auth).\n // Rate-limited so a caller can't drive unbounded JWT-verification +\n // cert-fetch work before the signature gate rejects them.\n POST: { requireAuth: false, rateLimit: { points: 120, duration: 60, keyPrefix: 'cc_webhook_gmail' } },\n}\n\ntype GmailHistorySyncJobPayload = {\n channelId: string\n scope: { tenantId: string; organizationId: string | null }\n notification: { emailAddress: string; historyId: string }\n}\n\nexport async function POST(req: Request): Promise<Response> {\n const expectedAudience = process.env.OM_GMAIL_PUBSUB_AUDIENCE\n const expectedEmail = process.env.OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL\n if (!expectedAudience || !expectedEmail) {\n // Misconfiguration is operator-facing; log and 503 so Pub/Sub retries\n // briefly and the operator notices.\n console.error(\n '[gmail-webhook] OM_GMAIL_PUBSUB_AUDIENCE / OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL not set',\n )\n return NextResponse.json({ error: 'webhook not configured' }, { status: 503 })\n }\n\n const verifier = getGmailPubSubVerifier()\n try {\n await verifier.verify({\n authorizationHeader: req.headers.get('authorization'),\n expectedAudience,\n expectedEmail,\n })\n } catch (err) {\n if (err instanceof GmailPubSubJwtError) {\n const status =\n err.code === 'wrong_audience' ? 403 :\n err.code === 'fetch_certs_failed' ? 503 :\n 401\n return NextResponse.json({ error: err.code, message: err.message }, { status })\n }\n throw err\n }\n\n let rawBody: string\n try {\n rawBody = await req.text()\n } catch {\n return NextResponse.json({ error: 'unreadable_body' }, { status: 400 })\n }\n\n let payload: { emailAddress: string; historyId: string | number }\n try {\n payload = decodeGmailPubSubBody(rawBody)\n } catch (err) {\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'invalid_payload' },\n { status: 400 },\n )\n }\n\n // Channel lookup is intentionally NOT tenant-scoped here \u2014 we don't have a\n // tenant signal beyond the email address. We enumerate matching channels\n // across all tenants and enqueue one sync job per match. Each job carries\n // its own tenant scope, so downstream ingest stays tenant-isolated.\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager).fork()\n const channels = await findWithDecryption(\n em,\n CommunicationChannel,\n {\n providerKey: 'gmail',\n externalIdentifier: payload.emailAddress,\n isActive: true,\n deletedAt: null,\n },\n )\n if (channels.length === 0) {\n // Return 204 anyway \u2014 see route header comment.\n return new NextResponse(null, { status: 204 })\n }\n\n const queue = getCommunicationChannelsQueue(\n COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,\n )\n for (const channel of channels) {\n const job: GmailHistorySyncJobPayload = {\n channelId: channel.id,\n scope: {\n tenantId: channel.tenantId,\n organizationId: channel.organizationId ?? null,\n },\n notification: {\n emailAddress: payload.emailAddress,\n historyId: String(payload.historyId),\n },\n }\n try {\n await queue.enqueue(job as unknown as Record<string, unknown>)\n } catch (err) {\n console.error(\n `[gmail-webhook] failed to enqueue history-sync for channel ${channel.id}:`,\n err,\n )\n }\n }\n\n return new NextResponse(null, { status: 204 })\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n POST: {\n summary: 'Gmail Pub/Sub push notification webhook (Spec C \u00A7 Phase C2)',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 204, description: 'Notification verified + history-sync job enqueued' },\n { status: 400, description: 'Body not a valid Pub/Sub envelope' },\n { status: 401, description: 'Invalid JWT or email claim' },\n { status: 403, description: 'Wrong audience' },\n { status: 503, description: 'Webhook not configured / Google certs unreachable' },\n ],\n },\n },\n}\n\nexport default POST\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,117 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
6
+ import {
7
+ COMMUNICATION_CHANNELS_REASSIGN_CONVERSATION_COMMAND_ID
8
+ } from "../../../../../commands/reassign-conversation.js";
9
+ import { validateRouteMutationGuard } from "../../../../../lib/route-mutation-guard.js";
10
+ const metadata = {
11
+ path: "/communication_channels/threads/[threadId]/assign",
12
+ PUT: {
13
+ requireAuth: true,
14
+ requireFeatures: ["communication_channels.assign"]
15
+ }
16
+ };
17
+ const bodySchema = z.object({
18
+ assignedUserId: z.string().uuid().nullable()
19
+ });
20
+ async function PUT(req, context) {
21
+ const { threadId } = await context.params;
22
+ if (!z.string().uuid().safeParse(threadId).success) {
23
+ return NextResponse.json({ error: "Invalid threadId" }, { status: 400 });
24
+ }
25
+ const auth = await getAuthFromRequest(req);
26
+ if (!auth?.tenantId) {
27
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
28
+ }
29
+ let body;
30
+ try {
31
+ const json = await readJsonSafe(req, null);
32
+ body = bodySchema.parse(json);
33
+ } catch (err) {
34
+ return NextResponse.json(
35
+ { error: err instanceof Error ? err.message : "Invalid request body" },
36
+ { status: 422 }
37
+ );
38
+ }
39
+ const container = await createRequestContainer();
40
+ const guard = await validateRouteMutationGuard({
41
+ container,
42
+ req,
43
+ auth,
44
+ input: {
45
+ resourceKind: "communication_channels.thread",
46
+ resourceId: threadId,
47
+ operation: "custom",
48
+ mutationPayload: body
49
+ }
50
+ });
51
+ if ("response" in guard) return guard.response;
52
+ const commandBus = container.resolve("commandBus");
53
+ const input = {
54
+ threadId,
55
+ assignedUserId: body.assignedUserId,
56
+ scope: {
57
+ tenantId: auth.tenantId,
58
+ organizationId: auth.orgId ?? null
59
+ }
60
+ };
61
+ const { result } = await commandBus.execute(COMMUNICATION_CHANNELS_REASSIGN_CONVERSATION_COMMAND_ID, {
62
+ input,
63
+ ctx: {
64
+ container,
65
+ auth,
66
+ organizationScope: null,
67
+ selectedOrganizationId: auth.orgId ?? null,
68
+ organizationIds: auth.orgId ? [auth.orgId] : null
69
+ }
70
+ });
71
+ if (result.status === "no_channel_link") {
72
+ return NextResponse.json({ error: result.reason }, { status: 404 });
73
+ }
74
+ if (result.status === "invalid_assignee") {
75
+ return NextResponse.json({ error: result.reason }, { status: 422 });
76
+ }
77
+ if (result.status === "noop") {
78
+ return NextResponse.json(
79
+ { threadId, assignedUserId: body.assignedUserId, unchanged: true },
80
+ { status: 200 }
81
+ );
82
+ }
83
+ await guard.afterSuccess();
84
+ return NextResponse.json(
85
+ {
86
+ threadId: result.threadId,
87
+ conversationId: result.conversationId,
88
+ previousAssignedUserId: result.previousAssignedUserId,
89
+ assignedUserId: result.nextAssignedUserId
90
+ },
91
+ { status: 200 }
92
+ );
93
+ }
94
+ const openApi = {
95
+ tags: ["CommunicationChannels"],
96
+ methods: {
97
+ PUT: {
98
+ summary: "Reassign a channel-linked conversation to a different owner",
99
+ tags: ["CommunicationChannels"],
100
+ responses: [
101
+ { status: 200, description: "Conversation reassigned (or unchanged)" },
102
+ { status: 400, description: "Invalid threadId" },
103
+ { status: 401, description: "Unauthorized" },
104
+ { status: 404, description: "Conversation not channel-linked" },
105
+ { status: 422, description: "Invalid request body" }
106
+ ]
107
+ }
108
+ }
109
+ };
110
+ var route_default = PUT;
111
+ export {
112
+ PUT,
113
+ route_default as default,
114
+ metadata,
115
+ openApi
116
+ };
117
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/communication_channels/api/put/threads/%5BthreadId%5D/assign/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport {\n COMMUNICATION_CHANNELS_REASSIGN_CONVERSATION_COMMAND_ID,\n type ReassignConversationInput,\n type ReassignConversationResult,\n} from '../../../../../commands/reassign-conversation'\nimport { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'\n\nexport const metadata = {\n path: '/communication_channels/threads/[threadId]/assign',\n PUT: {\n requireAuth: true,\n requireFeatures: ['communication_channels.assign'],\n },\n}\n\nconst bodySchema = z.object({\n assignedUserId: z.string().uuid().nullable(),\n})\n\ntype RouteContext = {\n params: Promise<{ threadId: string }> | { threadId: string }\n}\n\nexport async function PUT(req: Request, context: RouteContext): Promise<Response> {\n const { threadId } = await context.params\n if (!z.string().uuid().safeParse(threadId).success) {\n return NextResponse.json({ error: 'Invalid threadId' }, { status: 400 })\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: z.infer<typeof bodySchema>\n try {\n const json = await readJsonSafe(req, null)\n body = bodySchema.parse(json)\n } catch (err) {\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Invalid request body' },\n { status: 422 },\n )\n }\n\n const container = await createRequestContainer()\n const guard = await validateRouteMutationGuard({\n container,\n req,\n auth,\n input: {\n resourceKind: 'communication_channels.thread',\n resourceId: threadId,\n operation: 'custom',\n mutationPayload: body,\n },\n })\n if ('response' in guard) return guard.response\n\n const commandBus = container.resolve('commandBus') as CommandBus\n\n const input: ReassignConversationInput = {\n threadId,\n assignedUserId: body.assignedUserId,\n scope: {\n tenantId: auth.tenantId,\n organizationId: (auth as { orgId?: string | null }).orgId ?? null,\n },\n }\n const { result } = await commandBus.execute<\n ReassignConversationInput,\n ReassignConversationResult\n >(COMMUNICATION_CHANNELS_REASSIGN_CONVERSATION_COMMAND_ID, {\n input,\n ctx: {\n container,\n auth: auth as never,\n organizationScope: null,\n selectedOrganizationId: (auth as { orgId?: string | null }).orgId ?? null,\n organizationIds: (auth as { orgId?: string | null }).orgId\n ? [(auth as { orgId?: string | null }).orgId!]\n : null,\n },\n })\n\n if (result.status === 'no_channel_link') {\n return NextResponse.json({ error: result.reason }, { status: 404 })\n }\n if (result.status === 'invalid_assignee') {\n return NextResponse.json({ error: result.reason }, { status: 422 })\n }\n if (result.status === 'noop') {\n return NextResponse.json(\n { threadId, assignedUserId: body.assignedUserId, unchanged: true },\n { status: 200 },\n )\n }\n await guard.afterSuccess()\n return NextResponse.json(\n {\n threadId: result.threadId,\n conversationId: result.conversationId,\n previousAssignedUserId: result.previousAssignedUserId,\n assignedUserId: result.nextAssignedUserId,\n },\n { status: 200 },\n )\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n PUT: {\n summary: 'Reassign a channel-linked conversation to a different owner',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 200, description: 'Conversation reassigned (or unchanged)' },\n { status: 400, description: 'Invalid threadId' },\n { status: 401, description: 'Unauthorized' },\n { status: 404, description: 'Conversation not channel-linked' },\n { status: 422, description: 'Invalid request body' },\n ],\n },\n },\n}\nexport default PUT\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }