@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.
- 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 +259 -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 +425 -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
|
@@ -45,8 +45,13 @@ export class SyncExternalIdMapping {
|
|
|
45
45
|
|
|
46
46
|
@Entity({ tableName: 'integration_credentials' })
|
|
47
47
|
@Index({ properties: ['integrationId', 'organizationId', 'tenantId'] })
|
|
48
|
+
@Index({
|
|
49
|
+
name: 'integration_credentials_user_lookup_idx',
|
|
50
|
+
expression:
|
|
51
|
+
`create unique index "integration_credentials_user_lookup_idx" on "integration_credentials" ("integration_id", "organization_id", "tenant_id", "user_id") where "user_id" is not null and "deleted_at" is null`,
|
|
52
|
+
})
|
|
48
53
|
export class IntegrationCredentials {
|
|
49
|
-
[OptionalProps]?: 'createdAt' | 'updatedAt' | 'deletedAt'
|
|
54
|
+
[OptionalProps]?: 'createdAt' | 'updatedAt' | 'deletedAt' | 'userId'
|
|
50
55
|
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
51
56
|
id!: string
|
|
52
57
|
|
|
@@ -62,6 +67,21 @@ export class IntegrationCredentials {
|
|
|
62
67
|
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
63
68
|
tenantId!: string
|
|
64
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Per-user secret scoping (additive — added by the email integration spec).
|
|
72
|
+
*
|
|
73
|
+
* NULL = tenant-wide secret (existing behaviour, e.g. shared WhatsApp Business token).
|
|
74
|
+
* Set = per-user secret (e.g. Jane's personal Gmail OAuth refresh token).
|
|
75
|
+
*
|
|
76
|
+
* Cross-module link to `auth:user` is declared in
|
|
77
|
+
* `packages/core/src/modules/communication_channels/data/extensions.ts` via
|
|
78
|
+
* `EntityExtension` — no raw FOREIGN KEY constraint (root `AGENTS.md` rule:
|
|
79
|
+
* "No direct ORM relationships between modules"). App-layer query callers
|
|
80
|
+
* MUST scope by `user_id` on every per-user credential read.
|
|
81
|
+
*/
|
|
82
|
+
@Property({ name: 'user_id', type: 'uuid', nullable: true })
|
|
83
|
+
userId?: string | null
|
|
84
|
+
|
|
65
85
|
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
66
86
|
createdAt: Date = new Date()
|
|
67
87
|
|
|
@@ -39,6 +39,34 @@ function normalizeCredentialsRecord(value: unknown): Record<string, unknown> {
|
|
|
39
39
|
return isRecordValue(parsed) ? parsed : {}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Build the where-filter for credential lookups.
|
|
44
|
+
*
|
|
45
|
+
* Per-user scoping (added 2026-05-26): when `scope.userId` is set, the filter
|
|
46
|
+
* matches the row owned by that user — different users on the same tenant get
|
|
47
|
+
* their OWN row for the same provider. When `scope.userId` is `undefined` /
|
|
48
|
+
* `null`, the filter matches tenant-wide credentials (existing behaviour,
|
|
49
|
+
* e.g. shared Stripe/Akeneo API keys).
|
|
50
|
+
*
|
|
51
|
+
* The partial unique index `integration_credentials_user_lookup_idx` enforces
|
|
52
|
+
* uniqueness across `(integration_id, organization_id, tenant_id, user_id)`
|
|
53
|
+
* when `user_id IS NOT NULL`.
|
|
54
|
+
*/
|
|
55
|
+
export function buildCredentialsFilter(integrationId: string, scope: IntegrationScope) {
|
|
56
|
+
const base = {
|
|
57
|
+
integrationId,
|
|
58
|
+
organizationId: scope.organizationId,
|
|
59
|
+
tenantId: scope.tenantId,
|
|
60
|
+
deletedAt: null,
|
|
61
|
+
} as Record<string, unknown>
|
|
62
|
+
if (scope.userId) {
|
|
63
|
+
base.userId = scope.userId
|
|
64
|
+
} else {
|
|
65
|
+
base.userId = null
|
|
66
|
+
}
|
|
67
|
+
return base
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
export function createCredentialsService(em: EntityManager) {
|
|
43
71
|
const credentialsEncryptionSpec = [{ field: 'credentials' }]
|
|
44
72
|
|
|
@@ -118,18 +146,30 @@ export function createCredentialsService(em: EntityManager) {
|
|
|
118
146
|
|
|
119
147
|
return {
|
|
120
148
|
async getRaw(integrationId: string, scope: IntegrationScope): Promise<Record<string, unknown> | null> {
|
|
121
|
-
|
|
149
|
+
let row = await findOneWithDecryption(
|
|
122
150
|
em,
|
|
123
151
|
IntegrationCredentials,
|
|
124
|
-
|
|
125
|
-
integrationId,
|
|
126
|
-
organizationId: scope.organizationId,
|
|
127
|
-
tenantId: scope.tenantId,
|
|
128
|
-
deletedAt: null,
|
|
129
|
-
},
|
|
152
|
+
buildCredentialsFilter(integrationId, scope),
|
|
130
153
|
undefined,
|
|
131
154
|
scope,
|
|
132
155
|
)
|
|
156
|
+
// Spec 2026-05-21 (email-integration-foundation) "Hub credentials store":
|
|
157
|
+
// per-user secrets resolve as `WHERE user_id = currentUser.id OR user_id IS NULL`.
|
|
158
|
+
// A user-scoped read of a TENANT-WIDE integration (sync_excel, Stripe, Akeneo,
|
|
159
|
+
// S3, the channel OAuth *client* config) MUST still find the shared
|
|
160
|
+
// `user_id = NULL` row — the per-user row takes precedence, and we only fall
|
|
161
|
+
// back to the tenant-wide row when the user has none of their own. Writes stay
|
|
162
|
+
// strict (`save` uses the unmodified filter) so a per-user save never clobbers
|
|
163
|
+
// the shared credential.
|
|
164
|
+
if (!row && scope.userId) {
|
|
165
|
+
row = await findOneWithDecryption(
|
|
166
|
+
em,
|
|
167
|
+
IntegrationCredentials,
|
|
168
|
+
buildCredentialsFilter(integrationId, { ...scope, userId: null }),
|
|
169
|
+
undefined,
|
|
170
|
+
scope,
|
|
171
|
+
)
|
|
172
|
+
}
|
|
133
173
|
if (!row) return null
|
|
134
174
|
return decryptCredentialsBlob(row.credentials, scope)
|
|
135
175
|
},
|
|
@@ -150,12 +190,7 @@ export function createCredentialsService(em: EntityManager) {
|
|
|
150
190
|
const row = await findOneWithDecryption(
|
|
151
191
|
em,
|
|
152
192
|
IntegrationCredentials,
|
|
153
|
-
|
|
154
|
-
integrationId,
|
|
155
|
-
organizationId: scope.organizationId,
|
|
156
|
-
tenantId: scope.tenantId,
|
|
157
|
-
deletedAt: null,
|
|
158
|
-
},
|
|
193
|
+
buildCredentialsFilter(integrationId, scope),
|
|
159
194
|
undefined,
|
|
160
195
|
scope,
|
|
161
196
|
)
|
|
@@ -171,6 +206,7 @@ export function createCredentialsService(em: EntityManager) {
|
|
|
171
206
|
credentials: encryptedCredentials,
|
|
172
207
|
organizationId: scope.organizationId,
|
|
173
208
|
tenantId: scope.tenantId,
|
|
209
|
+
...(scope.userId ? { userId: scope.userId } : {}),
|
|
174
210
|
})
|
|
175
211
|
await em.persist(created).flush()
|
|
176
212
|
},
|
|
@@ -135,6 +135,22 @@
|
|
|
135
135
|
"comment": null,
|
|
136
136
|
"enumItems": [],
|
|
137
137
|
"mappedType": "datetime"
|
|
138
|
+
},
|
|
139
|
+
"user_id": {
|
|
140
|
+
"name": "user_id",
|
|
141
|
+
"type": "uuid",
|
|
142
|
+
"unsigned": false,
|
|
143
|
+
"autoincrement": false,
|
|
144
|
+
"primary": false,
|
|
145
|
+
"nullable": true,
|
|
146
|
+
"unique": false,
|
|
147
|
+
"length": null,
|
|
148
|
+
"precision": null,
|
|
149
|
+
"scale": null,
|
|
150
|
+
"default": null,
|
|
151
|
+
"comment": null,
|
|
152
|
+
"enumItems": [],
|
|
153
|
+
"mappedType": "uuid"
|
|
138
154
|
}
|
|
139
155
|
},
|
|
140
156
|
"indexes": [
|
|
@@ -159,6 +175,15 @@
|
|
|
159
175
|
"keyName": "integration_credentials_pkey",
|
|
160
176
|
"primary": true,
|
|
161
177
|
"unique": true
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"columnNames": [],
|
|
181
|
+
"composite": false,
|
|
182
|
+
"constraint": false,
|
|
183
|
+
"keyName": "integration_credentials_user_lookup_idx",
|
|
184
|
+
"primary": false,
|
|
185
|
+
"unique": true,
|
|
186
|
+
"expression": "create unique index \"integration_credentials_user_lookup_idx\" on \"integration_credentials\" (\"integration_id\", \"organization_id\", \"tenant_id\", \"user_id\") where \"user_id\" is not null and \"deleted_at\" is null"
|
|
162
187
|
}
|
|
163
188
|
],
|
|
164
189
|
"checks": [],
|
|
@@ -901,4 +926,4 @@
|
|
|
901
926
|
],
|
|
902
927
|
"views": [],
|
|
903
928
|
"nativeEnums": {}
|
|
904
|
-
}
|
|
929
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260526154136_integrations extends Migration {
|
|
4
|
+
|
|
5
|
+
override up(): void | Promise<void> {
|
|
6
|
+
this.addSql(`alter table "integration_credentials" add "user_id" uuid null;`);
|
|
7
|
+
this.addSql(`create unique index "integration_credentials_user_lookup_idx" on "integration_credentials" ("integration_id", "organization_id", "tenant_id", "user_id") where "user_id" is not null and "deleted_at" is null;`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
override down(): void | Promise<void> {
|
|
11
|
+
this.addSql(`drop index "integration_credentials_user_lookup_idx";`);
|
|
12
|
+
this.addSql(`alter table "integration_credentials" drop column "user_id";`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
|
@@ -125,6 +125,10 @@ const composeCommandSchema = composeMessageSchema.safeExtend({
|
|
|
125
125
|
tenantId: scopeSchema.shape.tenantId,
|
|
126
126
|
organizationId: scopeSchema.shape.organizationId,
|
|
127
127
|
userId: scopeSchema.shape.userId,
|
|
128
|
+
// Optional dedup key (inbound email ingest sets it; other callers leave it
|
|
129
|
+
// undefined). When set, a re-issued compose returns the first message instead
|
|
130
|
+
// of creating a duplicate.
|
|
131
|
+
idempotencyKey: z.string().min(1).max(255).optional(),
|
|
128
132
|
})
|
|
129
133
|
|
|
130
134
|
const updateDraftCommandSchema = updateDraftSchema.safeExtend({
|
|
@@ -207,7 +211,49 @@ async function requireMessageById(
|
|
|
207
211
|
return message
|
|
208
212
|
}
|
|
209
213
|
|
|
210
|
-
|
|
214
|
+
function isUniqueViolation(err: unknown): boolean {
|
|
215
|
+
if (!err || typeof err !== 'object') return false
|
|
216
|
+
const code = (err as { code?: string }).code
|
|
217
|
+
if (code === '23505') return true
|
|
218
|
+
const message = (err as { message?: string }).message
|
|
219
|
+
return typeof message === 'string' && /duplicate key value|unique constraint/i.test(message)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
type ComposeMessageResult = {
|
|
223
|
+
id: string
|
|
224
|
+
threadId: string | null
|
|
225
|
+
externalEmail: string | null
|
|
226
|
+
isDraft: boolean
|
|
227
|
+
recipientUserIds: string[]
|
|
228
|
+
/**
|
|
229
|
+
* True when this was an idempotent replay — an existing message was returned
|
|
230
|
+
* and nothing was written. Signals `buildLog` to skip the audit/undo entry.
|
|
231
|
+
*/
|
|
232
|
+
deduplicated?: boolean
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function buildComposeResultFromExisting(
|
|
236
|
+
em: EntityManager,
|
|
237
|
+
message: Message,
|
|
238
|
+
): Promise<ComposeMessageResult> {
|
|
239
|
+
const recipients = await findWithDecryption(
|
|
240
|
+
em,
|
|
241
|
+
MessageRecipient,
|
|
242
|
+
{ messageId: message.id, deletedAt: null },
|
|
243
|
+
undefined,
|
|
244
|
+
{ tenantId: message.tenantId, organizationId: message.organizationId ?? null },
|
|
245
|
+
)
|
|
246
|
+
return {
|
|
247
|
+
id: message.id,
|
|
248
|
+
threadId: message.threadId ?? null,
|
|
249
|
+
externalEmail: message.externalEmail ?? null,
|
|
250
|
+
isDraft: message.isDraft,
|
|
251
|
+
recipientUserIds: recipients.map((recipient) => recipient.recipientUserId),
|
|
252
|
+
deduplicated: true,
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: string | null; externalEmail: string | null; isDraft: boolean; recipientUserIds: string[]; deduplicated?: boolean }> = {
|
|
211
257
|
id: 'messages.messages.compose',
|
|
212
258
|
async execute(rawInput, ctx) {
|
|
213
259
|
const input = composeCommandSchema.parse(rawInput)
|
|
@@ -217,19 +263,42 @@ const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: str
|
|
|
217
263
|
}
|
|
218
264
|
|
|
219
265
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
266
|
+
const scope = { tenantId: input.tenantId, organizationId: input.organizationId }
|
|
267
|
+
|
|
268
|
+
// Idempotency fast-path: a retried inbound-email ingest re-issues compose
|
|
269
|
+
// for the same source message (the first attempt committed the message but a
|
|
270
|
+
// downstream transient failure rolled the ingest back). Return that message
|
|
271
|
+
// so the retry cannot create a duplicate.
|
|
272
|
+
if (input.idempotencyKey) {
|
|
273
|
+
const existing = await findOneWithDecryption(
|
|
274
|
+
em,
|
|
275
|
+
Message,
|
|
276
|
+
{ tenantId: input.tenantId, idempotencyKey: input.idempotencyKey },
|
|
277
|
+
undefined,
|
|
278
|
+
scope,
|
|
279
|
+
)
|
|
280
|
+
if (existing) return buildComposeResultFromExisting(em, existing)
|
|
281
|
+
}
|
|
282
|
+
|
|
220
283
|
let messageId = ''
|
|
221
284
|
let responseThreadId: string | null = null
|
|
222
285
|
let responseExternalEmail: string | null = null
|
|
223
286
|
|
|
224
|
-
|
|
287
|
+
const composeTx = em.transactional(async (trx) => {
|
|
225
288
|
const threadId = input.parentMessageId
|
|
226
289
|
? (
|
|
227
|
-
await
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
290
|
+
await findOneWithDecryption(
|
|
291
|
+
trx,
|
|
292
|
+
Message,
|
|
293
|
+
{
|
|
294
|
+
id: input.parentMessageId,
|
|
295
|
+
tenantId: input.tenantId,
|
|
296
|
+
organizationId: input.organizationId,
|
|
297
|
+
deletedAt: null,
|
|
298
|
+
},
|
|
299
|
+
undefined,
|
|
300
|
+
scope,
|
|
301
|
+
)
|
|
233
302
|
)?.threadId ?? input.parentMessageId
|
|
234
303
|
: undefined
|
|
235
304
|
|
|
@@ -254,6 +323,7 @@ const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: str
|
|
|
254
323
|
sentAt: input.isDraft ? null : new Date(),
|
|
255
324
|
actionData: input.actionData as MessageActionData | undefined,
|
|
256
325
|
sendViaEmail,
|
|
326
|
+
idempotencyKey: input.idempotencyKey ?? null,
|
|
257
327
|
tenantId: input.tenantId,
|
|
258
328
|
organizationId: input.organizationId,
|
|
259
329
|
})
|
|
@@ -313,6 +383,24 @@ const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: str
|
|
|
313
383
|
responseThreadId = message.threadId ?? null
|
|
314
384
|
responseExternalEmail = message.externalEmail ?? null
|
|
315
385
|
})
|
|
386
|
+
try {
|
|
387
|
+
await composeTx
|
|
388
|
+
} catch (err) {
|
|
389
|
+
// Lost a concurrent race on the same idempotency key — return the message
|
|
390
|
+
// the winning compose created. A propagated 23505 would otherwise be
|
|
391
|
+
// classified permanent and dead-letter the inbound mail.
|
|
392
|
+
if (input.idempotencyKey && isUniqueViolation(err)) {
|
|
393
|
+
const existing = await findOneWithDecryption(
|
|
394
|
+
em.fork(),
|
|
395
|
+
Message,
|
|
396
|
+
{ tenantId: input.tenantId, idempotencyKey: input.idempotencyKey },
|
|
397
|
+
undefined,
|
|
398
|
+
scope,
|
|
399
|
+
)
|
|
400
|
+
if (existing) return buildComposeResultFromExisting(em.fork(), existing)
|
|
401
|
+
}
|
|
402
|
+
throw err
|
|
403
|
+
}
|
|
316
404
|
|
|
317
405
|
if (!input.isDraft) {
|
|
318
406
|
await emitMessageSentEvent(ctx.container, {
|
|
@@ -345,6 +433,11 @@ const composeMessageCommand: CommandHandler<unknown, { id: string; threadId: str
|
|
|
345
433
|
return loadMessageAggregateSnapshot(em, result.id)
|
|
346
434
|
},
|
|
347
435
|
buildLog: async ({ input, result, snapshots }) => {
|
|
436
|
+
// Idempotent replay: execute returned a pre-existing message without writing
|
|
437
|
+
// anything. Skip the audit entry entirely — logging it as a fresh "Compose
|
|
438
|
+
// message" would both misrepresent the dedup and expose an undo that
|
|
439
|
+
// soft-deletes a legitimately-received inbound message.
|
|
440
|
+
if (result.deduplicated) return { skipLog: true }
|
|
348
441
|
const parsed = composeCommandSchema.parse(input)
|
|
349
442
|
return {
|
|
350
443
|
actionLabel: parsed.isDraft ? 'Create draft message' : 'Compose message',
|
|
@@ -2,12 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
import { useRouter } from 'next/navigation'
|
|
4
4
|
import { MessageComposer } from '@open-mercato/ui/backend/messages'
|
|
5
|
+
// UMES extension surface — compose page injection spot (SPEC-045d §9.3a).
|
|
6
|
+
// Channel provider packages inject "composer capabilities" widgets here
|
|
7
|
+
// (character limit warnings, channel format selector, attachment scoping, etc.).
|
|
8
|
+
import { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'
|
|
5
9
|
|
|
6
10
|
export function ComposeMessagePageClient() {
|
|
7
11
|
const router = useRouter()
|
|
8
12
|
|
|
9
13
|
return (
|
|
10
14
|
<div className="space-y-4">
|
|
15
|
+
{/*
|
|
16
|
+
Standalone widget mount above the composer — NOT CrudForm field
|
|
17
|
+
resolution. This page is not a CrudForm, so the `crud-form:*:fields`
|
|
18
|
+
field-event pipeline (onFieldChange, value transformers, etc.) does
|
|
19
|
+
not apply here. Provider packages render composer-capability widgets
|
|
20
|
+
(character-limit warnings, channel format selectors, attachment
|
|
21
|
+
scoping) into this spot purely as additional UI siblings.
|
|
22
|
+
*/}
|
|
23
|
+
<InjectionSpot
|
|
24
|
+
spotId="crud-form:messages:message:fields"
|
|
25
|
+
context={{ form: 'compose' }}
|
|
26
|
+
data={{}}
|
|
27
|
+
/>
|
|
11
28
|
<MessageComposer
|
|
12
29
|
inline
|
|
13
30
|
variant="compose"
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { MessageComposer } from '@open-mercato/ui/backend/messages'
|
|
5
|
+
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
5
6
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
6
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
7
8
|
import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
9
|
+
// UMES extension surface — message detail injection spots (SPEC-045d §9.3a)
|
|
10
|
+
import { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'
|
|
8
11
|
import {
|
|
9
12
|
getMessageUiComponentRegistry,
|
|
10
13
|
} from './utils/typeUiRegistry'
|
|
@@ -29,6 +32,7 @@ function MessageConversationDetailItem({
|
|
|
29
32
|
onToggle,
|
|
30
33
|
onReply,
|
|
31
34
|
onForward,
|
|
35
|
+
userFeatures,
|
|
32
36
|
}: {
|
|
33
37
|
messageId: string
|
|
34
38
|
isCollapsible: boolean
|
|
@@ -36,6 +40,7 @@ function MessageConversationDetailItem({
|
|
|
36
40
|
onToggle: () => void
|
|
37
41
|
onReply: (messageId: string) => void
|
|
38
42
|
onForward: (messageId: string) => void
|
|
43
|
+
userFeatures: string[]
|
|
39
44
|
}) {
|
|
40
45
|
const state = useMessageDetails(messageId)
|
|
41
46
|
const messageUiRegistry = React.useMemo(() => getMessageUiComponentRegistry(), [])
|
|
@@ -87,7 +92,23 @@ function MessageConversationDetailItem({
|
|
|
87
92
|
ContentComponent={ContentComponent}
|
|
88
93
|
/>
|
|
89
94
|
|
|
95
|
+
{/* UMES — channel payload renderer mounts here (channel-linked emails, Slack blocks, etc.). */}
|
|
96
|
+
<InjectionSpot
|
|
97
|
+
spotId="detail:messages:message:body:after"
|
|
98
|
+
context={{ messageId }}
|
|
99
|
+
data={state.detail}
|
|
100
|
+
/>
|
|
101
|
+
|
|
90
102
|
<MessageDetailMetaSection detail={state.detail} />
|
|
103
|
+
|
|
104
|
+
{/* UMES — channel sidebar widgets (channel info panel, contact preview, delivery status).
|
|
105
|
+
`userFeatures` lets feature-gated widgets (e.g. the channel reassignment editor,
|
|
106
|
+
gated on `communication_channels.assign`) render their controls for permitted users. */}
|
|
107
|
+
<InjectionSpot
|
|
108
|
+
spotId="detail:messages:message:sidebar"
|
|
109
|
+
context={{ messageId, userFeatures }}
|
|
110
|
+
data={state.detail}
|
|
111
|
+
/>
|
|
91
112
|
</section>
|
|
92
113
|
|
|
93
114
|
<MessageDetailActionsSection
|
|
@@ -138,6 +159,27 @@ function MessageConversationDetailItem({
|
|
|
138
159
|
|
|
139
160
|
export function MessageDetailPageClient({ id }: { id: string }) {
|
|
140
161
|
const state = useMessageDetails(id)
|
|
162
|
+
// Resolve the viewer's feature grants once and forward them into the sidebar
|
|
163
|
+
// injection spot so feature-gated widgets (channel reassignment editor) can
|
|
164
|
+
// render their controls. Without this, hub widgets that gate on
|
|
165
|
+
// `context.userFeatures` (e.g. `communication_channels.assign`) stay read-only.
|
|
166
|
+
const [userFeatures, setUserFeatures] = React.useState<string[]>([])
|
|
167
|
+
React.useEffect(() => {
|
|
168
|
+
let cancelled = false
|
|
169
|
+
void (async () => {
|
|
170
|
+
const res = await apiCall<{ ok: boolean; granted?: string[] }>('/api/auth/feature-check', {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers: { 'content-type': 'application/json' },
|
|
173
|
+
body: JSON.stringify({ features: ['communication_channels.assign'] }),
|
|
174
|
+
}).catch(() => null)
|
|
175
|
+
if (!cancelled && res?.ok && Array.isArray(res.result?.granted)) {
|
|
176
|
+
setUserFeatures(res.result.granted)
|
|
177
|
+
}
|
|
178
|
+
})()
|
|
179
|
+
return () => {
|
|
180
|
+
cancelled = true
|
|
181
|
+
}
|
|
182
|
+
}, [])
|
|
141
183
|
const [activeInlineComposer, setActiveInlineComposer] = React.useState<{
|
|
142
184
|
variant: 'reply' | 'forward'
|
|
143
185
|
messageId: string
|
|
@@ -234,6 +276,7 @@ export function MessageDetailPageClient({ id }: { id: string }) {
|
|
|
234
276
|
<MessageConversationDetailItem
|
|
235
277
|
key={item.id}
|
|
236
278
|
messageId={item.id}
|
|
279
|
+
userFeatures={userFeatures}
|
|
237
280
|
isCollapsible={!isForcedExpanded}
|
|
238
281
|
isExpanded
|
|
239
282
|
onToggle={() => state.toggleConversationItem(item.id)}
|
|
@@ -362,6 +362,10 @@ export function MessagesInboxPageClient() {
|
|
|
362
362
|
<div className="space-y-4">
|
|
363
363
|
<DataTable
|
|
364
364
|
title={t('messages.title', 'Messages')}
|
|
365
|
+
// UMES extension surface — opt into widget injection at:
|
|
366
|
+
// data-table:messages:columns / :row-actions / :bulk-actions / :filters / :toolbar / :search-trailing
|
|
367
|
+
// (SPEC-045d §9.3a — communication_channels hub renders channel badge + delivery status here)
|
|
368
|
+
extensionTableId="messages"
|
|
365
369
|
columns={columns}
|
|
366
370
|
data={rows}
|
|
367
371
|
bulkActions={bulkActions}
|
|
@@ -30,6 +30,11 @@ export type MessageActionData = {
|
|
|
30
30
|
@Index({ name: 'messages_type_idx', properties: ['type', 'tenantId'] })
|
|
31
31
|
@Index({ name: 'messages_tenant_idx', properties: ['tenantId', 'organizationId'] })
|
|
32
32
|
@Index({ name: 'messages_external_email_hash_idx', properties: ['externalEmailHash'] })
|
|
33
|
+
@Index({
|
|
34
|
+
name: 'messages_idempotency_key_uq',
|
|
35
|
+
expression:
|
|
36
|
+
'create unique index "messages_idempotency_key_uq" on "messages" ("tenant_id", "idempotency_key") where "idempotency_key" is not null',
|
|
37
|
+
})
|
|
33
38
|
export class Message {
|
|
34
39
|
[OptionalProps]?: 'type' | 'status' | 'priority' | 'bodyFormat' | 'isDraft' | 'createdAt' | 'updatedAt'
|
|
35
40
|
|
|
@@ -117,6 +122,12 @@ export class Message {
|
|
|
117
122
|
@Property({ name: 'external_email_hash', type: 'text', nullable: true })
|
|
118
123
|
externalEmailHash?: string | null
|
|
119
124
|
|
|
125
|
+
// Stable per-source idempotency key (inbound email = channel + provider
|
|
126
|
+
// message id). Lets a retried `compose` return the message created by the
|
|
127
|
+
// first attempt instead of duplicating it. Partial-unique per tenant.
|
|
128
|
+
@Property({ name: 'idempotency_key', type: 'text', nullable: true })
|
|
129
|
+
idempotencyKey?: string | null
|
|
130
|
+
|
|
120
131
|
@Property({ name: 'external_name', type: 'text', nullable: true })
|
|
121
132
|
externalName?: string | null
|
|
122
133
|
|
|
@@ -270,6 +270,15 @@
|
|
|
270
270
|
"nullable": true,
|
|
271
271
|
"mappedType": "text"
|
|
272
272
|
},
|
|
273
|
+
"idempotency_key": {
|
|
274
|
+
"name": "idempotency_key",
|
|
275
|
+
"type": "text",
|
|
276
|
+
"unsigned": false,
|
|
277
|
+
"autoincrement": false,
|
|
278
|
+
"primary": false,
|
|
279
|
+
"nullable": true,
|
|
280
|
+
"mappedType": "text"
|
|
281
|
+
},
|
|
273
282
|
"external_name": {
|
|
274
283
|
"name": "external_name",
|
|
275
284
|
"type": "text",
|
|
@@ -365,6 +374,15 @@
|
|
|
365
374
|
"primary": false,
|
|
366
375
|
"unique": false
|
|
367
376
|
},
|
|
377
|
+
{
|
|
378
|
+
"columnNames": [],
|
|
379
|
+
"composite": false,
|
|
380
|
+
"constraint": false,
|
|
381
|
+
"keyName": "messages_idempotency_key_uq",
|
|
382
|
+
"primary": false,
|
|
383
|
+
"unique": false,
|
|
384
|
+
"expression": "create unique index \"messages_idempotency_key_uq\" on \"messages\" (\"tenant_id\", \"idempotency_key\") where \"idempotency_key\" is not null"
|
|
385
|
+
},
|
|
368
386
|
{
|
|
369
387
|
"keyName": "messages_pkey",
|
|
370
388
|
"columnNames": [
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260531130000 extends Migration {
|
|
4
|
+
|
|
5
|
+
override async up(): Promise<void> {
|
|
6
|
+
this.addSql(`alter table "messages" add column if not exists "idempotency_key" text null;`);
|
|
7
|
+
this.addSql(`create unique index if not exists "messages_idempotency_key_uq" on "messages" ("tenant_id", "idempotency_key") where "idempotency_key" is not null;`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
override async down(): Promise<void> {
|
|
11
|
+
this.addSql(`drop index if exists "messages_idempotency_key_uq";`);
|
|
12
|
+
this.addSql(`alter table "messages" drop column if exists "idempotency_key";`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Messages module — widget injection table.
|
|
5
|
+
*
|
|
6
|
+
* The messages module currently does not register any widgets INTO other modules' spots.
|
|
7
|
+
* This file exists primarily to document the spots the messages module EXPOSES for other
|
|
8
|
+
* modules to inject into. The actual `<InjectionSpot>` placements live in the messages
|
|
9
|
+
* pages themselves (see `components/MessagesInboxPageClient.tsx`,
|
|
10
|
+
* `components/MessageDetailPageClient.tsx`, `components/ComposeMessagePageClient.tsx`).
|
|
11
|
+
*
|
|
12
|
+
* Exposed spot IDs (additive contract per BACKWARD_COMPATIBILITY.md §6):
|
|
13
|
+
* - `data-table:messages:columns` — auto-wired by `DataTable extensionTableId="messages"`
|
|
14
|
+
* - `data-table:messages:row-actions` — auto-wired by `DataTable extensionTableId="messages"`
|
|
15
|
+
* - `data-table:messages:bulk-actions` — auto-wired by `DataTable extensionTableId="messages"`
|
|
16
|
+
* - `data-table:messages:filters` — auto-wired by `DataTable extensionTableId="messages"`
|
|
17
|
+
* - `data-table:messages:search-trailing` — auto-wired by `DataTable extensionTableId="messages"`
|
|
18
|
+
* - `data-table:messages:toolbar` — auto-wired by `DataTable extensionTableId="messages"`
|
|
19
|
+
* - `detail:messages:message:body:after` — placed in `MessageDetailPageClient.tsx`
|
|
20
|
+
* - `detail:messages:message:sidebar` — placed in `MessageDetailPageClient.tsx`
|
|
21
|
+
* - `crud-form:messages:message:fields` — placed in `ComposeMessagePageClient.tsx` as a standalone widget mount above the composer (NOT CrudForm field resolution; the compose page is not a CrudForm)
|
|
22
|
+
*
|
|
23
|
+
* The `communication_channels` hub (SPEC-045d) is the primary consumer of these spots,
|
|
24
|
+
* injecting channel badges, channel payload renderers, reaction bars, channel info
|
|
25
|
+
* sidebars, and composer capability adapters.
|
|
26
|
+
*/
|
|
27
|
+
export const injectionTable: ModuleInjectionTable = {}
|
|
28
|
+
|
|
29
|
+
export default injectionTable
|