@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,267 @@
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 type { CommandBus } from '@open-mercato/shared/lib/commands'
7
+ import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
8
+ import { ExternalConversation, MessageChannelLink } from '../../../data/entities'
9
+ import {
10
+ COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID,
11
+ type ConnectCredentialChannelInput,
12
+ type ConnectCredentialChannelResult,
13
+ } from '../../../commands/connect-credential-channel'
14
+ import { emitCommunicationChannelsEvent } from '../../../events'
15
+ import {
16
+ TEST_SEED_PROVIDER_KEY,
17
+ ensureTestSeedAdapterRegistered,
18
+ isTestChannelSeedingEnabled,
19
+ } from '../../../lib/test-seed'
20
+
21
+ /**
22
+ * TEST-ONLY channel seeding endpoint.
23
+ *
24
+ * Gated by `OM_ENABLE_TEST_CHANNEL_SEEDING` — when the flag is unset (the
25
+ * production default) every request returns 404, so this route is invisible and
26
+ * inert in production. See `lib/test-seed.ts` for the full rationale.
27
+ *
28
+ * Two actions, both scoped to the caller's tenant/org:
29
+ * - `connect-channel`: connect a network-free `__test_seed__` channel owned by
30
+ * the caller (delegates to the real connect-credential command so the channel
31
+ * persists credentials + lands in `status='connected'`). Enables the outbound
32
+ * compose → deliver → `.sent` chain to complete in CI.
33
+ * - `emit-inbound`: insert an inbound `MessageChannelLink` (+ a `messages.message`
34
+ * row for threading) and emit `communication_channels.message.received` so the
35
+ * customers link-channel-message subscriber runs against real Postgres. Enables
36
+ * the inbound auto-link tests (TC-CRM-EMAIL-002..005).
37
+ */
38
+ export const metadata = {
39
+ path: '/communication_channels/test-seed',
40
+ POST: {
41
+ requireAuth: true,
42
+ requireFeatures: ['communication_channels.connect_user_channel'],
43
+ },
44
+ }
45
+
46
+ const addressObjectSchema = z.object({ address: z.string(), name: z.string().optional() })
47
+ const addressFieldSchema = z.union([
48
+ z.string(),
49
+ addressObjectSchema,
50
+ z.array(z.union([z.string(), addressObjectSchema])),
51
+ ])
52
+
53
+ const connectChannelSchema = z.object({
54
+ action: z.literal('connect-channel'),
55
+ displayName: z.string().min(1).max(255).optional(),
56
+ externalIdentifier: z.string().min(1).max(255).optional(),
57
+ })
58
+
59
+ const emitInboundSchema = z.object({
60
+ action: z.literal('emit-inbound'),
61
+ /** Channel that owns the inbound message; controls authorUserId + default visibility. */
62
+ channelId: z.string().uuid(),
63
+ /** Provider key persisted on the link (defaults to the stub provider). */
64
+ providerKey: z.string().min(1).max(64).optional(),
65
+ /** Normalized inbound addresses (stored under channelPayload). */
66
+ from: addressFieldSchema.optional(),
67
+ to: addressFieldSchema.optional(),
68
+ cc: addressFieldSchema.optional(),
69
+ subject: z.string().max(500).optional(),
70
+ bodyText: z.string().max(200_000).optional(),
71
+ /** RFC2822 Message-ID of this inbound message (for In-Reply-To matching). */
72
+ messageId: z.string().max(500).optional(),
73
+ /** RFC2822 In-Reply-To header (threading-inheritance fallback). */
74
+ inReplyTo: z.string().max(500).optional(),
75
+ references: z.array(z.string().max(500)).max(50).optional(),
76
+ /**
77
+ * Open Mercato `messages.message` thread id this inbound message belongs to.
78
+ * When set, a `messages.message` row is created with this `threadId` so the
79
+ * hub-thread inheritance join can resolve a Person from a sibling message.
80
+ */
81
+ messageThreadId: z.string().uuid().optional(),
82
+ })
83
+
84
+ const bodySchema = z.discriminatedUnion('action', [connectChannelSchema, emitInboundSchema])
85
+
86
+ export async function POST(req: Request): Promise<Response> {
87
+ // Fail-closed: invisible in production. Mirrors an unknown route (404) rather
88
+ // than 403 so the surface leaks nothing when the flag is off.
89
+ if (!isTestChannelSeedingEnabled()) {
90
+ return NextResponse.json({ error: 'Not found' }, { status: 404 })
91
+ }
92
+
93
+ const auth = await getAuthFromRequest(req)
94
+ if (!auth?.sub || !auth?.tenantId) {
95
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
96
+ }
97
+
98
+ let body: z.infer<typeof bodySchema>
99
+ try {
100
+ body = bodySchema.parse(await readJsonSafe(req, null))
101
+ } catch (err) {
102
+ return NextResponse.json(
103
+ { error: err instanceof Error ? err.message : 'Invalid request body' },
104
+ { status: 422 },
105
+ )
106
+ }
107
+
108
+ const container = await createRequestContainer()
109
+ // Defensive: make sure the stub adapter is registered for this process even if
110
+ // a worker-only node skipped module di registration.
111
+ ensureTestSeedAdapterRegistered()
112
+
113
+ const tenantId = auth.tenantId as string
114
+ const organizationId = (auth as { orgId?: string | null }).orgId ?? null
115
+ const userId = auth.sub as string
116
+
117
+ if (body.action === 'connect-channel') {
118
+ const stamp = Date.now()
119
+ const commandBus = container.resolve('commandBus') as CommandBus
120
+ const input: ConnectCredentialChannelInput = {
121
+ providerKey: TEST_SEED_PROVIDER_KEY,
122
+ displayName: body.displayName ?? `Test Seed Channel ${stamp}`,
123
+ credentials: {
124
+ username: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
125
+ fromAddress: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
126
+ },
127
+ userId,
128
+ scope: { tenantId, organizationId },
129
+ }
130
+ const { result } = await commandBus.execute<
131
+ ConnectCredentialChannelInput,
132
+ ConnectCredentialChannelResult
133
+ >(COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID, {
134
+ input,
135
+ ctx: {
136
+ container,
137
+ auth: auth as never,
138
+ organizationScope: null,
139
+ selectedOrganizationId: organizationId,
140
+ organizationIds: organizationId ? [organizationId] : null,
141
+ },
142
+ })
143
+ if (result.status !== 'connected') {
144
+ return NextResponse.json(
145
+ { error: '[internal] test-seed connect failed', detail: result },
146
+ { status: 500 },
147
+ )
148
+ }
149
+ return NextResponse.json(
150
+ { channelId: result.channelId, externalIdentifier: result.externalIdentifier },
151
+ { status: 201 },
152
+ )
153
+ }
154
+
155
+ // action === 'emit-inbound'
156
+ const em = (container.resolve('em') as EntityManager).fork()
157
+ const providerKey = body.providerKey ?? TEST_SEED_PROVIDER_KEY
158
+
159
+ // A MessageChannelLink requires a non-null external_conversation_id (FK) and
160
+ // message_id. Create a synthetic conversation + (optionally threaded) message
161
+ // so the link is shaped like a real inbound row the subscriber can consume.
162
+ const conversation = em.create(ExternalConversation, {
163
+ channelId: body.channelId,
164
+ externalConversationId: `inbound-seed:${Date.now()}:${Math.random().toString(16).slice(2, 8)}`,
165
+ subject: body.subject ?? null,
166
+ tenantId,
167
+ organizationId,
168
+ lastMessageAt: new Date(),
169
+ })
170
+ em.persist(conversation)
171
+ await em.flush()
172
+
173
+ // Insert the platform `messages.message` row via raw SQL rather than importing
174
+ // the messages module's entity class (cross-module ORM coupling rule). Only
175
+ // `thread_id` matters for the hub-thread inheritance join (TC-CRM-EMAIL-005);
176
+ // the rest satisfy NOT NULL constraints.
177
+ const messageRows = (await em.getConnection().execute(
178
+ `INSERT INTO messages
179
+ (type, thread_id, sender_user_id, subject, body, body_format, priority, status,
180
+ is_draft, sent_at, visibility, source_entity_type, source_entity_id,
181
+ tenant_id, organization_id, created_at, updated_at)
182
+ VALUES
183
+ (?, ?, ?, ?, ?, 'text', 'normal', 'sent',
184
+ false, now(), 'public', 'communication_channels.test_seed_inbound', ?,
185
+ ?, ?, now(), now())
186
+ RETURNING id`,
187
+ [
188
+ `channel.${providerKey}`,
189
+ body.messageThreadId ?? null,
190
+ userId,
191
+ body.subject ?? '(no subject)',
192
+ body.bodyText ?? '',
193
+ body.channelId,
194
+ tenantId,
195
+ organizationId,
196
+ ],
197
+ )) as Array<{ id: string }>
198
+ const messageId = messageRows[0]?.id
199
+ if (!messageId) {
200
+ return NextResponse.json({ error: '[internal] failed to seed message row' }, { status: 500 })
201
+ }
202
+
203
+ const link = em.create(MessageChannelLink, {
204
+ messageId,
205
+ externalConversationId: conversation.id,
206
+ providerKey,
207
+ channelType: 'email',
208
+ direction: 'inbound',
209
+ deliveryStatus: 'delivered',
210
+ channelPayload: {
211
+ ...(body.from !== undefined ? { from: body.from } : {}),
212
+ ...(body.to !== undefined ? { to: body.to } : {}),
213
+ ...(body.cc !== undefined ? { cc: body.cc } : {}),
214
+ ...(body.subject !== undefined ? { subject: body.subject } : {}),
215
+ ...(body.bodyText !== undefined ? { text: body.bodyText } : {}),
216
+ ...(body.inReplyTo !== undefined ? { inReplyTo: body.inReplyTo } : {}),
217
+ ...(body.references !== undefined ? { references: body.references } : {}),
218
+ },
219
+ channelContentType: 'text/plain',
220
+ channelMetadata: {
221
+ ...(body.messageId !== undefined ? { messageId: body.messageId } : {}),
222
+ },
223
+ tenantId,
224
+ organizationId,
225
+ })
226
+ em.persist(link)
227
+ await em.flush()
228
+
229
+ // Emit the hub event through the real event bus so the persistent customers
230
+ // link-channel-message-received subscriber is enqueued to the `events` queue.
231
+ await emitCommunicationChannelsEvent(
232
+ 'communication_channels.message.received',
233
+ {
234
+ channelLinkId: link.id,
235
+ channelId: body.channelId,
236
+ providerKey,
237
+ direction: 'inbound',
238
+ tenantId,
239
+ organizationId,
240
+ },
241
+ { persistent: true },
242
+ )
243
+
244
+ return NextResponse.json(
245
+ { channelLinkId: link.id, messageId, conversationId: conversation.id },
246
+ { status: 201 },
247
+ )
248
+ }
249
+
250
+ export const openApi = {
251
+ tags: ['CommunicationChannels'],
252
+ methods: {
253
+ POST: {
254
+ summary: 'Test-only: seed a connected channel or emit an inbound message (env-gated)',
255
+ tags: ['CommunicationChannels'],
256
+ responses: [
257
+ { status: 201, description: 'Channel seeded / inbound message emitted' },
258
+ { status: 401, description: 'Unauthorized' },
259
+ { status: 404, description: 'Test channel seeding disabled (production default)' },
260
+ { status: 422, description: 'Invalid request body' },
261
+ { status: 500, description: 'Seed failed' },
262
+ ],
263
+ },
264
+ },
265
+ }
266
+
267
+ export default POST
@@ -0,0 +1,227 @@
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 type { EntityManager } from '@mikro-orm/postgresql'
5
+ import { CommunicationChannel } from '../../../../data/entities'
6
+ import { getChannelAdapterRegistry } from '../../../../lib/adapter-registry-singleton'
7
+ import {
8
+ COMMUNICATION_CHANNELS_QUEUES,
9
+ getCommunicationChannelsQueue,
10
+ } from '../../../../lib/queue'
11
+ import type { InboundMessage } from '../../../../lib/adapter'
12
+ import type { InboundProcessorPayload } from '../../../../workers/inbound-processor'
13
+ import type { ReactionInboundJob } from '../../../../lib/reaction-processor-types'
14
+
15
+ /**
16
+ * Inbound webhook endpoint for the communication_channels hub.
17
+ *
18
+ * One endpoint per provider — provider key in the path. The hub iterates all
19
+ * candidate `CommunicationChannel` rows for `(provider_key, is_active=true,
20
+ * deleted_at IS NULL)` across tenants and asks each adapter to verify the
21
+ * signature with that channel's credentials. The first successful verification
22
+ * pins the request to that channel's tenant scope; we then enqueue an inbound
23
+ * processor job and return 202.
24
+ *
25
+ * Fail-closed: if no candidate verifies, we return 401 — never 200. This mirrors
26
+ * the per-tenant authentication model used by `shipping_carriers/api/webhook/[provider]`
27
+ * and the security note in SPEC-045d §13.
28
+ *
29
+ * No auth required at the route level — signature verification IS the auth.
30
+ */
31
+ export const metadata = {
32
+ path: '/communication_channels/webhook/[provider]',
33
+ POST: {
34
+ requireAuth: false,
35
+ // Unauthenticated by design (signature verification IS the auth), but the
36
+ // handler fans out an O(N) cross-tenant candidate scan, so bound per-IP
37
+ // request volume to limit abuse. Generous enough for real provider traffic;
38
+ // the dedicated gmail route carries its own matching limits.
39
+ rateLimit: { points: 120, duration: 60, keyPrefix: 'cc_webhook_inbound' },
40
+ },
41
+ }
42
+
43
+ type RouteContext = {
44
+ params: Promise<{ provider: string }> | { provider: string }
45
+ }
46
+
47
+ export async function POST(req: Request, { params }: RouteContext): Promise<Response> {
48
+ const resolvedParams = await params
49
+ const providerKey = resolvedParams.provider
50
+
51
+ const registry = getChannelAdapterRegistry()
52
+ const adapter = registry?.get(providerKey)
53
+ if (!adapter) {
54
+ return NextResponse.json(
55
+ { error: `No ChannelAdapter for provider: ${providerKey}` },
56
+ { status: 404 },
57
+ )
58
+ }
59
+
60
+ const rawBody = await req.text()
61
+ const headers: Record<string, string> = {}
62
+ req.headers.forEach((value, key) => {
63
+ headers[key] = value
64
+ })
65
+
66
+ const container = await createRequestContainer()
67
+ const em = (container.resolve('em') as EntityManager).fork()
68
+
69
+ type CredentialsServiceLike = {
70
+ resolve: (
71
+ providerOrIntegrationId: string,
72
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
73
+ ) => Promise<Record<string, unknown> | null>
74
+ }
75
+ let credentialsService: CredentialsServiceLike | null = null
76
+ try {
77
+ credentialsService = container.resolve<CredentialsServiceLike>('integrationCredentialsService')
78
+ } catch {
79
+ credentialsService = null
80
+ }
81
+
82
+ try {
83
+ // The webhook is unauthenticated. The TENANT MUST be derived from a channel whose
84
+ // per-tenant credentials successfully verify the inbound signature — NEVER from
85
+ // attacker-controlled payload headers or unsigned retries.
86
+ const candidates = await findWithDecryption(
87
+ em,
88
+ CommunicationChannel,
89
+ {
90
+ providerKey,
91
+ isActive: true,
92
+ deletedAt: null,
93
+ },
94
+ { orderBy: { createdAt: 'desc' } },
95
+ )
96
+
97
+ let matchedChannel: CommunicationChannel | null = null
98
+ let matchedScope: { tenantId: string; organizationId: string } | null = null
99
+ let event: InboundMessage | null = null
100
+ let lastVerificationError: unknown = null
101
+
102
+ for (const candidate of candidates as CommunicationChannel[]) {
103
+ const candidateScope = {
104
+ tenantId: candidate.tenantId,
105
+ organizationId: candidate.organizationId ?? candidate.tenantId,
106
+ }
107
+ // Per-user credential lookup: each candidate channel has its own user
108
+ // and therefore its own credentials row. See review R2-C1 / N1.
109
+ const credentialsLookupScope = {
110
+ ...candidateScope,
111
+ userId: candidate.userId ?? null,
112
+ }
113
+ let credentials: Record<string, unknown> = {}
114
+ if (candidate.credentialsRef && credentialsService) {
115
+ try {
116
+ credentials =
117
+ (await credentialsService.resolve(`channel_${providerKey}`, credentialsLookupScope)) ?? {}
118
+ } catch {
119
+ credentials = {}
120
+ }
121
+ }
122
+ try {
123
+ event = await adapter.verifyWebhook({
124
+ rawBody,
125
+ headers,
126
+ credentials,
127
+ scope: candidateScope,
128
+ })
129
+ matchedChannel = candidate
130
+ matchedScope = candidateScope
131
+ break
132
+ } catch (error: unknown) {
133
+ lastVerificationError = error
134
+ }
135
+ }
136
+
137
+ if (!event || !matchedChannel || !matchedScope) {
138
+ throw (
139
+ lastVerificationError ?? new Error('Webhook verification failed: no matching channel')
140
+ )
141
+ }
142
+
143
+ // Dispatch by event type:
144
+ // - 'message' (default) → inbound-processor (slice 2b)
145
+ // - 'reaction' → reaction-processor (slice 2d)
146
+ // - 'status_update' / 'other' → 202 not handled (future slice)
147
+ if (event.eventType === 'reaction') {
148
+ if (typeof adapter.normalizeInboundReaction !== 'function') {
149
+ return NextResponse.json(
150
+ {
151
+ received: true,
152
+ queued: false,
153
+ reason: `adapter '${providerKey}' does not implement normalizeInboundReaction`,
154
+ },
155
+ { status: 202 },
156
+ )
157
+ }
158
+ const reactionEvent = await adapter.normalizeInboundReaction(event)
159
+ const reactionJob: ReactionInboundJob = {
160
+ kind: 'inbound',
161
+ providerKey,
162
+ channelId: matchedChannel.id,
163
+ channelType: matchedChannel.channelType,
164
+ event: reactionEvent,
165
+ // Use the channel's REAL org (null when null), matching the poll and
166
+ // dedicated gmail webhook path — `candidateScope` falls org
167
+ // back to tenantId for credential/verify lookups, which must not leak
168
+ // into the ingest scope (it would diverge dedup for null-org channels).
169
+ scope: { tenantId: matchedScope.tenantId, organizationId: matchedChannel.organizationId ?? null },
170
+ attempt: 1,
171
+ }
172
+ const reactionsQueue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.reactions)
173
+ await reactionsQueue.enqueue(reactionJob as unknown as Record<string, unknown>)
174
+ return NextResponse.json({ received: true, queued: true, kind: 'reaction' }, { status: 202 })
175
+ }
176
+
177
+ if (event.eventType && event.eventType !== 'message') {
178
+ return NextResponse.json(
179
+ {
180
+ received: true,
181
+ queued: false,
182
+ reason: `event ${event.eventType} not yet handled`,
183
+ },
184
+ { status: 202 },
185
+ )
186
+ }
187
+
188
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.inbound)
189
+ const jobPayload: InboundProcessorPayload = {
190
+ providerKey,
191
+ channelId: matchedChannel.id,
192
+ channelType: matchedChannel.channelType,
193
+ raw: event,
194
+ // Ingest scope uses the channel's REAL org (null when null) so dedup matches
195
+ // the poll + dedicated webhook paths; see the reaction branch above.
196
+ scope: { tenantId: matchedScope.tenantId, organizationId: matchedChannel.organizationId ?? null },
197
+ }
198
+ await queue.enqueue(jobPayload as unknown as Record<string, unknown>)
199
+
200
+ return NextResponse.json({ received: true, queued: true, kind: 'message' }, { status: 202 })
201
+ } catch (error: unknown) {
202
+ // Do not echo adapter/verification internals to an unauthenticated caller;
203
+ // log the detail server-side and return a fixed, minimal message.
204
+ console.warn(
205
+ '[communication_channels] inbound webhook verification failed:',
206
+ error instanceof Error ? error.message : error,
207
+ )
208
+ return NextResponse.json({ error: 'verification_failed' }, { status: 401 })
209
+ }
210
+ }
211
+
212
+ export const openApi = {
213
+ tags: ['CommunicationChannels'],
214
+ summary: 'Receive a communication channel webhook',
215
+ methods: {
216
+ POST: {
217
+ summary: 'Process an inbound channel webhook (Slack, WhatsApp, Email, ...)',
218
+ tags: ['CommunicationChannels'],
219
+ responses: [
220
+ { status: 202, description: 'Webhook accepted for async processing' },
221
+ { status: 401, description: 'Signature verification failed against every candidate channel' },
222
+ { status: 404, description: 'Unknown provider' },
223
+ ],
224
+ },
225
+ },
226
+ }
227
+ export default POST
@@ -0,0 +1,161 @@
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 type { EntityManager } from '@mikro-orm/postgresql'
5
+ import { CommunicationChannel } from '../../../../data/entities'
6
+ import {
7
+ COMMUNICATION_CHANNELS_QUEUES,
8
+ getCommunicationChannelsQueue,
9
+ } from '../../../../lib/queue'
10
+ import {
11
+ decodeGmailPubSubBody,
12
+ getGmailPubSubVerifier,
13
+ GmailPubSubJwtError,
14
+ } from '../../../../lib/gmail-pubsub-jwt'
15
+
16
+ /**
17
+ * Spec C § Phase C2 — Gmail Pub/Sub push webhook.
18
+ *
19
+ * Auth model: NOT authenticated via the platform's session cookie. Pub/Sub
20
+ * authenticates with a Google-signed JWT in the `Authorization: Bearer …`
21
+ * header, which we verify against Google's public certs.
22
+ *
23
+ * Validation pipeline:
24
+ * 1. Verify JWT signature, audience (`OM_GMAIL_PUBSUB_AUDIENCE`), and
25
+ * email claim (`OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL`).
26
+ * 2. Decode the Pub/Sub envelope → `{ emailAddress, historyId }`.
27
+ * 3. Look up every active Gmail channel matching `emailAddress`. Multiple
28
+ * tenants may have the same mailbox connected — we enqueue one sync
29
+ * job per matching channel (each tenant sees their own data).
30
+ * 4. Return `204 No Content` even when no channels match — returning 4xx
31
+ * would cause Pub/Sub to retry forever on a permanently-orphaned
32
+ * registration.
33
+ */
34
+ export const metadata = {
35
+ path: '/communication_channels/webhooks/gmail',
36
+ // Unauthenticated at the platform layer (a Google-signed JWT is the auth).
37
+ // Rate-limited so a caller can't drive unbounded JWT-verification +
38
+ // cert-fetch work before the signature gate rejects them.
39
+ POST: { requireAuth: false, rateLimit: { points: 120, duration: 60, keyPrefix: 'cc_webhook_gmail' } },
40
+ }
41
+
42
+ type GmailHistorySyncJobPayload = {
43
+ channelId: string
44
+ scope: { tenantId: string; organizationId: string | null }
45
+ notification: { emailAddress: string; historyId: string }
46
+ }
47
+
48
+ export async function POST(req: Request): Promise<Response> {
49
+ const expectedAudience = process.env.OM_GMAIL_PUBSUB_AUDIENCE
50
+ const expectedEmail = process.env.OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL
51
+ if (!expectedAudience || !expectedEmail) {
52
+ // Misconfiguration is operator-facing; log and 503 so Pub/Sub retries
53
+ // briefly and the operator notices.
54
+ console.error(
55
+ '[gmail-webhook] OM_GMAIL_PUBSUB_AUDIENCE / OM_GMAIL_PUBSUB_SERVICE_ACCOUNT_EMAIL not set',
56
+ )
57
+ return NextResponse.json({ error: 'webhook not configured' }, { status: 503 })
58
+ }
59
+
60
+ const verifier = getGmailPubSubVerifier()
61
+ try {
62
+ await verifier.verify({
63
+ authorizationHeader: req.headers.get('authorization'),
64
+ expectedAudience,
65
+ expectedEmail,
66
+ })
67
+ } catch (err) {
68
+ if (err instanceof GmailPubSubJwtError) {
69
+ const status =
70
+ err.code === 'wrong_audience' ? 403 :
71
+ err.code === 'fetch_certs_failed' ? 503 :
72
+ 401
73
+ return NextResponse.json({ error: err.code, message: err.message }, { status })
74
+ }
75
+ throw err
76
+ }
77
+
78
+ let rawBody: string
79
+ try {
80
+ rawBody = await req.text()
81
+ } catch {
82
+ return NextResponse.json({ error: 'unreadable_body' }, { status: 400 })
83
+ }
84
+
85
+ let payload: { emailAddress: string; historyId: string | number }
86
+ try {
87
+ payload = decodeGmailPubSubBody(rawBody)
88
+ } catch (err) {
89
+ return NextResponse.json(
90
+ { error: err instanceof Error ? err.message : 'invalid_payload' },
91
+ { status: 400 },
92
+ )
93
+ }
94
+
95
+ // Channel lookup is intentionally NOT tenant-scoped here — we don't have a
96
+ // tenant signal beyond the email address. We enumerate matching channels
97
+ // across all tenants and enqueue one sync job per match. Each job carries
98
+ // its own tenant scope, so downstream ingest stays tenant-isolated.
99
+ const container = await createRequestContainer()
100
+ const em = (container.resolve('em') as EntityManager).fork()
101
+ const channels = await findWithDecryption(
102
+ em,
103
+ CommunicationChannel,
104
+ {
105
+ providerKey: 'gmail',
106
+ externalIdentifier: payload.emailAddress,
107
+ isActive: true,
108
+ deletedAt: null,
109
+ },
110
+ )
111
+ if (channels.length === 0) {
112
+ // Return 204 anyway — see route header comment.
113
+ return new NextResponse(null, { status: 204 })
114
+ }
115
+
116
+ const queue = getCommunicationChannelsQueue(
117
+ COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,
118
+ )
119
+ for (const channel of channels) {
120
+ const job: GmailHistorySyncJobPayload = {
121
+ channelId: channel.id,
122
+ scope: {
123
+ tenantId: channel.tenantId,
124
+ organizationId: channel.organizationId ?? null,
125
+ },
126
+ notification: {
127
+ emailAddress: payload.emailAddress,
128
+ historyId: String(payload.historyId),
129
+ },
130
+ }
131
+ try {
132
+ await queue.enqueue(job as unknown as Record<string, unknown>)
133
+ } catch (err) {
134
+ console.error(
135
+ `[gmail-webhook] failed to enqueue history-sync for channel ${channel.id}:`,
136
+ err,
137
+ )
138
+ }
139
+ }
140
+
141
+ return new NextResponse(null, { status: 204 })
142
+ }
143
+
144
+ export const openApi = {
145
+ tags: ['CommunicationChannels'],
146
+ methods: {
147
+ POST: {
148
+ summary: 'Gmail Pub/Sub push notification webhook (Spec C § Phase C2)',
149
+ tags: ['CommunicationChannels'],
150
+ responses: [
151
+ { status: 204, description: 'Notification verified + history-sync job enqueued' },
152
+ { status: 400, description: 'Body not a valid Pub/Sub envelope' },
153
+ { status: 401, description: 'Invalid JWT or email claim' },
154
+ { status: 403, description: 'Wrong audience' },
155
+ { status: 503, description: 'Webhook not configured / Google certs unreachable' },
156
+ ],
157
+ },
158
+ },
159
+ }
160
+
161
+ export default POST