@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,174 @@
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 type { EntityManager } from '@mikro-orm/postgresql'
6
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
7
+ import { CommunicationChannel } from '../../../../../data/entities'
8
+ import { ChannelAccessDeniedError, assertCanManageChannel } from '../../../../../lib/access-control'
9
+ import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../../../../../lib/queue'
10
+ import type { PollChannelJobPayload } from '../../../../../workers/poll-channel'
11
+ import { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'
12
+
13
+ type RbacServiceLike = {
14
+ loadAcl: (
15
+ userId: string,
16
+ scope: { tenantId: string | null; organizationId: string | null },
17
+ ) => Promise<{ isSuperAdmin: boolean; features: string[]; organizations: string[] | null }>
18
+ }
19
+
20
+ export const metadata = {
21
+ path: '/communication_channels/channels/[id]/poll-now',
22
+ POST: {
23
+ // Owner self-service: a user may sync their OWN mailbox (gated by
24
+ // `connect_user_channel`). Polling a shared/tenant-wide channel still
25
+ // requires `manage` — enforced per channel type by `assertCanManageChannel`.
26
+ requireAuth: true,
27
+ requireFeatures: ['communication_channels.connect_user_channel'],
28
+ },
29
+ }
30
+
31
+ type RouteContext = {
32
+ params: Promise<{ id: string }> | { id: string }
33
+ }
34
+
35
+ /**
36
+ * Manual poll trigger — enqueues a single `poll-channel` job immediately so
37
+ * the operator (or a demo) doesn't have to wait for the 60-second scheduler
38
+ * tick + per-channel `poll_interval_seconds` window.
39
+ *
40
+ * Per-user access guard mirrors the rest of the channels API: only the channel
41
+ * owner (or an admin with `communication_channels.admin`) can trigger a poll.
42
+ */
43
+ export async function POST(req: Request, context: RouteContext): Promise<Response> {
44
+ const { id } = await context.params
45
+ if (!z.string().uuid().safeParse(id).success) {
46
+ return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })
47
+ }
48
+
49
+ const auth = await getAuthFromRequest(req)
50
+ if (!auth?.sub || !auth?.tenantId) {
51
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
52
+ }
53
+
54
+ const container = await createRequestContainer()
55
+ const em = (container.resolve('em') as EntityManager).fork()
56
+ const organizationId = (auth as { orgId?: string | null }).orgId ?? null
57
+ const dscope = { tenantId: auth.tenantId as string, organizationId }
58
+
59
+ const channel = await findOneWithDecryption(
60
+ em,
61
+ CommunicationChannel,
62
+ {
63
+ id,
64
+ tenantId: auth.tenantId as string,
65
+ organizationId,
66
+ deletedAt: null,
67
+ },
68
+ undefined,
69
+ dscope,
70
+ )
71
+ if (!channel) {
72
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
73
+ }
74
+
75
+ // Load features via RBAC so admin bypass is honoured.
76
+ let userFeatures: string[] = []
77
+ try {
78
+ const rbac = container.resolve('rbacService') as RbacServiceLike
79
+ const acl = await rbac.loadAcl(auth.sub as string, {
80
+ tenantId: auth.tenantId as string,
81
+ organizationId,
82
+ })
83
+ userFeatures = acl?.isSuperAdmin ? ['*'] : Array.isArray(acl?.features) ? acl.features : []
84
+ } catch {
85
+ userFeatures = []
86
+ }
87
+ try {
88
+ assertCanManageChannel(
89
+ { userId: (channel as { userId?: string | null }).userId },
90
+ auth.sub as string,
91
+ userFeatures,
92
+ 'communication_channels.manage',
93
+ )
94
+ } catch (err) {
95
+ if (err instanceof ChannelAccessDeniedError) {
96
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
97
+ }
98
+ throw err
99
+ }
100
+
101
+ if (!channel.isActive) {
102
+ return NextResponse.json({ error: 'Channel is disabled' }, { status: 409 })
103
+ }
104
+ // Allow manual poll-now from 'connected' AND 'error' states. The operator's
105
+ // intent in clicking "Poll now" while the channel is in error is exactly
106
+ // "retry the connection right now"; a successful poll auto-resets status
107
+ // back to 'connected' (see poll-channel.ts).
108
+ // Block only the explicitly-broken lifecycle states.
109
+ if (channel.status === 'requires_reauth') {
110
+ return NextResponse.json(
111
+ { error: 'Channel needs reauthentication — reconnect from /backend/profile/communication-channels' },
112
+ { status: 409 },
113
+ )
114
+ }
115
+ if (channel.status === 'disconnected') {
116
+ return NextResponse.json(
117
+ { error: 'Channel is disconnected — reconnect to resume polling' },
118
+ { status: 409 },
119
+ )
120
+ }
121
+
122
+ const guard = await validateRouteMutationGuard({
123
+ container,
124
+ req,
125
+ auth,
126
+ input: {
127
+ resourceKind: 'communication_channels.channel',
128
+ resourceId: channel.id,
129
+ operation: 'custom',
130
+ mutationPayload: { action: 'poll-now' },
131
+ },
132
+ })
133
+ if ('response' in guard) return guard.response
134
+
135
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)
136
+ const payload: PollChannelJobPayload = {
137
+ channelId: channel.id,
138
+ scope: {
139
+ tenantId: auth.tenantId as string,
140
+ organizationId: organizationId ?? null,
141
+ },
142
+ attempt: 1,
143
+ }
144
+ await queue.enqueue(payload as unknown as Record<string, unknown>)
145
+ await guard.afterSuccess()
146
+
147
+ return NextResponse.json(
148
+ {
149
+ ok: true,
150
+ channelId: channel.id,
151
+ queued: true,
152
+ message: 'Poll queued — new messages will appear after the worker runs.',
153
+ },
154
+ { status: 202 },
155
+ )
156
+ }
157
+
158
+ export const openApi = {
159
+ tags: ['CommunicationChannels'],
160
+ methods: {
161
+ POST: {
162
+ summary: 'Manually trigger a poll cycle for a channel (demo / operator override)',
163
+ tags: ['CommunicationChannels'],
164
+ responses: [
165
+ { status: 202, description: 'Poll job enqueued' },
166
+ { status: 400, description: 'Invalid channel id' },
167
+ { status: 401, description: 'Unauthorized' },
168
+ { status: 404, description: 'Channel not found' },
169
+ { status: 409, description: 'Channel disabled or not connected' },
170
+ ],
171
+ },
172
+ },
173
+ }
174
+ export default POST
@@ -0,0 +1,158 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import type { EntityManager } from '@mikro-orm/postgresql'
4
+ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
7
+ import type { CrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
8
+ import { CommunicationChannel } from '../../../../../../data/entities'
9
+ import { ChannelAccessDeniedError, assertCanManageChannel } from '../../../../../../lib/access-control'
10
+ import { pushRegister } from '../../../../../../commands/push-register'
11
+ import { validateRouteMutationGuard } from '../../../../../../lib/route-mutation-guard'
12
+
13
+ type RbacServiceLike = {
14
+ loadAcl: (
15
+ userId: string,
16
+ scope: { tenantId: string | null; organizationId: string | null },
17
+ ) => Promise<{ isSuperAdmin: boolean; features: string[]; organizations: string[] | null }>
18
+ }
19
+
20
+ /**
21
+ * Spec C § Phase C5 — Operator-facing "Re-register push" endpoint.
22
+ *
23
+ * Owner self-service: a user may (re-)register push on their OWN mailbox (gated
24
+ * by `connect_user_channel`). Registering push on a shared/tenant-wide channel
25
+ * still requires `communication_channels.channel.push.manage` — enforced per
26
+ * channel type by `assertCanManageChannel` in the handler. Used by the profile
27
+ * page and the channel detail page's `PushStatusSection` to recover from a
28
+ * `pushStatus='failed'` state.
29
+ */
30
+ export const metadata = {
31
+ path: '/communication_channels/channels/[id]/push/register',
32
+ POST: {
33
+ requireAuth: true,
34
+ requireFeatures: ['communication_channels.connect_user_channel'],
35
+ },
36
+ }
37
+
38
+ type RouteContext = {
39
+ params: Promise<{ id: string }> | { id: string }
40
+ }
41
+
42
+ export async function POST(req: Request, context: RouteContext): Promise<Response> {
43
+ const { id } = await context.params
44
+ if (!z.string().uuid().safeParse(id).success) {
45
+ return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })
46
+ }
47
+
48
+ const auth = await getAuthFromRequest(req)
49
+ if (!auth?.sub || !auth?.tenantId) {
50
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
51
+ }
52
+ const organizationId = (auth as { orgId?: string | null }).orgId ?? null
53
+ if (!organizationId) {
54
+ return NextResponse.json({ error: 'No organization scope' }, { status: 400 })
55
+ }
56
+
57
+ const container = await createRequestContainer()
58
+
59
+ // Defense-in-depth: `push.manage` is admin-default, but enforce per-user
60
+ // ownership anyway so a non-admin operator who is granted `push.manage`
61
+ // cannot re-register push on another user's channel. Admins (and tenant-wide
62
+ // channels) pass via `assertCanAccessChannel`. Automatic callers (OAuth
63
+ // callback, connect, renew worker) invoke `pushRegister` directly and are
64
+ // intentionally not subject to this user-facing guard.
65
+ const em = (container.resolve('em') as EntityManager).fork()
66
+ const channel = await findOneWithDecryption(
67
+ em,
68
+ CommunicationChannel,
69
+ { id, tenantId: auth.tenantId as string, organizationId, deletedAt: null },
70
+ undefined,
71
+ { tenantId: auth.tenantId as string, organizationId },
72
+ )
73
+ if (!channel) {
74
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
75
+ }
76
+ let userFeatures: string[] = []
77
+ try {
78
+ const rbac = container.resolve('rbacService') as RbacServiceLike
79
+ const acl = await rbac.loadAcl(auth.sub as string, {
80
+ tenantId: auth.tenantId as string,
81
+ organizationId,
82
+ })
83
+ userFeatures = acl?.isSuperAdmin ? ['*'] : Array.isArray(acl?.features) ? acl.features : []
84
+ } catch {
85
+ userFeatures = []
86
+ }
87
+ try {
88
+ assertCanManageChannel(
89
+ { userId: (channel as { userId?: string | null }).userId },
90
+ auth.sub as string,
91
+ userFeatures,
92
+ 'communication_channels.channel.push.manage',
93
+ )
94
+ } catch (err) {
95
+ if (err instanceof ChannelAccessDeniedError) {
96
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
97
+ }
98
+ throw err
99
+ }
100
+
101
+ const guard = await validateRouteMutationGuard({
102
+ container,
103
+ req,
104
+ auth,
105
+ input: {
106
+ resourceKind: 'communication_channels.channel',
107
+ resourceId: id,
108
+ operation: 'custom',
109
+ mutationPayload: { pushStatus: 'register' },
110
+ },
111
+ })
112
+ if ('response' in guard) return guard.response
113
+
114
+ try {
115
+ const result = await pushRegister({
116
+ container,
117
+ scope: { tenantId: auth.tenantId as string, organizationId, userId: auth.sub as string },
118
+ input: { channelId: id },
119
+ })
120
+ await guard.afterSuccess()
121
+ return NextResponse.json({ ok: true, ...result }, { status: 202 })
122
+ } catch (err) {
123
+ const candidate = err as CrudFormError
124
+ if (candidate && typeof candidate.status === 'number') {
125
+ return NextResponse.json(
126
+ { error: candidate.message, fieldErrors: candidate.fieldErrors },
127
+ { status: candidate.status },
128
+ )
129
+ }
130
+ console.error(`[push-register] failed for channel ${id}:`, err)
131
+ return NextResponse.json(
132
+ { error: err instanceof Error ? err.message : 'Failed to register push' },
133
+ { status: 500 },
134
+ )
135
+ }
136
+ }
137
+
138
+ export const openApi = {
139
+ tags: ['CommunicationChannels'],
140
+ methods: {
141
+ POST: {
142
+ summary: 'Force-register push delivery for a channel (Spec C § Phase C5)',
143
+ tags: ['CommunicationChannels'],
144
+ responses: [
145
+ { status: 202, description: 'Push registration attempted; check result.pushStatus' },
146
+ { status: 400, description: 'Invalid id or unsupported provider' },
147
+ { status: 401, description: 'Unauthorized' },
148
+ { status: 403, description: 'Missing push.manage feature' },
149
+ { status: 404, description: 'Channel not found' },
150
+ { status: 409, description: 'Provider does not support push (IMAP)' },
151
+ { status: 502, description: 'Provider returned an error during registration' },
152
+ { status: 503, description: 'Webhook base URL or Pub/Sub topic not configured' },
153
+ ],
154
+ },
155
+ },
156
+ }
157
+
158
+ export default POST
@@ -0,0 +1,114 @@
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 type { CommandBus } from '@open-mercato/shared/lib/commands'
6
+ import {
7
+ COMMUNICATION_CHANNELS_SET_PRIMARY_COMMAND_ID,
8
+ type SetPrimaryChannelInput,
9
+ type SetPrimaryChannelResult,
10
+ } from '../../../../../commands/set-primary-channel'
11
+ import { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'
12
+
13
+ export const metadata = {
14
+ path: '/communication_channels/channels/[id]/set-primary',
15
+ POST: {
16
+ // Owner self-service: every email user controls their own channels. The
17
+ // set-primary command enforces strict ownership (`not_owner` → 404), and
18
+ // "primary" only applies to per-user channels, so `connect_user_channel`
19
+ // is the correct gate (not `manage`).
20
+ requireAuth: true,
21
+ requireFeatures: ['communication_channels.connect_user_channel'],
22
+ },
23
+ }
24
+
25
+ type RouteContext = {
26
+ params: Promise<{ id: string }> | { id: string }
27
+ }
28
+
29
+ export async function POST(req: Request, context: RouteContext): Promise<Response> {
30
+ const { id } = await context.params
31
+ if (!z.string().uuid().safeParse(id).success) {
32
+ return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })
33
+ }
34
+
35
+ const auth = await getAuthFromRequest(req)
36
+ if (!auth?.sub || !auth?.tenantId) {
37
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
38
+ }
39
+
40
+ const container = await createRequestContainer()
41
+ const guard = await validateRouteMutationGuard({
42
+ container,
43
+ req,
44
+ auth,
45
+ input: {
46
+ resourceKind: 'communication_channels.channel',
47
+ resourceId: id,
48
+ operation: 'custom',
49
+ mutationPayload: { isPrimary: true },
50
+ },
51
+ })
52
+ if ('response' in guard) return guard.response
53
+
54
+ const commandBus = container.resolve('commandBus') as CommandBus
55
+
56
+ const input: SetPrimaryChannelInput = {
57
+ channelId: id,
58
+ userId: auth.sub as string,
59
+ scope: {
60
+ tenantId: auth.tenantId as string,
61
+ organizationId: (auth as { orgId?: string | null }).orgId ?? null,
62
+ },
63
+ }
64
+ const { result } = await commandBus.execute<
65
+ SetPrimaryChannelInput,
66
+ SetPrimaryChannelResult
67
+ >(COMMUNICATION_CHANNELS_SET_PRIMARY_COMMAND_ID, {
68
+ input,
69
+ ctx: {
70
+ container,
71
+ auth: auth as never,
72
+ organizationScope: null,
73
+ selectedOrganizationId: (auth as { orgId?: string | null }).orgId ?? null,
74
+ organizationIds: (auth as { orgId?: string | null }).orgId
75
+ ? [(auth as { orgId?: string | null }).orgId!]
76
+ : null,
77
+ },
78
+ })
79
+
80
+ // Non-ownership maps to 404 (existence masking), consistent with the other
81
+ // channel-scoped routes (GET [id], poll-now, import-history, test-send, DELETE).
82
+ if (result.status === 'not_owner') {
83
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
84
+ }
85
+ if (result.status === 'noop') {
86
+ return NextResponse.json({ channelId: id, isPrimary: true, unchanged: true }, { status: 200 })
87
+ }
88
+ await guard.afterSuccess()
89
+ return NextResponse.json(
90
+ {
91
+ channelId: result.channelId,
92
+ isPrimary: true,
93
+ previousPrimaryChannelId: result.previousPrimaryChannelId,
94
+ },
95
+ { status: 200 },
96
+ )
97
+ }
98
+
99
+ export const openApi = {
100
+ tags: ['CommunicationChannels'],
101
+ methods: {
102
+ POST: {
103
+ summary: 'Mark a per-user channel as primary',
104
+ tags: ['CommunicationChannels'],
105
+ responses: [
106
+ { status: 200, description: 'Channel set as primary (or already primary)' },
107
+ { status: 400, description: 'Invalid channel id' },
108
+ { status: 401, description: 'Unauthorized' },
109
+ { status: 404, description: 'Channel not found or not owned by current user' },
110
+ ],
111
+ },
112
+ },
113
+ }
114
+ export default POST
@@ -0,0 +1,241 @@
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 type { EntityManager } from '@mikro-orm/postgresql'
7
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
8
+ import { CommunicationChannel } from '../../../../../data/entities'
9
+ import { getChannelAdapter } from '../../../../../lib/adapter-registry-singleton'
10
+ import { ChannelAccessDeniedError, assertCanManageChannel } from '../../../../../lib/access-control'
11
+ import { refreshCredentialsIfNeeded } from '../../../../../lib/credential-refresh'
12
+ import { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'
13
+
14
+ type RbacServiceLike = {
15
+ loadAcl: (
16
+ userId: string,
17
+ scope: { tenantId: string | null; organizationId: string | null },
18
+ ) => Promise<{ isSuperAdmin: boolean; features: string[]; organizations: string[] | null }>
19
+ }
20
+
21
+ export const metadata = {
22
+ path: '/communication_channels/channels/[id]/test-send',
23
+ POST: {
24
+ // Owner self-service: a user may send a test from their OWN mailbox (gated
25
+ // by `connect_user_channel`). Test-sending from a shared/tenant-wide channel
26
+ // still requires `manage` — enforced per channel type by `assertCanManageChannel`.
27
+ requireAuth: true,
28
+ requireFeatures: ['communication_channels.connect_user_channel'],
29
+ },
30
+ }
31
+
32
+ const bodySchema = z.object({
33
+ to: z.string().email(),
34
+ subject: z.string().min(1).max(500).optional(),
35
+ body: z.string().max(50_000).optional(),
36
+ })
37
+
38
+ type RouteContext = {
39
+ params: Promise<{ id: string }> | { id: string }
40
+ }
41
+
42
+ type CredentialsServiceLike = {
43
+ resolve: (
44
+ integrationId: string,
45
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
46
+ ) => Promise<Record<string, unknown> | null>
47
+ save?: (
48
+ integrationId: string,
49
+ credentials: Record<string, unknown>,
50
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
51
+ ) => Promise<void>
52
+ }
53
+
54
+ /**
55
+ * Admin diagnostic — send a test message via the adapter without creating a
56
+ * platform `Message`. Useful for verifying credentials + outbound connectivity
57
+ * after a channel is configured.
58
+ *
59
+ * The result is NOT persisted to `ExternalMessage` / `MessageChannelLink` — it
60
+ * is a one-shot probe.
61
+ */
62
+ export async function POST(req: Request, context: RouteContext): Promise<Response> {
63
+ const { id } = await context.params
64
+ if (!z.string().uuid().safeParse(id).success) {
65
+ return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })
66
+ }
67
+
68
+ const auth = await getAuthFromRequest(req)
69
+ if (!auth?.sub || !auth?.tenantId) {
70
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
71
+ }
72
+
73
+ let body: z.infer<typeof bodySchema>
74
+ try {
75
+ body = bodySchema.parse(await readJsonSafe(req, null))
76
+ } catch (err) {
77
+ return NextResponse.json(
78
+ { error: err instanceof Error ? err.message : 'Invalid request body' },
79
+ { status: 422 },
80
+ )
81
+ }
82
+
83
+ const container = await createRequestContainer()
84
+ const em = (container.resolve('em') as EntityManager).fork()
85
+ const organizationId = (auth as { orgId?: string | null }).orgId ?? null
86
+ const dscope = { tenantId: auth.tenantId as string, organizationId }
87
+ const channel = await findOneWithDecryption(
88
+ em,
89
+ CommunicationChannel,
90
+ {
91
+ id,
92
+ tenantId: auth.tenantId as string,
93
+ organizationId,
94
+ deletedAt: null,
95
+ },
96
+ undefined,
97
+ dscope,
98
+ )
99
+ if (!channel) {
100
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
101
+ }
102
+ // Load features via RBAC service so admin bypass (`communication_channels.admin`,
103
+ // wildcards, super-admin) is honoured. The `auth` object from
104
+ // `getAuthFromRequest` carries identity only — feature ACLs live in the RBAC
105
+ // service and must be loaded per request.
106
+ let userFeatures: string[] = []
107
+ try {
108
+ const rbac = container.resolve('rbacService') as RbacServiceLike
109
+ const acl = await rbac.loadAcl(auth.sub as string, {
110
+ tenantId: auth.tenantId as string,
111
+ organizationId,
112
+ })
113
+ userFeatures = acl?.isSuperAdmin ? ['*'] : Array.isArray(acl?.features) ? acl.features : []
114
+ } catch {
115
+ userFeatures = []
116
+ }
117
+ try {
118
+ assertCanManageChannel(
119
+ { userId: channel.userId },
120
+ auth.sub as string,
121
+ userFeatures,
122
+ 'communication_channels.manage',
123
+ )
124
+ } catch (err) {
125
+ if (err instanceof ChannelAccessDeniedError) {
126
+ return NextResponse.json({ error: 'Channel not found' }, { status: 404 })
127
+ }
128
+ const status = (err as { statusCode?: number }).statusCode ?? 403
129
+ return NextResponse.json(
130
+ { error: err instanceof Error ? err.message : 'Access denied' },
131
+ { status },
132
+ )
133
+ }
134
+ if (!channel.isActive || channel.status !== 'connected') {
135
+ return NextResponse.json(
136
+ { error: `Channel is in status '${channel.status}' (not connected)` },
137
+ { status: 409 },
138
+ )
139
+ }
140
+
141
+ const guard = await validateRouteMutationGuard({
142
+ container,
143
+ req,
144
+ auth,
145
+ input: {
146
+ resourceKind: 'communication_channels.channel',
147
+ resourceId: channel.id,
148
+ operation: 'custom',
149
+ mutationPayload: body as unknown as Record<string, unknown>,
150
+ },
151
+ })
152
+ if ('response' in guard) return guard.response
153
+
154
+ const adapter = getChannelAdapter(channel.providerKey)
155
+ if (!adapter) {
156
+ return NextResponse.json(
157
+ { error: `No adapter registered for provider '${channel.providerKey}'` },
158
+ { status: 404 },
159
+ )
160
+ }
161
+
162
+ // Resolve credentials + optionally refresh.
163
+ let credentials: Record<string, unknown> = {}
164
+ let credentialsService: CredentialsServiceLike | null = null
165
+ try {
166
+ credentialsService = container.resolve('integrationCredentialsService') as CredentialsServiceLike
167
+ } catch {
168
+ credentialsService = null
169
+ }
170
+ if (channel.credentialsRef && credentialsService) {
171
+ credentials =
172
+ (await credentialsService
173
+ .resolve(`channel_${channel.providerKey}`, {
174
+ tenantId: auth.tenantId as string,
175
+ organizationId: organizationId ?? (auth.tenantId as string),
176
+ userId: channel.userId ?? null,
177
+ })
178
+ .catch(() => null)) ?? {}
179
+ }
180
+ const credentialScope = {
181
+ tenantId: auth.tenantId as string,
182
+ organizationId: organizationId ?? (auth.tenantId as string),
183
+ userId: channel.userId ?? null,
184
+ }
185
+ const refreshed = await refreshCredentialsIfNeeded(
186
+ {
187
+ adapter,
188
+ channelId: channel.id,
189
+ credentials,
190
+ scope: credentialScope,
191
+ },
192
+ { credentialsService },
193
+ )
194
+ credentials = refreshed.credentials
195
+
196
+ try {
197
+ const converted = await adapter.convertOutbound({
198
+ body: body.body ?? 'Test message from Open Mercato',
199
+ bodyFormat: 'text',
200
+ })
201
+ const result = await adapter.sendMessage({
202
+ content: converted.content,
203
+ credentials,
204
+ scope: {
205
+ tenantId: auth.tenantId as string,
206
+ organizationId: organizationId ?? (auth.tenantId as string),
207
+ },
208
+ metadata: { to: body.to, subject: body.subject ?? 'Test send', testSend: true },
209
+ })
210
+ await guard.afterSuccess()
211
+ return NextResponse.json({
212
+ status: result.status,
213
+ externalMessageId: result.externalMessageId,
214
+ providerError: result.error ?? null,
215
+ })
216
+ } catch (err) {
217
+ const message = err instanceof Error ? err.message : 'send failed'
218
+ return NextResponse.json({ status: 'failed', error: message }, { status: 502 })
219
+ }
220
+ }
221
+
222
+ export const openApi = {
223
+ tags: ['CommunicationChannels'],
224
+ methods: {
225
+ POST: {
226
+ summary: 'Diagnostic — send a test message through the channel without persisting',
227
+ tags: ['CommunicationChannels'],
228
+ responses: [
229
+ { status: 200, description: 'Test send result' },
230
+ { status: 400, description: 'Invalid channel id' },
231
+ { status: 401, description: 'Unauthorized' },
232
+ { status: 403, description: 'Not allowed to manage this channel' },
233
+ { status: 404, description: 'Channel or adapter not found' },
234
+ { status: 409, description: 'Channel not connected' },
235
+ { status: 422, description: 'Invalid request body' },
236
+ { status: 502, description: 'Provider error' },
237
+ ],
238
+ },
239
+ },
240
+ }
241
+ export default POST