@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4397.1.9a65481757
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.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
- package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
- package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
- package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
- package/dist/generated/entities/channel_thread_token/index.js +17 -0
- package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
- package/dist/generated/entities/communication_channel/index.js +43 -0
- package/dist/generated/entities/communication_channel/index.js.map +7 -0
- package/dist/generated/entities/customer_interaction/index.js +4 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entities/external_conversation/index.js +25 -0
- package/dist/generated/entities/external_conversation/index.js.map +7 -0
- package/dist/generated/entities/external_message/index.js +25 -0
- package/dist/generated/entities/external_message/index.js.map +7 -0
- package/dist/generated/entities/integration_credentials/index.js +3 -1
- package/dist/generated/entities/integration_credentials/index.js.map +2 -2
- package/dist/generated/entities/message/index.js +2 -0
- package/dist/generated/entities/message/index.js.map +2 -2
- package/dist/generated/entities/message_channel_link/index.js +33 -0
- package/dist/generated/entities/message_channel_link/index.js.map +7 -0
- package/dist/generated/entities/message_reaction/index.js +25 -0
- package/dist/generated/entities/message_reaction/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +11 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +117 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/authFixtures.js +2 -1
- package/dist/helpers/integration/authFixtures.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
- package/dist/modules/communication_channels/acl.js +47 -0
- package/dist/modules/communication_channels/acl.js.map +7 -0
- package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
- package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
- package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
- package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
- package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
- package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
- package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
- package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
- package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
- package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
- package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
- package/dist/modules/communication_channels/commands/interceptors.js +68 -0
- package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
- package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
- package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-register.js +146 -0
- package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-renew.js +23 -0
- package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
- package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
- package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
- package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
- package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
- package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
- package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
- package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
- package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
- package/dist/modules/communication_channels/data/enrichers.js +286 -0
- package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
- package/dist/modules/communication_channels/data/entities.js +447 -0
- package/dist/modules/communication_channels/data/entities.js.map +7 -0
- package/dist/modules/communication_channels/data/extensions.js +67 -0
- package/dist/modules/communication_channels/data/extensions.js.map +7 -0
- package/dist/modules/communication_channels/data/validators.js +123 -0
- package/dist/modules/communication_channels/data/validators.js.map +7 -0
- package/dist/modules/communication_channels/di.js +35 -0
- package/dist/modules/communication_channels/di.js.map +7 -0
- package/dist/modules/communication_channels/encryption.js +12 -0
- package/dist/modules/communication_channels/encryption.js.map +7 -0
- package/dist/modules/communication_channels/events.js +124 -0
- package/dist/modules/communication_channels/events.js.map +7 -0
- package/dist/modules/communication_channels/index.js +20 -0
- package/dist/modules/communication_channels/index.js.map +7 -0
- package/dist/modules/communication_channels/lib/access-control.js +43 -0
- package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
- package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
- package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter.js +1 -0
- package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
- package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
- package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
- package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
- package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
- package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
- package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-contact.js +14 -0
- package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-mime.js +269 -0
- package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
- package/dist/modules/communication_channels/lib/error-classification.js +101 -0
- package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
- package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
- package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
- package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
- package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
- package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
- package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
- package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
- package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
- package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
- package/dist/modules/communication_channels/lib/provider-health.js +24 -0
- package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-state.js +19 -0
- package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
- package/dist/modules/communication_channels/lib/queue.js +54 -0
- package/dist/modules/communication_channels/lib/queue.js.map +7 -0
- package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
- package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
- package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
- package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
- package/dist/modules/communication_channels/lib/registry.js +67 -0
- package/dist/modules/communication_channels/lib/registry.js.map +7 -0
- package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
- package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
- package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
- package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
- package/dist/modules/communication_channels/lib/system-user.js +22 -0
- package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
- package/dist/modules/communication_channels/lib/test-seed.js +68 -0
- package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
- package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
- package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
- package/dist/modules/communication_channels/lib/thread-token.js +219 -0
- package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
- package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
- package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
- package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
- package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/notifications.client.js +51 -0
- package/dist/modules/communication_channels/notifications.client.js.map +7 -0
- package/dist/modules/communication_channels/notifications.handlers.js +53 -0
- package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
- package/dist/modules/communication_channels/notifications.js +56 -0
- package/dist/modules/communication_channels/notifications.js.map +7 -0
- package/dist/modules/communication_channels/setup.js +105 -0
- package/dist/modules/communication_channels/setup.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
- package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
- package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
- package/dist/modules/communication_channels/widgets/components.js +7 -0
- package/dist/modules/communication_channels/widgets/components.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
- package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
- package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
- package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
- package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
- package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
- package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
- package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
- package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
- package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
- package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
- package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
- package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
- package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
- package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
- package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
- package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
- package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
- package/dist/modules/customers/acl.js +18 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +9 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +18 -7
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
- package/dist/modules/customers/api/interactions/counts/route.js +6 -0
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +26 -7
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
- package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
- package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/route.js +12 -4
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +46 -5
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +16 -0
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
- package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
- package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
- package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
- package/dist/modules/customers/components/detail/DealForm.js +2 -1
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +10 -0
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
- package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
- package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
- package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
- package/dist/modules/customers/data/enrichers.js +133 -2
- package/dist/modules/customers/data/enrichers.js.map +2 -2
- package/dist/modules/customers/data/entities.js +18 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/extensions.js +16 -0
- package/dist/modules/customers/data/extensions.js.map +7 -0
- package/dist/modules/customers/encryption.js +11 -0
- package/dist/modules/customers/encryption.js.map +2 -2
- package/dist/modules/customers/events.js +4 -1
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
- package/dist/modules/customers/lib/kysely.js.map +2 -2
- package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
- package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
- package/dist/modules/customers/lib/personEmailThreads.js +205 -0
- package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
- package/dist/modules/customers/lib/visibilityFilter.js +51 -0
- package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
- package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
- package/dist/modules/customers/setup.js +2 -1
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
- package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
- package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
- package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
- package/dist/modules/integrations/data/entities.js +8 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +29 -14
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
- package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
- package/dist/modules/messages/commands/messages.js +70 -8
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
- package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
- package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
- package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/data/entities.js +8 -1
- package/dist/modules/messages/data/entities.js.map +2 -2
- package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
- package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
- package/dist/modules/messages/widgets/injection-table.js +7 -0
- package/dist/modules/messages/widgets/injection-table.js.map +7 -0
- package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
- package/generated/entities/channel_thread_mapping/index.ts +11 -0
- package/generated/entities/channel_thread_token/index.ts +7 -0
- package/generated/entities/communication_channel/index.ts +20 -0
- package/generated/entities/customer_interaction/index.ts +2 -0
- package/generated/entities/external_conversation/index.ts +11 -0
- package/generated/entities/external_message/index.ts +11 -0
- package/generated/entities/integration_credentials/index.ts +1 -0
- package/generated/entities/message/index.ts +1 -0
- package/generated/entities/message_channel_link/index.ts +15 -0
- package/generated/entities/message_reaction/index.ts +11 -0
- package/generated/entities.ids.generated.ts +11 -0
- package/generated/entity-fields-registry.ts +117 -0
- package/package.json +9 -7
- package/src/helpers/integration/authFixtures.ts +4 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
- package/src/modules/communication_channels/acl.ts +43 -0
- package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
- package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
- package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
- package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
- package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
- package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
- package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
- package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
- package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
- package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
- package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
- package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
- package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
- package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
- package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
- package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
- package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
- package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
- package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
- package/src/modules/communication_channels/commands/interceptors.ts +104 -0
- package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
- package/src/modules/communication_channels/commands/push-register.ts +203 -0
- package/src/modules/communication_channels/commands/push-renew.ts +49 -0
- package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
- package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
- package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
- package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
- package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
- package/src/modules/communication_channels/data/enrichers.ts +413 -0
- package/src/modules/communication_channels/data/entities.ts +546 -0
- package/src/modules/communication_channels/data/extensions.ts +76 -0
- package/src/modules/communication_channels/data/validators.ts +138 -0
- package/src/modules/communication_channels/di.ts +40 -0
- package/src/modules/communication_channels/encryption.ts +44 -0
- package/src/modules/communication_channels/events.ts +122 -0
- package/src/modules/communication_channels/i18n/de.json +138 -0
- package/src/modules/communication_channels/i18n/en.json +138 -0
- package/src/modules/communication_channels/i18n/es.json +138 -0
- package/src/modules/communication_channels/i18n/pl.json +138 -0
- package/src/modules/communication_channels/index.ts +19 -0
- package/src/modules/communication_channels/lib/access-control.ts +110 -0
- package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
- package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
- package/src/modules/communication_channels/lib/adapter.ts +605 -0
- package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
- package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
- package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
- package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
- package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
- package/src/modules/communication_channels/lib/email-contact.ts +17 -0
- package/src/modules/communication_channels/lib/email-mime.ts +442 -0
- package/src/modules/communication_channels/lib/error-classification.ts +144 -0
- package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
- package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
- package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
- package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
- package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
- package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
- package/src/modules/communication_channels/lib/provider-health.ts +47 -0
- package/src/modules/communication_channels/lib/push-state.ts +38 -0
- package/src/modules/communication_channels/lib/queue.ts +66 -0
- package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
- package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
- package/src/modules/communication_channels/lib/registry.ts +99 -0
- package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
- package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
- package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
- package/src/modules/communication_channels/lib/system-user.ts +74 -0
- package/src/modules/communication_channels/lib/test-seed.ts +140 -0
- package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
- package/src/modules/communication_channels/lib/thread-token.ts +355 -0
- package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
- package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
- package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
- package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
- package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
- package/src/modules/communication_channels/notifications.client.ts +50 -0
- package/src/modules/communication_channels/notifications.handlers.ts +86 -0
- package/src/modules/communication_channels/notifications.ts +52 -0
- package/src/modules/communication_channels/setup.ts +158 -0
- package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
- package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
- package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
- package/src/modules/communication_channels/widgets/components.ts +36 -0
- package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
- package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
- package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
- package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
- package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
- package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
- package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
- package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
- package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
- package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
- package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
- package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
- package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
- package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
- package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
- package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
- package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
- package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
- package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
- package/src/modules/customers/acl.ts +18 -0
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/companies/[id]/route.ts +21 -1
- package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
- package/src/modules/customers/api/interactions/counts/route.ts +10 -0
- package/src/modules/customers/api/interactions/route.ts +51 -5
- package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
- package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
- package/src/modules/customers/api/people/[id]/route.ts +17 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
- package/src/modules/customers/commands/deals.ts +65 -6
- package/src/modules/customers/commands/interactions.ts +30 -0
- package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
- package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
- package/src/modules/customers/components/detail/DealForm.tsx +2 -1
- package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
- package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
- package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
- package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
- package/src/modules/customers/data/enrichers.ts +252 -1
- package/src/modules/customers/data/entities.ts +46 -1
- package/src/modules/customers/data/extensions.ts +26 -0
- package/src/modules/customers/encryption.ts +11 -0
- package/src/modules/customers/events.ts +4 -0
- package/src/modules/customers/i18n/de.json +41 -0
- package/src/modules/customers/i18n/en.json +41 -0
- package/src/modules/customers/i18n/es.json +41 -0
- package/src/modules/customers/i18n/pl.json +41 -0
- package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
- package/src/modules/customers/lib/kysely.ts +16 -0
- package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
- package/src/modules/customers/lib/personEmailThreads.ts +325 -0
- package/src/modules/customers/lib/visibilityFilter.ts +152 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
- package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
- package/src/modules/customers/setup.ts +1 -0
- package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
- package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
- package/src/modules/integrations/AGENTS.md +9 -0
- package/src/modules/integrations/data/entities.ts +21 -1
- package/src/modules/integrations/lib/credentials-service.ts +49 -13
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
- package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
- package/src/modules/messages/commands/messages.ts +101 -8
- package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
- package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
- package/src/modules/messages/data/entities.ts +11 -0
- package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
- package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
- package/src/modules/messages/widgets/injection-table.ts +29 -0
|
@@ -0,0 +1,157 @@
|
|
|
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 { toAbsoluteUrl } from "@open-mercato/shared/lib/url";
|
|
7
|
+
import { getChannelAdapter } from "../../../../../lib/adapter-registry-singleton.js";
|
|
8
|
+
import { resolveOAuthClientCredentials } from "../../../../../lib/oauth-client-config.js";
|
|
9
|
+
import {
|
|
10
|
+
COMMUNICATION_CHANNELS_OAUTH_STATE_COOKIE_NAME,
|
|
11
|
+
COMMUNICATION_CHANNELS_OAUTH_STATE_TTL_MS,
|
|
12
|
+
createOAuthState,
|
|
13
|
+
DEFAULT_OAUTH_RETURN_URL,
|
|
14
|
+
isSafeOAuthReturnUrl,
|
|
15
|
+
normalizeOAuthReturnUrl,
|
|
16
|
+
OAuthStateError
|
|
17
|
+
} from "../../../../../lib/oauth-state.js";
|
|
18
|
+
const metadata = {
|
|
19
|
+
path: "/communication_channels/oauth/[provider]/initiate",
|
|
20
|
+
POST: {
|
|
21
|
+
requireAuth: true,
|
|
22
|
+
requireFeatures: ["communication_channels.connect_user_channel"]
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const initiateBodySchema = z.object({
|
|
26
|
+
channelType: z.literal("email").optional(),
|
|
27
|
+
/** Where to send the user after the callback succeeds. Defaults to the profile page. */
|
|
28
|
+
returnUrl: z.string().min(1).max(2048).refine(isSafeOAuthReturnUrl, {
|
|
29
|
+
message: "returnUrl must be a same-origin path"
|
|
30
|
+
}).optional(),
|
|
31
|
+
/** Optional pre-filled email — Google `login_hint`. */
|
|
32
|
+
loginHint: z.string().email().optional()
|
|
33
|
+
});
|
|
34
|
+
function defaultRedirectUri(req, providerKey) {
|
|
35
|
+
return toAbsoluteUrl(req, `/api/communication_channels/oauth/${providerKey}/callback`);
|
|
36
|
+
}
|
|
37
|
+
async function POST(req, context) {
|
|
38
|
+
const { provider } = await context.params;
|
|
39
|
+
if (!/^[a-z0-9_-]+$/i.test(provider)) {
|
|
40
|
+
return NextResponse.json({ error: "Invalid provider" }, { status: 400 });
|
|
41
|
+
}
|
|
42
|
+
const adapter = getChannelAdapter(provider);
|
|
43
|
+
if (!adapter) {
|
|
44
|
+
return NextResponse.json(
|
|
45
|
+
{ error: `No ChannelAdapter for provider: ${provider}` },
|
|
46
|
+
{ status: 404 }
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (typeof adapter.buildOAuthAuthorizeUrl !== "function") {
|
|
50
|
+
return NextResponse.json(
|
|
51
|
+
{ error: `Provider '${provider}' does not support OAuth (no buildOAuthAuthorizeUrl)` },
|
|
52
|
+
{ status: 400 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const auth = await getAuthFromRequest(req);
|
|
56
|
+
if (!auth?.sub || !auth?.tenantId) {
|
|
57
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
58
|
+
}
|
|
59
|
+
let body;
|
|
60
|
+
try {
|
|
61
|
+
const json = await readJsonSafe(req, {});
|
|
62
|
+
body = initiateBodySchema.parse(json ?? {});
|
|
63
|
+
} catch (err) {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: err instanceof Error ? err.message : "Invalid request body" },
|
|
66
|
+
{ status: 422 }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const container = await createRequestContainer();
|
|
70
|
+
const credentialsService = (() => {
|
|
71
|
+
try {
|
|
72
|
+
return container.resolve("integrationCredentialsService");
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
const credentials = await resolveOAuthClientCredentials(credentialsService, provider, {
|
|
78
|
+
tenantId: auth.tenantId,
|
|
79
|
+
organizationId: auth.orgId ?? null
|
|
80
|
+
});
|
|
81
|
+
if (!credentials) {
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{
|
|
84
|
+
error: `${provider} is not configured for this workspace yet. An administrator must add the OAuth Client ID and Secret under Integrations before mailboxes can be connected.`,
|
|
85
|
+
code: "oauth_client_not_configured"
|
|
86
|
+
},
|
|
87
|
+
{ status: 409 }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const redirectUri = defaultRedirectUri(req, provider);
|
|
91
|
+
const stateEnvelope = createOAuthState({
|
|
92
|
+
userId: auth.sub,
|
|
93
|
+
tenantId: auth.tenantId,
|
|
94
|
+
organizationId: auth.orgId ?? null,
|
|
95
|
+
providerKey: provider,
|
|
96
|
+
returnUrl: normalizeOAuthReturnUrl(body.returnUrl, DEFAULT_OAUTH_RETURN_URL)
|
|
97
|
+
});
|
|
98
|
+
let result;
|
|
99
|
+
try {
|
|
100
|
+
result = await adapter.buildOAuthAuthorizeUrl({
|
|
101
|
+
state: stateEnvelope.stateParam,
|
|
102
|
+
nonce: stateEnvelope.payload.nonce,
|
|
103
|
+
redirectUri,
|
|
104
|
+
credentials,
|
|
105
|
+
scope: {
|
|
106
|
+
tenantId: auth.tenantId,
|
|
107
|
+
organizationId: auth.orgId ?? auth.tenantId
|
|
108
|
+
},
|
|
109
|
+
loginHint: body.loginHint
|
|
110
|
+
});
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (err instanceof OAuthStateError) {
|
|
113
|
+
return NextResponse.json({ error: err.message, code: err.code }, { status: 500 });
|
|
114
|
+
}
|
|
115
|
+
const message = err instanceof Error ? err.message : "Failed to build OAuth authorize URL";
|
|
116
|
+
return NextResponse.json({ error: message }, { status: 502 });
|
|
117
|
+
}
|
|
118
|
+
const finalCookie = result.extra ? (await import("../../../../../lib/oauth-state.js")).encryptOAuthState({
|
|
119
|
+
...stateEnvelope.payload,
|
|
120
|
+
extra: { ...stateEnvelope.payload.extra ?? {}, ...result.extra }
|
|
121
|
+
}) : stateEnvelope.cookie;
|
|
122
|
+
const response = NextResponse.json({ authorizeUrl: result.authorizeUrl });
|
|
123
|
+
response.cookies.set({
|
|
124
|
+
name: COMMUNICATION_CHANNELS_OAUTH_STATE_COOKIE_NAME,
|
|
125
|
+
value: finalCookie,
|
|
126
|
+
httpOnly: true,
|
|
127
|
+
secure: process.env.NODE_ENV === "production",
|
|
128
|
+
sameSite: "lax",
|
|
129
|
+
path: "/",
|
|
130
|
+
maxAge: Math.floor(COMMUNICATION_CHANNELS_OAUTH_STATE_TTL_MS / 1e3)
|
|
131
|
+
});
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
const openApi = {
|
|
135
|
+
tags: ["CommunicationChannels"],
|
|
136
|
+
methods: {
|
|
137
|
+
POST: {
|
|
138
|
+
summary: "Start a per-user channel OAuth flow",
|
|
139
|
+
tags: ["CommunicationChannels"],
|
|
140
|
+
responses: [
|
|
141
|
+
{ status: 200, description: "Authorize URL + state cookie set" },
|
|
142
|
+
{ status: 400, description: "Invalid provider or unsupported (no OAuth)" },
|
|
143
|
+
{ status: 401, description: "Unauthorized" },
|
|
144
|
+
{ status: 422, description: "Invalid request body" },
|
|
145
|
+
{ status: 502, description: "Adapter failed to build authorize URL" }
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var route_default = POST;
|
|
151
|
+
export {
|
|
152
|
+
POST,
|
|
153
|
+
route_default as default,
|
|
154
|
+
metadata,
|
|
155
|
+
openApi
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/communication_channels/api/post/oauth/%5Bprovider%5D/initiate/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { toAbsoluteUrl } from '@open-mercato/shared/lib/url'\nimport { getChannelAdapter } from '../../../../../lib/adapter-registry-singleton'\nimport { resolveOAuthClientCredentials } from '../../../../../lib/oauth-client-config'\nimport {\n COMMUNICATION_CHANNELS_OAUTH_STATE_COOKIE_NAME,\n COMMUNICATION_CHANNELS_OAUTH_STATE_TTL_MS,\n createOAuthState,\n DEFAULT_OAUTH_RETURN_URL,\n isSafeOAuthReturnUrl,\n normalizeOAuthReturnUrl,\n OAuthStateError,\n} from '../../../../../lib/oauth-state'\n\nexport const metadata = {\n path: '/communication_channels/oauth/[provider]/initiate',\n POST: {\n requireAuth: true,\n requireFeatures: ['communication_channels.connect_user_channel'],\n },\n}\n\nconst initiateBodySchema = z.object({\n channelType: z.literal('email').optional(),\n /** Where to send the user after the callback succeeds. Defaults to the profile page. */\n returnUrl: z.string().min(1).max(2048).refine(isSafeOAuthReturnUrl, {\n message: 'returnUrl must be a same-origin path',\n }).optional(),\n /** Optional pre-filled email \u2014 Google `login_hint`. */\n loginHint: z.string().email().optional(),\n})\n\ntype RouteContext = {\n params: Promise<{ provider: string }> | { provider: string }\n}\n\ntype CredentialsServiceLike = {\n resolve: (\n integrationId: string,\n scope: { organizationId: string; tenantId: string },\n ) => Promise<Record<string, unknown> | null>\n}\n\n/**\n * Default profile redirect \u2014 matches the per-user channels page registered by\n * slice 3d (the `/backend/profile/communication-channels` route).\n */\nfunction defaultRedirectUri(req: Request, providerKey: string): string {\n // Derive the origin from the configured app URL / forwarded headers rather\n // than the raw request origin, so the redirect_uri matches the value the\n // provider has registered even when the app sits behind a reverse proxy.\n return toAbsoluteUrl(req, `/api/communication_channels/oauth/${providerKey}/callback`)\n}\n\nexport async function POST(req: Request, context: RouteContext): Promise<Response> {\n const { provider } = await context.params\n if (!/^[a-z0-9_-]+$/i.test(provider)) {\n return NextResponse.json({ error: 'Invalid provider' }, { status: 400 })\n }\n\n const adapter = getChannelAdapter(provider)\n if (!adapter) {\n return NextResponse.json(\n { error: `No ChannelAdapter for provider: ${provider}` },\n { status: 404 },\n )\n }\n if (typeof adapter.buildOAuthAuthorizeUrl !== 'function') {\n return NextResponse.json(\n { error: `Provider '${provider}' does not support OAuth (no buildOAuthAuthorizeUrl)` },\n { status: 400 },\n )\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: z.infer<typeof initiateBodySchema>\n try {\n const json = await readJsonSafe(req, {})\n body = initiateBodySchema.parse(json ?? {})\n } catch (err) {\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Invalid request body' },\n { status: 422 },\n )\n }\n\n const container = await createRequestContainer()\n const credentialsService = (() => {\n try {\n return container.resolve('integrationCredentialsService') as CredentialsServiceLike\n } catch {\n return null\n }\n })()\n // OAuth client credentials are tenant-level \u2014 the admin configures them under\n // the `channel_<provider>` integration in the Integrations UI (stored at\n // userId = null). A missing row means the provider has not been set up yet;\n // we surface that as an actionable error instead of handing an empty object to\n // the adapter (which would throw a cryptic Zod \"expected string\" message).\n const credentials = await resolveOAuthClientCredentials(credentialsService, provider, {\n tenantId: auth.tenantId as string,\n organizationId: (auth as { orgId?: string | null }).orgId ?? null,\n })\n if (!credentials) {\n return NextResponse.json(\n {\n error:\n `${provider} is not configured for this workspace yet. An administrator must add the OAuth Client ID and Secret under Integrations before mailboxes can be connected.`,\n code: 'oauth_client_not_configured',\n },\n { status: 409 },\n )\n }\n\n const redirectUri = defaultRedirectUri(req, provider)\n const stateEnvelope = createOAuthState({\n userId: auth.sub as string,\n tenantId: auth.tenantId as string,\n organizationId: (auth as { orgId?: string | null }).orgId ?? null,\n providerKey: provider,\n returnUrl: normalizeOAuthReturnUrl(body.returnUrl, DEFAULT_OAUTH_RETURN_URL),\n })\n\n let result\n try {\n result = await adapter.buildOAuthAuthorizeUrl({\n state: stateEnvelope.stateParam,\n nonce: stateEnvelope.payload.nonce,\n redirectUri,\n credentials,\n scope: {\n tenantId: auth.tenantId as string,\n organizationId:\n (auth as { orgId?: string | null }).orgId ?? (auth.tenantId as string),\n },\n loginHint: body.loginHint,\n })\n } catch (err) {\n if (err instanceof OAuthStateError) {\n return NextResponse.json({ error: err.message, code: err.code }, { status: 500 })\n }\n const message = err instanceof Error ? err.message : 'Failed to build OAuth authorize URL'\n return NextResponse.json({ error: message }, { status: 502 })\n }\n\n // If the adapter packed extras (PKCE verifier, scopes), bake them into the\n // state cookie now so the callback handler can pass them to exchangeOAuthCode.\n const finalCookie = result.extra\n ? (await import('../../../../../lib/oauth-state')).encryptOAuthState({\n ...stateEnvelope.payload,\n extra: { ...(stateEnvelope.payload.extra ?? {}), ...result.extra },\n })\n : stateEnvelope.cookie\n\n const response = NextResponse.json({ authorizeUrl: result.authorizeUrl })\n response.cookies.set({\n name: COMMUNICATION_CHANNELS_OAUTH_STATE_COOKIE_NAME,\n value: finalCookie,\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: Math.floor(COMMUNICATION_CHANNELS_OAUTH_STATE_TTL_MS / 1000),\n })\n return response\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n POST: {\n summary: 'Start a per-user channel OAuth flow',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 200, description: 'Authorize URL + state cookie set' },\n { status: 400, description: 'Invalid provider or unsupported (no OAuth)' },\n { status: 401, description: 'Unauthorized' },\n { status: 422, description: 'Invalid request body' },\n { status: 502, description: 'Adapter failed to build authorize URL' },\n ],\n },\n },\n}\nexport default POST\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
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 { sendAsUser } from "../../../lib/send-as-user.js";
|
|
7
|
+
import { validateRouteMutationGuard } from "../../../lib/route-mutation-guard.js";
|
|
8
|
+
const metadata = {
|
|
9
|
+
path: "/communication_channels/send-as-user",
|
|
10
|
+
POST: {
|
|
11
|
+
requireAuth: true,
|
|
12
|
+
requireFeatures: ["communication_channels.manage"]
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const bodySchema = z.object({
|
|
16
|
+
/** ID of the user-owned channel to send from. Caller MUST own the channel. */
|
|
17
|
+
userChannelId: z.string().uuid(),
|
|
18
|
+
to: z.array(z.string().email()).min(1),
|
|
19
|
+
cc: z.array(z.string().email()).optional(),
|
|
20
|
+
bcc: z.array(z.string().email()).optional(),
|
|
21
|
+
// `.regex(/^[^\r\n]*$/)` rejects CR/LF so a caller cannot inject extra email
|
|
22
|
+
// headers (e.g. a hidden Bcc) through the subject / threading fields. The
|
|
23
|
+
// outbound MIME assembler also sanitizes these, but fail fast at the edge.
|
|
24
|
+
subject: z.string().min(1).max(500).regex(/^[^\r\n]*$/),
|
|
25
|
+
body: z.object({
|
|
26
|
+
plain: z.string().max(5e4).optional(),
|
|
27
|
+
html: z.string().max(2e5).optional()
|
|
28
|
+
}),
|
|
29
|
+
inReplyTo: z.string().min(1).max(500).regex(/^[^\r\n]*$/).optional(),
|
|
30
|
+
references: z.array(z.string().min(1).max(500).regex(/^[^\r\n]*$/)).optional(),
|
|
31
|
+
/**
|
|
32
|
+
* Free-form metadata persisted on the resulting MessageChannelLink. Used by
|
|
33
|
+
* downstream subscribers (e.g. the customers module's link-channel-message
|
|
34
|
+
* subscriber) to anchor the sent message back to a CRM Person or honor a
|
|
35
|
+
* caller-specified visibility flag. Keys are caller-defined; the hub does
|
|
36
|
+
* not interpret them.
|
|
37
|
+
*/
|
|
38
|
+
channelMetadata: z.record(z.string(), z.unknown()).optional()
|
|
39
|
+
});
|
|
40
|
+
async function POST(req) {
|
|
41
|
+
const auth = await getAuthFromRequest(req);
|
|
42
|
+
if (!auth?.sub || !auth?.tenantId) {
|
|
43
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
44
|
+
}
|
|
45
|
+
let body;
|
|
46
|
+
try {
|
|
47
|
+
body = bodySchema.parse(await readJsonSafe(req, null));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: err instanceof Error ? err.message : "Invalid request body" },
|
|
51
|
+
{ status: 422 }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const container = await createRequestContainer();
|
|
55
|
+
const organizationId = auth.orgId ?? null;
|
|
56
|
+
const guard = await validateRouteMutationGuard({
|
|
57
|
+
container,
|
|
58
|
+
req,
|
|
59
|
+
auth,
|
|
60
|
+
input: {
|
|
61
|
+
resourceKind: "communication_channels.channel",
|
|
62
|
+
resourceId: body.userChannelId,
|
|
63
|
+
operation: "custom",
|
|
64
|
+
mutationPayload: body
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if ("response" in guard) return guard.response;
|
|
68
|
+
const result = await sendAsUser(
|
|
69
|
+
container,
|
|
70
|
+
{ userId: auth.sub, tenantId: auth.tenantId, organizationId, auth },
|
|
71
|
+
body
|
|
72
|
+
);
|
|
73
|
+
if (!result.ok) {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
result.fieldErrors ? { error: result.error, fieldErrors: result.fieldErrors } : { error: result.error },
|
|
76
|
+
{ status: result.status }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
await guard.afterSuccess();
|
|
80
|
+
return NextResponse.json(
|
|
81
|
+
{
|
|
82
|
+
messageId: result.messageId,
|
|
83
|
+
threadId: result.threadId,
|
|
84
|
+
channelId: result.channelId,
|
|
85
|
+
providerKey: result.providerKey,
|
|
86
|
+
enqueuedForDelivery: true
|
|
87
|
+
},
|
|
88
|
+
{ status: 202 }
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const openApi = {
|
|
92
|
+
tags: ["CommunicationChannels"],
|
|
93
|
+
methods: {
|
|
94
|
+
POST: {
|
|
95
|
+
summary: "Send a message through the current user's own channel",
|
|
96
|
+
tags: ["CommunicationChannels"],
|
|
97
|
+
responses: [
|
|
98
|
+
{ status: 202, description: "Message persisted; outbound delivery enqueued" },
|
|
99
|
+
{ status: 401, description: "Unauthorized" },
|
|
100
|
+
{ status: 403, description: "Cannot send through a channel you don't own" },
|
|
101
|
+
{ status: 404, description: "Channel not found" },
|
|
102
|
+
{ status: 409, description: "Channel in a non-deliverable transitional status" },
|
|
103
|
+
{ status: 422, description: "Invalid body, or channel requires_reauth / disconnected" }
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var route_default = POST;
|
|
109
|
+
export {
|
|
110
|
+
POST,
|
|
111
|
+
route_default as default,
|
|
112
|
+
metadata,
|
|
113
|
+
openApi
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/communication_channels/api/post/send-as-user/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { sendAsUser } from '../../../lib/send-as-user'\nimport { validateRouteMutationGuard } from '../../../lib/route-mutation-guard'\n\nexport const metadata = {\n path: '/communication_channels/send-as-user',\n POST: {\n requireAuth: true,\n requireFeatures: ['communication_channels.manage'],\n },\n}\n\nconst bodySchema = z.object({\n /** ID of the user-owned channel to send from. Caller MUST own the channel. */\n userChannelId: z.string().uuid(),\n to: z.array(z.string().email()).min(1),\n cc: z.array(z.string().email()).optional(),\n bcc: z.array(z.string().email()).optional(),\n // `.regex(/^[^\\r\\n]*$/)` rejects CR/LF so a caller cannot inject extra email\n // headers (e.g. a hidden Bcc) through the subject / threading fields. The\n // outbound MIME assembler also sanitizes these, but fail fast at the edge.\n subject: z.string().min(1).max(500).regex(/^[^\\r\\n]*$/),\n body: z.object({\n plain: z.string().max(50_000).optional(),\n html: z.string().max(200_000).optional(),\n }),\n inReplyTo: z.string().min(1).max(500).regex(/^[^\\r\\n]*$/).optional(),\n references: z.array(z.string().min(1).max(500).regex(/^[^\\r\\n]*$/)).optional(),\n /**\n * Free-form metadata persisted on the resulting MessageChannelLink. Used by\n * downstream subscribers (e.g. the customers module's link-channel-message\n * subscriber) to anchor the sent message back to a CRM Person or honor a\n * caller-specified visibility flag. Keys are caller-defined; the hub does\n * not interpret them.\n */\n channelMetadata: z.record(z.string(), z.unknown()).optional(),\n})\n\n/**\n * Programmatic send-as-user facade (HTTP entry point).\n *\n * Thin wrapper around the in-process `sendAsUser` lib facade\n * (`../../../lib/send-as-user`), which is also resolvable via DI as\n * `communicationChannelsSendAsUser` for in-process cross-module callers.\n * Returns once the Message is persisted; the actual external send is async.\n */\nexport async function POST(req: Request): Promise<Response> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: z.infer<typeof bodySchema>\n try {\n body = bodySchema.parse(await readJsonSafe(req, null))\n } catch (err) {\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Invalid request body' },\n { status: 422 },\n )\n }\n\n const container = await createRequestContainer()\n const organizationId = (auth as { orgId?: string | null }).orgId ?? null\n const guard = await validateRouteMutationGuard({\n container,\n req,\n auth,\n input: {\n resourceKind: 'communication_channels.channel',\n resourceId: body.userChannelId,\n operation: 'custom',\n mutationPayload: body as unknown as Record<string, unknown>,\n },\n })\n if ('response' in guard) return guard.response\n\n const result = await sendAsUser(\n container,\n { userId: auth.sub as string, tenantId: auth.tenantId as string, organizationId, auth },\n body,\n )\n\n if (!result.ok) {\n return NextResponse.json(\n result.fieldErrors ? { error: result.error, fieldErrors: result.fieldErrors } : { error: result.error },\n { status: result.status },\n )\n }\n await guard.afterSuccess()\n\n return NextResponse.json(\n {\n messageId: result.messageId,\n threadId: result.threadId,\n channelId: result.channelId,\n providerKey: result.providerKey,\n enqueuedForDelivery: true,\n },\n { status: 202 },\n )\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n POST: {\n summary: 'Send a message through the current user\\'s own channel',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 202, description: 'Message persisted; outbound delivery enqueued' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Cannot send through a channel you don\\'t own' },\n { status: 404, description: 'Channel not found' },\n { status: 409, description: 'Channel in a non-deliverable transitional status' },\n { status: 422, description: 'Invalid body, or channel requires_reauth / disconnected' },\n ],\n },\n },\n}\nexport default POST\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,kCAAkC;AAEpC,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,iBAAiB,CAAC,+BAA+B;AAAA,EACnD;AACF;AAEA,MAAM,aAAa,EAAE,OAAO;AAAA;AAAA,EAE1B,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC;AAAA,EACrC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,SAAS;AAAA,EACzC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA,EAI1C,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,MAAM,YAAY;AAAA,EACtD,MAAM,EAAE,OAAO;AAAA,IACb,OAAO,EAAE,OAAO,EAAE,IAAI,GAAM,EAAE,SAAS;AAAA,IACvC,MAAM,EAAE,OAAO,EAAE,IAAI,GAAO,EAAE,SAAS;AAAA,EACzC,CAAC;AAAA,EACD,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,MAAM,YAAY,EAAE,SAAS;AAAA,EACnE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ7E,iBAAiB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAC9D,CAAC;AAUD,eAAsB,KAAK,KAAiC;AAC1D,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,OAAO,CAAC,MAAM,UAAU;AACjC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,WAAW,MAAM,MAAM,aAAa,KAAK,IAAI,CAAC;AAAA,EACvD,SAAS,KAAK;AACZ,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,uBAAuB;AAAA,MACrE,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,iBAAkB,KAAmC,SAAS;AACpE,QAAM,QAAQ,MAAM,2BAA2B;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,MACL,cAAc;AAAA,MACd,YAAY,KAAK;AAAA,MACjB,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA,EACF,CAAC;AACD,MAAI,cAAc,MAAO,QAAO,MAAM;AAEtC,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA,EAAE,QAAQ,KAAK,KAAe,UAAU,KAAK,UAAoB,gBAAgB,KAAK;AAAA,IACtF;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,WAAO,aAAa;AAAA,MAClB,OAAO,cAAc,EAAE,OAAO,OAAO,OAAO,aAAa,OAAO,YAAY,IAAI,EAAE,OAAO,OAAO,MAAM;AAAA,MACtG,EAAE,QAAQ,OAAO,OAAO;AAAA,IAC1B;AAAA,EACF;AACA,QAAM,MAAM,aAAa;AAEzB,SAAO,aAAa;AAAA,IAClB;AAAA,MACE,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,aAAa,OAAO;AAAA,MACpB,qBAAqB;AAAA,IACvB;AAAA,IACA,EAAE,QAAQ,IAAI;AAAA,EAChB;AACF;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,uBAAuB;AAAA,EAC9B,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,MAAM,CAAC,uBAAuB;AAAA,MAC9B,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,gDAAgD;AAAA,QAC5E,EAAE,QAAQ,KAAK,aAAa,eAAe;AAAA,QAC3C,EAAE,QAAQ,KAAK,aAAa,8CAA+C;AAAA,QAC3E,EAAE,QAAQ,KAAK,aAAa,oBAAoB;AAAA,QAChD,EAAE,QAAQ,KAAK,aAAa,mDAAmD;AAAA,QAC/E,EAAE,QAAQ,KAAK,aAAa,0DAA0D;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AACF;AACA,IAAO,gBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
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 { ExternalConversation, MessageChannelLink } from "../../../data/entities.js";
|
|
7
|
+
import {
|
|
8
|
+
COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID
|
|
9
|
+
} from "../../../commands/connect-credential-channel.js";
|
|
10
|
+
import { emitCommunicationChannelsEvent } from "../../../events.js";
|
|
11
|
+
import {
|
|
12
|
+
TEST_SEED_PROVIDER_KEY,
|
|
13
|
+
ensureTestSeedAdapterRegistered,
|
|
14
|
+
isTestChannelSeedingEnabled
|
|
15
|
+
} from "../../../lib/test-seed.js";
|
|
16
|
+
const metadata = {
|
|
17
|
+
path: "/communication_channels/test-seed",
|
|
18
|
+
POST: {
|
|
19
|
+
requireAuth: true,
|
|
20
|
+
requireFeatures: ["communication_channels.connect_user_channel"]
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const addressObjectSchema = z.object({ address: z.string(), name: z.string().optional() });
|
|
24
|
+
const addressFieldSchema = z.union([
|
|
25
|
+
z.string(),
|
|
26
|
+
addressObjectSchema,
|
|
27
|
+
z.array(z.union([z.string(), addressObjectSchema]))
|
|
28
|
+
]);
|
|
29
|
+
const connectChannelSchema = z.object({
|
|
30
|
+
action: z.literal("connect-channel"),
|
|
31
|
+
displayName: z.string().min(1).max(255).optional(),
|
|
32
|
+
externalIdentifier: z.string().min(1).max(255).optional()
|
|
33
|
+
});
|
|
34
|
+
const emitInboundSchema = z.object({
|
|
35
|
+
action: z.literal("emit-inbound"),
|
|
36
|
+
/** Channel that owns the inbound message; controls authorUserId + default visibility. */
|
|
37
|
+
channelId: z.string().uuid(),
|
|
38
|
+
/** Provider key persisted on the link (defaults to the stub provider). */
|
|
39
|
+
providerKey: z.string().min(1).max(64).optional(),
|
|
40
|
+
/** Normalized inbound addresses (stored under channelPayload). */
|
|
41
|
+
from: addressFieldSchema.optional(),
|
|
42
|
+
to: addressFieldSchema.optional(),
|
|
43
|
+
cc: addressFieldSchema.optional(),
|
|
44
|
+
subject: z.string().max(500).optional(),
|
|
45
|
+
bodyText: z.string().max(2e5).optional(),
|
|
46
|
+
/** RFC2822 Message-ID of this inbound message (for In-Reply-To matching). */
|
|
47
|
+
messageId: z.string().max(500).optional(),
|
|
48
|
+
/** RFC2822 In-Reply-To header (threading-inheritance fallback). */
|
|
49
|
+
inReplyTo: z.string().max(500).optional(),
|
|
50
|
+
references: z.array(z.string().max(500)).max(50).optional(),
|
|
51
|
+
/**
|
|
52
|
+
* Open Mercato `messages.message` thread id this inbound message belongs to.
|
|
53
|
+
* When set, a `messages.message` row is created with this `threadId` so the
|
|
54
|
+
* hub-thread inheritance join can resolve a Person from a sibling message.
|
|
55
|
+
*/
|
|
56
|
+
messageThreadId: z.string().uuid().optional()
|
|
57
|
+
});
|
|
58
|
+
const bodySchema = z.discriminatedUnion("action", [connectChannelSchema, emitInboundSchema]);
|
|
59
|
+
async function POST(req) {
|
|
60
|
+
if (!isTestChannelSeedingEnabled()) {
|
|
61
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
62
|
+
}
|
|
63
|
+
const auth = await getAuthFromRequest(req);
|
|
64
|
+
if (!auth?.sub || !auth?.tenantId) {
|
|
65
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
66
|
+
}
|
|
67
|
+
let body;
|
|
68
|
+
try {
|
|
69
|
+
body = bodySchema.parse(await readJsonSafe(req, null));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return NextResponse.json(
|
|
72
|
+
{ error: err instanceof Error ? err.message : "Invalid request body" },
|
|
73
|
+
{ status: 422 }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const container = await createRequestContainer();
|
|
77
|
+
ensureTestSeedAdapterRegistered();
|
|
78
|
+
const tenantId = auth.tenantId;
|
|
79
|
+
const organizationId = auth.orgId ?? null;
|
|
80
|
+
const userId = auth.sub;
|
|
81
|
+
if (body.action === "connect-channel") {
|
|
82
|
+
const stamp = Date.now();
|
|
83
|
+
const commandBus = container.resolve("commandBus");
|
|
84
|
+
const input = {
|
|
85
|
+
providerKey: TEST_SEED_PROVIDER_KEY,
|
|
86
|
+
displayName: body.displayName ?? `Test Seed Channel ${stamp}`,
|
|
87
|
+
credentials: {
|
|
88
|
+
username: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
|
|
89
|
+
fromAddress: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`
|
|
90
|
+
},
|
|
91
|
+
userId,
|
|
92
|
+
scope: { tenantId, organizationId }
|
|
93
|
+
};
|
|
94
|
+
const { result } = await commandBus.execute(COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID, {
|
|
95
|
+
input,
|
|
96
|
+
ctx: {
|
|
97
|
+
container,
|
|
98
|
+
auth,
|
|
99
|
+
organizationScope: null,
|
|
100
|
+
selectedOrganizationId: organizationId,
|
|
101
|
+
organizationIds: organizationId ? [organizationId] : null
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (result.status !== "connected") {
|
|
105
|
+
return NextResponse.json(
|
|
106
|
+
{ error: "[internal] test-seed connect failed", detail: result },
|
|
107
|
+
{ status: 500 }
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return NextResponse.json(
|
|
111
|
+
{ channelId: result.channelId, externalIdentifier: result.externalIdentifier },
|
|
112
|
+
{ status: 201 }
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const em = container.resolve("em").fork();
|
|
116
|
+
const providerKey = body.providerKey ?? TEST_SEED_PROVIDER_KEY;
|
|
117
|
+
const conversation = em.create(ExternalConversation, {
|
|
118
|
+
channelId: body.channelId,
|
|
119
|
+
externalConversationId: `inbound-seed:${Date.now()}:${Math.random().toString(16).slice(2, 8)}`,
|
|
120
|
+
subject: body.subject ?? null,
|
|
121
|
+
tenantId,
|
|
122
|
+
organizationId,
|
|
123
|
+
lastMessageAt: /* @__PURE__ */ new Date()
|
|
124
|
+
});
|
|
125
|
+
em.persist(conversation);
|
|
126
|
+
await em.flush();
|
|
127
|
+
const messageRows = await em.getConnection().execute(
|
|
128
|
+
`INSERT INTO messages
|
|
129
|
+
(type, thread_id, sender_user_id, subject, body, body_format, priority, status,
|
|
130
|
+
is_draft, sent_at, visibility, source_entity_type, source_entity_id,
|
|
131
|
+
tenant_id, organization_id, created_at, updated_at)
|
|
132
|
+
VALUES
|
|
133
|
+
(?, ?, ?, ?, ?, 'text', 'normal', 'sent',
|
|
134
|
+
false, now(), 'public', 'communication_channels.test_seed_inbound', ?,
|
|
135
|
+
?, ?, now(), now())
|
|
136
|
+
RETURNING id`,
|
|
137
|
+
[
|
|
138
|
+
`channel.${providerKey}`,
|
|
139
|
+
body.messageThreadId ?? null,
|
|
140
|
+
userId,
|
|
141
|
+
body.subject ?? "(no subject)",
|
|
142
|
+
body.bodyText ?? "",
|
|
143
|
+
body.channelId,
|
|
144
|
+
tenantId,
|
|
145
|
+
organizationId
|
|
146
|
+
]
|
|
147
|
+
);
|
|
148
|
+
const messageId = messageRows[0]?.id;
|
|
149
|
+
if (!messageId) {
|
|
150
|
+
return NextResponse.json({ error: "[internal] failed to seed message row" }, { status: 500 });
|
|
151
|
+
}
|
|
152
|
+
const link = em.create(MessageChannelLink, {
|
|
153
|
+
messageId,
|
|
154
|
+
externalConversationId: conversation.id,
|
|
155
|
+
providerKey,
|
|
156
|
+
channelType: "email",
|
|
157
|
+
direction: "inbound",
|
|
158
|
+
deliveryStatus: "delivered",
|
|
159
|
+
channelPayload: {
|
|
160
|
+
...body.from !== void 0 ? { from: body.from } : {},
|
|
161
|
+
...body.to !== void 0 ? { to: body.to } : {},
|
|
162
|
+
...body.cc !== void 0 ? { cc: body.cc } : {},
|
|
163
|
+
...body.subject !== void 0 ? { subject: body.subject } : {},
|
|
164
|
+
...body.bodyText !== void 0 ? { text: body.bodyText } : {},
|
|
165
|
+
...body.inReplyTo !== void 0 ? { inReplyTo: body.inReplyTo } : {},
|
|
166
|
+
...body.references !== void 0 ? { references: body.references } : {}
|
|
167
|
+
},
|
|
168
|
+
channelContentType: "text/plain",
|
|
169
|
+
channelMetadata: {
|
|
170
|
+
...body.messageId !== void 0 ? { messageId: body.messageId } : {}
|
|
171
|
+
},
|
|
172
|
+
tenantId,
|
|
173
|
+
organizationId
|
|
174
|
+
});
|
|
175
|
+
em.persist(link);
|
|
176
|
+
await em.flush();
|
|
177
|
+
await emitCommunicationChannelsEvent(
|
|
178
|
+
"communication_channels.message.received",
|
|
179
|
+
{
|
|
180
|
+
channelLinkId: link.id,
|
|
181
|
+
channelId: body.channelId,
|
|
182
|
+
providerKey,
|
|
183
|
+
direction: "inbound",
|
|
184
|
+
tenantId,
|
|
185
|
+
organizationId
|
|
186
|
+
},
|
|
187
|
+
{ persistent: true }
|
|
188
|
+
);
|
|
189
|
+
return NextResponse.json(
|
|
190
|
+
{ channelLinkId: link.id, messageId, conversationId: conversation.id },
|
|
191
|
+
{ status: 201 }
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const openApi = {
|
|
195
|
+
tags: ["CommunicationChannels"],
|
|
196
|
+
methods: {
|
|
197
|
+
POST: {
|
|
198
|
+
summary: "Test-only: seed a connected channel or emit an inbound message (env-gated)",
|
|
199
|
+
tags: ["CommunicationChannels"],
|
|
200
|
+
responses: [
|
|
201
|
+
{ status: 201, description: "Channel seeded / inbound message emitted" },
|
|
202
|
+
{ status: 401, description: "Unauthorized" },
|
|
203
|
+
{ status: 404, description: "Test channel seeding disabled (production default)" },
|
|
204
|
+
{ status: 422, description: "Invalid request body" },
|
|
205
|
+
{ status: 500, description: "Seed failed" }
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
var route_default = POST;
|
|
211
|
+
export {
|
|
212
|
+
POST,
|
|
213
|
+
route_default as default,
|
|
214
|
+
metadata,
|
|
215
|
+
openApi
|
|
216
|
+
};
|
|
217
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/communication_channels/api/post/test-seed/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { ExternalConversation, MessageChannelLink } from '../../../data/entities'\nimport {\n COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID,\n type ConnectCredentialChannelInput,\n type ConnectCredentialChannelResult,\n} from '../../../commands/connect-credential-channel'\nimport { emitCommunicationChannelsEvent } from '../../../events'\nimport {\n TEST_SEED_PROVIDER_KEY,\n ensureTestSeedAdapterRegistered,\n isTestChannelSeedingEnabled,\n} from '../../../lib/test-seed'\n\n/**\n * TEST-ONLY channel seeding endpoint.\n *\n * Gated by `OM_ENABLE_TEST_CHANNEL_SEEDING` \u2014 when the flag is unset (the\n * production default) every request returns 404, so this route is invisible and\n * inert in production. See `lib/test-seed.ts` for the full rationale.\n *\n * Two actions, both scoped to the caller's tenant/org:\n * - `connect-channel`: connect a network-free `__test_seed__` channel owned by\n * the caller (delegates to the real connect-credential command so the channel\n * persists credentials + lands in `status='connected'`). Enables the outbound\n * compose \u2192 deliver \u2192 `.sent` chain to complete in CI.\n * - `emit-inbound`: insert an inbound `MessageChannelLink` (+ a `messages.message`\n * row for threading) and emit `communication_channels.message.received` so the\n * customers link-channel-message subscriber runs against real Postgres. Enables\n * the inbound auto-link tests (TC-CRM-EMAIL-002..005).\n */\nexport const metadata = {\n path: '/communication_channels/test-seed',\n POST: {\n requireAuth: true,\n requireFeatures: ['communication_channels.connect_user_channel'],\n },\n}\n\nconst addressObjectSchema = z.object({ address: z.string(), name: z.string().optional() })\nconst addressFieldSchema = z.union([\n z.string(),\n addressObjectSchema,\n z.array(z.union([z.string(), addressObjectSchema])),\n])\n\nconst connectChannelSchema = z.object({\n action: z.literal('connect-channel'),\n displayName: z.string().min(1).max(255).optional(),\n externalIdentifier: z.string().min(1).max(255).optional(),\n})\n\nconst emitInboundSchema = z.object({\n action: z.literal('emit-inbound'),\n /** Channel that owns the inbound message; controls authorUserId + default visibility. */\n channelId: z.string().uuid(),\n /** Provider key persisted on the link (defaults to the stub provider). */\n providerKey: z.string().min(1).max(64).optional(),\n /** Normalized inbound addresses (stored under channelPayload). */\n from: addressFieldSchema.optional(),\n to: addressFieldSchema.optional(),\n cc: addressFieldSchema.optional(),\n subject: z.string().max(500).optional(),\n bodyText: z.string().max(200_000).optional(),\n /** RFC2822 Message-ID of this inbound message (for In-Reply-To matching). */\n messageId: z.string().max(500).optional(),\n /** RFC2822 In-Reply-To header (threading-inheritance fallback). */\n inReplyTo: z.string().max(500).optional(),\n references: z.array(z.string().max(500)).max(50).optional(),\n /**\n * Open Mercato `messages.message` thread id this inbound message belongs to.\n * When set, a `messages.message` row is created with this `threadId` so the\n * hub-thread inheritance join can resolve a Person from a sibling message.\n */\n messageThreadId: z.string().uuid().optional(),\n})\n\nconst bodySchema = z.discriminatedUnion('action', [connectChannelSchema, emitInboundSchema])\n\nexport async function POST(req: Request): Promise<Response> {\n // Fail-closed: invisible in production. Mirrors an unknown route (404) rather\n // than 403 so the surface leaks nothing when the flag is off.\n if (!isTestChannelSeedingEnabled()) {\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: z.infer<typeof bodySchema>\n try {\n body = bodySchema.parse(await readJsonSafe(req, null))\n } catch (err) {\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Invalid request body' },\n { status: 422 },\n )\n }\n\n const container = await createRequestContainer()\n // Defensive: make sure the stub adapter is registered for this process even if\n // a worker-only node skipped module di registration.\n ensureTestSeedAdapterRegistered()\n\n const tenantId = auth.tenantId as string\n const organizationId = (auth as { orgId?: string | null }).orgId ?? null\n const userId = auth.sub as string\n\n if (body.action === 'connect-channel') {\n const stamp = Date.now()\n const commandBus = container.resolve('commandBus') as CommandBus\n const input: ConnectCredentialChannelInput = {\n providerKey: TEST_SEED_PROVIDER_KEY,\n displayName: body.displayName ?? `Test Seed Channel ${stamp}`,\n credentials: {\n username: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,\n fromAddress: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,\n },\n userId,\n scope: { tenantId, organizationId },\n }\n const { result } = await commandBus.execute<\n ConnectCredentialChannelInput,\n ConnectCredentialChannelResult\n >(COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID, {\n input,\n ctx: {\n container,\n auth: auth as never,\n organizationScope: null,\n selectedOrganizationId: organizationId,\n organizationIds: organizationId ? [organizationId] : null,\n },\n })\n if (result.status !== 'connected') {\n return NextResponse.json(\n { error: '[internal] test-seed connect failed', detail: result },\n { status: 500 },\n )\n }\n return NextResponse.json(\n { channelId: result.channelId, externalIdentifier: result.externalIdentifier },\n { status: 201 },\n )\n }\n\n // action === 'emit-inbound'\n const em = (container.resolve('em') as EntityManager).fork()\n const providerKey = body.providerKey ?? TEST_SEED_PROVIDER_KEY\n\n // A MessageChannelLink requires a non-null external_conversation_id (FK) and\n // message_id. Create a synthetic conversation + (optionally threaded) message\n // so the link is shaped like a real inbound row the subscriber can consume.\n const conversation = em.create(ExternalConversation, {\n channelId: body.channelId,\n externalConversationId: `inbound-seed:${Date.now()}:${Math.random().toString(16).slice(2, 8)}`,\n subject: body.subject ?? null,\n tenantId,\n organizationId,\n lastMessageAt: new Date(),\n })\n em.persist(conversation)\n await em.flush()\n\n // Insert the platform `messages.message` row via raw SQL rather than importing\n // the messages module's entity class (cross-module ORM coupling rule). Only\n // `thread_id` matters for the hub-thread inheritance join (TC-CRM-EMAIL-005);\n // the rest satisfy NOT NULL constraints.\n const messageRows = (await em.getConnection().execute(\n `INSERT INTO messages\n (type, thread_id, sender_user_id, subject, body, body_format, priority, status,\n is_draft, sent_at, visibility, source_entity_type, source_entity_id,\n tenant_id, organization_id, created_at, updated_at)\n VALUES\n (?, ?, ?, ?, ?, 'text', 'normal', 'sent',\n false, now(), 'public', 'communication_channels.test_seed_inbound', ?,\n ?, ?, now(), now())\n RETURNING id`,\n [\n `channel.${providerKey}`,\n body.messageThreadId ?? null,\n userId,\n body.subject ?? '(no subject)',\n body.bodyText ?? '',\n body.channelId,\n tenantId,\n organizationId,\n ],\n )) as Array<{ id: string }>\n const messageId = messageRows[0]?.id\n if (!messageId) {\n return NextResponse.json({ error: '[internal] failed to seed message row' }, { status: 500 })\n }\n\n const link = em.create(MessageChannelLink, {\n messageId,\n externalConversationId: conversation.id,\n providerKey,\n channelType: 'email',\n direction: 'inbound',\n deliveryStatus: 'delivered',\n channelPayload: {\n ...(body.from !== undefined ? { from: body.from } : {}),\n ...(body.to !== undefined ? { to: body.to } : {}),\n ...(body.cc !== undefined ? { cc: body.cc } : {}),\n ...(body.subject !== undefined ? { subject: body.subject } : {}),\n ...(body.bodyText !== undefined ? { text: body.bodyText } : {}),\n ...(body.inReplyTo !== undefined ? { inReplyTo: body.inReplyTo } : {}),\n ...(body.references !== undefined ? { references: body.references } : {}),\n },\n channelContentType: 'text/plain',\n channelMetadata: {\n ...(body.messageId !== undefined ? { messageId: body.messageId } : {}),\n },\n tenantId,\n organizationId,\n })\n em.persist(link)\n await em.flush()\n\n // Emit the hub event through the real event bus so the persistent customers\n // link-channel-message-received subscriber is enqueued to the `events` queue.\n await emitCommunicationChannelsEvent(\n 'communication_channels.message.received',\n {\n channelLinkId: link.id,\n channelId: body.channelId,\n providerKey,\n direction: 'inbound',\n tenantId,\n organizationId,\n },\n { persistent: true },\n )\n\n return NextResponse.json(\n { channelLinkId: link.id, messageId, conversationId: conversation.id },\n { status: 201 },\n )\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n POST: {\n summary: 'Test-only: seed a connected channel or emit an inbound message (env-gated)',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 201, description: 'Channel seeded / inbound message emitted' },\n { status: 401, description: 'Unauthorized' },\n { status: 404, description: 'Test channel seeding disabled (production default)' },\n { status: 422, description: 'Invalid request body' },\n { status: 500, description: 'Seed failed' },\n ],\n },\n },\n}\n\nexport default POST\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|