@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
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260526134719_communication_channels extends Migration {
|
|
4
|
+
|
|
5
|
+
override up(): void | Promise<void> {
|
|
6
|
+
this.addSql(`create table "channel_thread_mappings" ("id" uuid not null default gen_random_uuid(), "external_conversation_id" uuid not null, "message_thread_id" uuid not null, "channel_id" uuid not null, "provider_key" text not null, "external_thread_ref" text not null, "assigned_user_id" uuid null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
|
|
7
|
+
this.addSql(`create index "channel_thread_mappings_thread_idx" on "channel_thread_mappings" ("message_thread_id", "tenant_id");`);
|
|
8
|
+
this.addSql(`create index "channel_thread_mappings_ext_conv_idx" on "channel_thread_mappings" ("external_conversation_id", "tenant_id");`);
|
|
9
|
+
this.addSql(`alter table "channel_thread_mappings" add constraint "channel_thread_mappings_ext_conv_uq" unique ("external_conversation_id", "tenant_id");`);
|
|
10
|
+
|
|
11
|
+
this.addSql(`create table "communication_channels" ("id" uuid not null default gen_random_uuid(), "provider_key" text not null, "channel_type" text not null, "display_name" text not null, "external_identifier" text null, "credentials_ref" uuid null, "capabilities" jsonb null, "is_active" boolean not null default true, "user_id" uuid null, "is_primary" boolean not null default false, "poll_interval_seconds" int null, "last_polled_at" timestamptz null, "status" text not null default 'connected', "last_error" text null, "channel_state" jsonb null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, primary key ("id"));`);
|
|
12
|
+
this.addSql(`create index "communication_channels_tenant_type_active_idx" on "communication_channels" ("tenant_id", "channel_type", "is_active");`);
|
|
13
|
+
this.addSql(`create index "communication_channels_tenant_provider_idx" on "communication_channels" ("tenant_id", "provider_key");`);
|
|
14
|
+
this.addSql(`create unique index "communication_channels_one_primary_per_user_uq" on "communication_channels" ("user_id") where "is_primary" and "user_id" is not null and "deleted_at" is null;`);
|
|
15
|
+
this.addSql(`create index "communication_channels_poll_due_idx" on "communication_channels" ("is_active", "last_polled_at") where "deleted_at" is null;`);
|
|
16
|
+
this.addSql(`create index "communication_channels_user_lookup_idx" on "communication_channels" ("user_id", "channel_type", "deleted_at");`);
|
|
17
|
+
|
|
18
|
+
this.addSql(`create table "external_conversations" ("id" uuid not null default gen_random_uuid(), "channel_id" uuid not null, "external_conversation_id" text not null, "subject" text null, "contact_person_id" uuid null, "assigned_user_id" uuid null, "last_message_at" timestamptz null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
|
|
19
|
+
this.addSql(`create index "external_conversations_assigned_user_idx" on "external_conversations" ("assigned_user_id");`);
|
|
20
|
+
this.addSql(`create index "external_conversations_contact_person_idx" on "external_conversations" ("contact_person_id");`);
|
|
21
|
+
this.addSql(`create index "external_conversations_channel_idx" on "external_conversations" ("channel_id", "external_conversation_id");`);
|
|
22
|
+
this.addSql(`alter table "external_conversations" add constraint "external_conversations_channel_external_uq" unique ("channel_id", "external_conversation_id");`);
|
|
23
|
+
|
|
24
|
+
this.addSql(`create table "external_messages" ("id" uuid not null default gen_random_uuid(), "channel_id" uuid not null, "conversation_id" uuid not null, "external_message_id" text not null, "direction" text not null, "sender_identifier" text null, "sender_display_name" text null, "provider_timestamp" timestamptz null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, primary key ("id"));`);
|
|
25
|
+
this.addSql(`create index "external_messages_channel_external_idx" on "external_messages" ("channel_id", "external_message_id");`);
|
|
26
|
+
this.addSql(`create index "external_messages_conversation_idx" on "external_messages" ("conversation_id");`);
|
|
27
|
+
this.addSql(`alter table "external_messages" add constraint "external_messages_channel_external_uq" unique ("channel_id", "external_message_id");`);
|
|
28
|
+
|
|
29
|
+
this.addSql(`create table "message_channel_links" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "external_conversation_id" uuid not null, "external_message_id" uuid null, "provider_key" text not null, "channel_type" text not null, "direction" text not null, "delivery_status" text not null default 'pending', "channel_payload" jsonb null, "channel_content_type" text null, "interactive_state" jsonb null, "channel_metadata" jsonb null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, primary key ("id"));`);
|
|
30
|
+
this.addSql(`create index "message_channel_links_ext_msg_idx" on "message_channel_links" ("external_message_id");`);
|
|
31
|
+
this.addSql(`create index "message_channel_links_ext_conv_idx" on "message_channel_links" ("external_conversation_id");`);
|
|
32
|
+
this.addSql(`create index "message_channel_links_message_idx" on "message_channel_links" ("message_id");`);
|
|
33
|
+
this.addSql(`alter table "message_channel_links" add constraint "message_channel_links_message_uq" unique ("message_id");`);
|
|
34
|
+
|
|
35
|
+
this.addSql(`create table "message_reactions" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "emoji" text not null, "reacted_by_user_id" uuid null, "reacted_by_external_id" text null, "reacted_by_display_name" text null, "provider_key" text null, "external_reaction_id" text null, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, primary key ("id"));`);
|
|
36
|
+
this.addSql(`alter table "message_reactions" add constraint "message_reactions_exactly_one_actor_chk" check (("reacted_by_user_id" is null) <> ("reacted_by_external_id" is null));`);
|
|
37
|
+
this.addSql(`create index "message_reactions_message_emoji_idx" on "message_reactions" ("message_id", "emoji");`);
|
|
38
|
+
this.addSql(`create index "message_reactions_message_idx" on "message_reactions" ("message_id");`);
|
|
39
|
+
this.addSql(`create unique index "message_reactions_external_actor_uq" on "message_reactions" ("tenant_id", "message_id", "emoji", "reacted_by_external_id") where "reacted_by_external_id" is not null;`);
|
|
40
|
+
this.addSql(`create unique index "message_reactions_internal_actor_uq" on "message_reactions" ("tenant_id", "message_id", "emoji", "reacted_by_user_id") where "reacted_by_user_id" is not null;`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override down(): void | Promise<void> {
|
|
44
|
+
this.addSql(`drop table if exists "message_reactions";`);
|
|
45
|
+
this.addSql(`drop table if exists "message_channel_links";`);
|
|
46
|
+
this.addSql(`drop table if exists "external_messages";`);
|
|
47
|
+
this.addSql(`drop table if exists "external_conversations";`);
|
|
48
|
+
this.addSql(`drop index if exists "communication_channels_user_lookup_idx";`);
|
|
49
|
+
this.addSql(`drop index if exists "communication_channels_poll_due_idx";`);
|
|
50
|
+
this.addSql(`drop index if exists "communication_channels_one_primary_per_user_uq";`);
|
|
51
|
+
this.addSql(`drop table if exists "communication_channels";`);
|
|
52
|
+
this.addSql(`drop table if exists "channel_thread_mappings";`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260527195446_communication_channels extends Migration {
|
|
4
|
+
|
|
5
|
+
override up(): void | Promise<void> {
|
|
6
|
+
this.addSql(`create table "channel_ingest_dead_letters" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "channel_id" uuid not null, "provider_key" text not null, "external_uid" text null, "external_message_id" text null, "error_class" text not null, "error_message" text not null, "raw_body" text null, "created_at" timestamptz not null, primary key ("id"));`);
|
|
7
|
+
this.addSql(`create index "channel_ingest_dead_letters_created_idx" on "channel_ingest_dead_letters" ("tenant_id", "created_at");`);
|
|
8
|
+
this.addSql(`create index "channel_ingest_dead_letters_channel_idx" on "channel_ingest_dead_letters" ("channel_id", "tenant_id");`);
|
|
9
|
+
|
|
10
|
+
this.addSql(`create table "channel_thread_tokens" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "message_thread_id" uuid not null, "token" text not null, "created_at" timestamptz not null, "last_seen_at" timestamptz null, primary key ("id"));`);
|
|
11
|
+
this.addSql(`create index "channel_thread_tokens_thread_idx" on "channel_thread_tokens" ("message_thread_id", "tenant_id");`);
|
|
12
|
+
this.addSql(`alter table "channel_thread_tokens" add constraint "channel_thread_tokens_token_uq" unique ("tenant_id", "token");`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
override down(): void | Promise<void> {
|
|
16
|
+
this.addSql(`drop table if exists "channel_ingest_dead_letters";`);
|
|
17
|
+
this.addSql(`drop table if exists "channel_thread_tokens";`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260529231848_communication_channels extends Migration {
|
|
4
|
+
|
|
5
|
+
override up(): void | Promise<void> {
|
|
6
|
+
this.addSql(`create index "communication_channels_provider_external_idx" on "communication_channels" ("provider_key", "external_identifier") where "deleted_at" is null;`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
override down(): void | Promise<void> {
|
|
10
|
+
this.addSql(`drop index "communication_channels_provider_external_idx";`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260531120000_communication_channels extends Migration {
|
|
4
|
+
|
|
5
|
+
override up(): void | Promise<void> {
|
|
6
|
+
// Enforce one thread token per (tenant, message_thread_id). Replaces the
|
|
7
|
+
// earlier non-unique `channel_thread_tokens_thread_idx` so `getOrCreateThreadToken`
|
|
8
|
+
// is idempotent under concurrency (insert-on-conflict). The new unique index
|
|
9
|
+
// also serves the `WHERE tenant_id = ? AND message_thread_id = ?` lookup.
|
|
10
|
+
this.addSql(`drop index if exists "channel_thread_tokens_thread_idx";`);
|
|
11
|
+
this.addSql(`alter table "channel_thread_tokens" add constraint "channel_thread_tokens_thread_uq" unique ("tenant_id", "message_thread_id");`);
|
|
12
|
+
// Enforce one channel per (tenant, user, provider, mailbox) so a reconnect
|
|
13
|
+
// heals the existing row instead of inserting a duplicate. Partial: tenant-wide
|
|
14
|
+
// (null user_id) and identifier-less channels are exempt.
|
|
15
|
+
this.addSql(`create unique index "communication_channels_user_provider_external_uq" on "communication_channels" ("tenant_id", "user_id", "provider_key", "external_identifier") where "deleted_at" is null and "user_id" is not null and "external_identifier" is not null;`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override down(): void | Promise<void> {
|
|
19
|
+
this.addSql(`drop index if exists "communication_channels_user_provider_external_uq";`);
|
|
20
|
+
this.addSql(`alter table "channel_thread_tokens" drop constraint if exists "channel_thread_tokens_thread_uq";`);
|
|
21
|
+
this.addSql(`create index "channel_thread_tokens_thread_idx" on "channel_thread_tokens" ("message_thread_id", "tenant_id");`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
|
|
4
|
+
import { MessageReceivedRenderer } from './widgets/notifications/MessageReceivedRenderer'
|
|
5
|
+
import { ChannelRequiresReauthRenderer } from './widgets/notifications/ChannelRequiresReauthRenderer'
|
|
6
|
+
|
|
7
|
+
export const communicationChannelsNotificationTypes: NotificationTypeDefinition[] = [
|
|
8
|
+
{
|
|
9
|
+
type: 'communication_channels.message.received',
|
|
10
|
+
module: 'communication_channels',
|
|
11
|
+
titleKey: 'communication_channels.notifications.message_received.title',
|
|
12
|
+
bodyKey: 'communication_channels.notifications.message_received.body',
|
|
13
|
+
icon: 'message-circle',
|
|
14
|
+
severity: 'info',
|
|
15
|
+
actions: [
|
|
16
|
+
{
|
|
17
|
+
id: 'view',
|
|
18
|
+
labelKey: 'common.view',
|
|
19
|
+
variant: 'outline',
|
|
20
|
+
href: '/backend/messages/{sourceEntityId}',
|
|
21
|
+
icon: 'external-link',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
linkHref: '/backend/messages/{sourceEntityId}',
|
|
25
|
+
Renderer: MessageReceivedRenderer,
|
|
26
|
+
expiresAfterHours: 168,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'communication_channels.channel.requires_reauth',
|
|
30
|
+
module: 'communication_channels',
|
|
31
|
+
titleKey: 'communication_channels.notifications.channel_requires_reauth.title',
|
|
32
|
+
bodyKey: 'communication_channels.notifications.channel_requires_reauth.body',
|
|
33
|
+
icon: 'alert-triangle',
|
|
34
|
+
severity: 'warning',
|
|
35
|
+
actions: [
|
|
36
|
+
{
|
|
37
|
+
id: 'reconnect',
|
|
38
|
+
labelKey: 'communication_channels.notifications.channel_requires_reauth.reconnect',
|
|
39
|
+
variant: 'outline',
|
|
40
|
+
href: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
|
|
41
|
+
icon: 'refresh-cw',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
linkHref: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
|
|
45
|
+
Renderer: ChannelRequiresReauthRenderer,
|
|
46
|
+
expiresAfterHours: 720,
|
|
47
|
+
},
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
export default communicationChannelsNotificationTypes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { NotificationHandler } from '@open-mercato/shared/modules/notifications/handler'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reactive notification handlers for the Communications Hub (Phase 4 of the
|
|
5
|
+
* email integration spec).
|
|
6
|
+
*
|
|
7
|
+
* These handlers fire on the browser when the matching notification arrives via
|
|
8
|
+
* the notifications stream. They run in addition to (not instead of) the static
|
|
9
|
+
* UI surfaces:
|
|
10
|
+
*
|
|
11
|
+
* - The notification appears in the user's bell dropdown via
|
|
12
|
+
* `notifications.client.ts` renderers (already wired in slice 2a).
|
|
13
|
+
* - The dropdown's primary action navigates to the reconnect URL.
|
|
14
|
+
*
|
|
15
|
+
* What the handlers add:
|
|
16
|
+
* - **Toast**: surfaces the message even when the user isn't looking at the
|
|
17
|
+
* bell. The reauth toast is the highest-signal interruption — without
|
|
18
|
+
* reauth the channel goes silent.
|
|
19
|
+
* - **Event emit**: lets the profile page / channel admin DataTable hook in
|
|
20
|
+
* via `useAppEvent(...)` (DOM Event Bridge) so they auto-refresh when the
|
|
21
|
+
* state flips without requiring a manual reload.
|
|
22
|
+
* - **Refresh notifications**: keeps the bell badge accurate.
|
|
23
|
+
*
|
|
24
|
+
* Custom event names emitted here on the DOM Event Bridge. They are published
|
|
25
|
+
* for any page that opts in via `useAppEvent(...)`; no surface subscribes to
|
|
26
|
+
* them today (the unified inbox auto-refreshes from the `messages.message.*`
|
|
27
|
+
* bridge, and the reconnect flow is driven by the bell notification + its
|
|
28
|
+
* reconnect action). Kept as forward-compatible hooks for future
|
|
29
|
+
* row-highlight / refetch UX.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export const CHANNEL_REQUIRES_REAUTH_EVENT = 'om:communication_channels:channel-requires-reauth'
|
|
33
|
+
export const MESSAGE_RECEIVED_EVENT = 'om:communication_channels:message-received'
|
|
34
|
+
|
|
35
|
+
export const notificationHandlers: NotificationHandler[] = [
|
|
36
|
+
{
|
|
37
|
+
id: 'communication_channels.channel-requires-reauth-toast',
|
|
38
|
+
notificationType: 'communication_channels.channel.requires_reauth',
|
|
39
|
+
features: ['communication_channels.connect_user_channel'],
|
|
40
|
+
priority: 110,
|
|
41
|
+
handle(notification, context) {
|
|
42
|
+
context.toast({
|
|
43
|
+
title: notification.title,
|
|
44
|
+
body: notification.body ?? undefined,
|
|
45
|
+
severity: 'warning',
|
|
46
|
+
action: {
|
|
47
|
+
label:
|
|
48
|
+
context.t?.(
|
|
49
|
+
'communication_channels.notifications.channel_requires_reauth.reconnect',
|
|
50
|
+
'Reconnect',
|
|
51
|
+
) ?? 'Reconnect',
|
|
52
|
+
onClick: () => {
|
|
53
|
+
const channelId = notification.sourceEntityId
|
|
54
|
+
const target = channelId
|
|
55
|
+
? `/backend/profile/communication-channels?reconnect=${encodeURIComponent(channelId)}`
|
|
56
|
+
: '/backend/profile/communication-channels'
|
|
57
|
+
context.navigate(target)
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
context.emitEvent(CHANNEL_REQUIRES_REAUTH_EVENT, {
|
|
62
|
+
notificationId: notification.id,
|
|
63
|
+
channelId: notification.sourceEntityId ?? null,
|
|
64
|
+
})
|
|
65
|
+
context.refreshNotifications()
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'communication_channels.message-received-event',
|
|
70
|
+
notificationType: 'communication_channels.message.received',
|
|
71
|
+
features: ['communication_channels.view'],
|
|
72
|
+
priority: 100,
|
|
73
|
+
handle(notification, context) {
|
|
74
|
+
// Inbox refresh is the only side effect — the user's bell dropdown already
|
|
75
|
+
// shows the notification entry. A toast here would be noisy because the
|
|
76
|
+
// bell badge increments anyway, and inbound email lands in the unified
|
|
77
|
+
// inbox where the user expects it.
|
|
78
|
+
context.emitEvent(MESSAGE_RECEIVED_EVENT, {
|
|
79
|
+
notificationId: notification.id,
|
|
80
|
+
messageId: notification.sourceEntityId ?? null,
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
export default notificationHandlers
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
|
|
2
|
+
|
|
3
|
+
export const notificationTypes: NotificationTypeDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
type: 'communication_channels.message.received',
|
|
6
|
+
module: 'communication_channels',
|
|
7
|
+
titleKey: 'communication_channels.notifications.message_received.title',
|
|
8
|
+
bodyKey: 'communication_channels.notifications.message_received.body',
|
|
9
|
+
icon: 'message-circle',
|
|
10
|
+
severity: 'info',
|
|
11
|
+
actions: [
|
|
12
|
+
{
|
|
13
|
+
id: 'view',
|
|
14
|
+
labelKey: 'common.view',
|
|
15
|
+
variant: 'outline',
|
|
16
|
+
href: '/backend/messages/{sourceEntityId}',
|
|
17
|
+
icon: 'external-link',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
linkHref: '/backend/messages/{sourceEntityId}',
|
|
21
|
+
expiresAfterHours: 168, // 7 days
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
/**
|
|
25
|
+
* Channel-agnostic notification raised when an adapter loses authorization
|
|
26
|
+
* (OAuth refresh token revoked, IMAP/SMTP password rotated, WhatsApp token expired).
|
|
27
|
+
* Persisted by the `channel-requires-reauth-notification` subscriber in
|
|
28
|
+
* response to the `communication_channels.channel.requires_reauth` event
|
|
29
|
+
* (emitted by the poll worker and outbound delivery). Consumed by the email
|
|
30
|
+
* integration spec's reconnect flow.
|
|
31
|
+
*/
|
|
32
|
+
type: 'communication_channels.channel.requires_reauth',
|
|
33
|
+
module: 'communication_channels',
|
|
34
|
+
titleKey: 'communication_channels.notifications.channel_requires_reauth.title',
|
|
35
|
+
bodyKey: 'communication_channels.notifications.channel_requires_reauth.body',
|
|
36
|
+
icon: 'alert-triangle',
|
|
37
|
+
severity: 'warning',
|
|
38
|
+
actions: [
|
|
39
|
+
{
|
|
40
|
+
id: 'reconnect',
|
|
41
|
+
labelKey: 'communication_channels.notifications.channel_requires_reauth.reconnect',
|
|
42
|
+
variant: 'outline',
|
|
43
|
+
href: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
|
|
44
|
+
icon: 'refresh-cw',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
linkHref: '/backend/profile/communication-channels?reconnect={sourceEntityId}',
|
|
48
|
+
expiresAfterHours: 720, // 30 days — auth issues should be addressed promptly
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
export default notificationTypes
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
|
|
3
|
+
import { COMMUNICATION_CHANNELS_QUEUES } from './lib/queue'
|
|
4
|
+
|
|
5
|
+
type SchedulerServiceLike = {
|
|
6
|
+
register: (registration: {
|
|
7
|
+
id: string
|
|
8
|
+
name: string
|
|
9
|
+
scopeType: 'system' | 'organization' | 'tenant'
|
|
10
|
+
organizationId?: string
|
|
11
|
+
tenantId?: string
|
|
12
|
+
scheduleType: 'cron' | 'interval'
|
|
13
|
+
scheduleValue: string
|
|
14
|
+
timezone?: string
|
|
15
|
+
targetType: 'queue' | 'command'
|
|
16
|
+
targetQueue?: string
|
|
17
|
+
targetPayload?: unknown
|
|
18
|
+
sourceType?: 'user' | 'module'
|
|
19
|
+
sourceModule?: string
|
|
20
|
+
isEnabled?: boolean
|
|
21
|
+
description?: string
|
|
22
|
+
}) => Promise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tick interval in seconds. Default 60s per email integration spec
|
|
27
|
+
* § Hub Deltas → Delta 6 (scheduler mechanism).
|
|
28
|
+
*/
|
|
29
|
+
const POLL_TICK_INTERVAL_SECONDS = Math.max(
|
|
30
|
+
10,
|
|
31
|
+
Number.parseInt(process.env.OM_HUB_POLL_SCHEDULER_TICK_SECONDS ?? '60', 10) || 60,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `scheduled_jobs.id` is a uuid column, so a module-owned schedule's stable
|
|
36
|
+
* registration key must be hashed into a uuid rather than used verbatim — this
|
|
37
|
+
* keeps `schedulerService.register()` an idempotent upsert across re-runs of
|
|
38
|
+
* seedDefaults instead of trying to insert a raw string into the uuid PK.
|
|
39
|
+
*/
|
|
40
|
+
function stableScheduleUuid(stableKey: string): string {
|
|
41
|
+
const hex = createHash('sha256').update(stableKey).digest('hex')
|
|
42
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function stablePollTickScheduleId(organizationId: string): string {
|
|
46
|
+
return stableScheduleUuid(`communication_channels:poll-tick:${organizationId}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const setup: ModuleSetupConfig = {
|
|
50
|
+
defaultRoleFeatures: {
|
|
51
|
+
superadmin: [
|
|
52
|
+
'communication_channels.view',
|
|
53
|
+
'communication_channels.manage',
|
|
54
|
+
'communication_channels.react',
|
|
55
|
+
'communication_channels.assign',
|
|
56
|
+
'communication_channels.connect_user_channel',
|
|
57
|
+
'communication_channels.admin',
|
|
58
|
+
'communication_channels.channel.import_history',
|
|
59
|
+
'communication_channels.channel.push.manage',
|
|
60
|
+
],
|
|
61
|
+
admin: [
|
|
62
|
+
'communication_channels.view',
|
|
63
|
+
'communication_channels.manage',
|
|
64
|
+
'communication_channels.react',
|
|
65
|
+
'communication_channels.assign',
|
|
66
|
+
'communication_channels.connect_user_channel',
|
|
67
|
+
'communication_channels.admin',
|
|
68
|
+
'communication_channels.channel.import_history',
|
|
69
|
+
'communication_channels.channel.push.manage',
|
|
70
|
+
],
|
|
71
|
+
manager: [
|
|
72
|
+
'communication_channels.view',
|
|
73
|
+
'communication_channels.manage',
|
|
74
|
+
'communication_channels.react',
|
|
75
|
+
'communication_channels.assign',
|
|
76
|
+
'communication_channels.connect_user_channel',
|
|
77
|
+
],
|
|
78
|
+
employee: [
|
|
79
|
+
'communication_channels.view',
|
|
80
|
+
'communication_channels.react',
|
|
81
|
+
'communication_channels.connect_user_channel',
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async seedDefaults({ container, organizationId, tenantId }) {
|
|
86
|
+
/**
|
|
87
|
+
* Register the per-channel polling tick with `@open-mercato/scheduler`.
|
|
88
|
+
*
|
|
89
|
+
* Per email integration spec § Hub Deltas → Delta 6: every
|
|
90
|
+
* `POLL_TICK_INTERVAL_SECONDS` (default 60s), enqueue a single
|
|
91
|
+
* `communication-channels-poll-tick` job that enumerates due channels and
|
|
92
|
+
* fans out to the `communication-channels-poll` queue.
|
|
93
|
+
*
|
|
94
|
+
* The registration is per-organization (one tick row per org/tenant pair)
|
|
95
|
+
* so multi-tenant deploys schedule independently. Skipped silently when the
|
|
96
|
+
* scheduler module isn't enabled — keeps the hub usable in scheduler-less
|
|
97
|
+
* test harnesses.
|
|
98
|
+
*/
|
|
99
|
+
const cradle = container as { hasRegistration?: (name: string) => boolean }
|
|
100
|
+
if (typeof cradle.hasRegistration !== 'function' || !cradle.hasRegistration('schedulerService')) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
const schedulerService = container.resolve('schedulerService') as SchedulerServiceLike
|
|
104
|
+
// Best-effort: a scheduler failure must not abort tenant initialization for
|
|
105
|
+
// every other module (mirrors the ai_assistant setup pattern). The schedule
|
|
106
|
+
// ids are deterministic uuids so re-runs upsert idempotently.
|
|
107
|
+
try {
|
|
108
|
+
await schedulerService.register({
|
|
109
|
+
id: stablePollTickScheduleId(organizationId),
|
|
110
|
+
name: 'Communication channels poll tick',
|
|
111
|
+
description:
|
|
112
|
+
`Enumerates active polling channels every ${POLL_TICK_INTERVAL_SECONDS}s and enqueues per-channel poll jobs.`,
|
|
113
|
+
scopeType: 'organization',
|
|
114
|
+
organizationId,
|
|
115
|
+
tenantId,
|
|
116
|
+
scheduleType: 'interval',
|
|
117
|
+
scheduleValue: `${POLL_TICK_INTERVAL_SECONDS}s`,
|
|
118
|
+
timezone: 'UTC',
|
|
119
|
+
targetType: 'queue',
|
|
120
|
+
targetQueue: COMMUNICATION_CHANNELS_QUEUES.pollTick,
|
|
121
|
+
targetPayload: {
|
|
122
|
+
scope: { tenantId, organizationId },
|
|
123
|
+
},
|
|
124
|
+
sourceType: 'module',
|
|
125
|
+
sourceModule: 'communication_channels',
|
|
126
|
+
isEnabled: true,
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Spec C § Phase C4 — Gmail watch renewal cron, per-org so multi-tenant
|
|
130
|
+
// deploys schedule independently.
|
|
131
|
+
await schedulerService.register({
|
|
132
|
+
id: stableScheduleUuid(`communication_channels:gmail-renew-watch:${organizationId}`),
|
|
133
|
+
name: 'Gmail watch renewal',
|
|
134
|
+
description:
|
|
135
|
+
'Daily 04:00 UTC. Re-issues gmail.users.watch for channels within OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS of expiry.',
|
|
136
|
+
scopeType: 'organization',
|
|
137
|
+
organizationId,
|
|
138
|
+
tenantId,
|
|
139
|
+
scheduleType: 'cron',
|
|
140
|
+
scheduleValue: '0 4 * * *',
|
|
141
|
+
timezone: 'UTC',
|
|
142
|
+
targetType: 'queue',
|
|
143
|
+
targetQueue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,
|
|
144
|
+
targetPayload: { scope: { tenantId, organizationId } },
|
|
145
|
+
sourceType: 'module',
|
|
146
|
+
sourceModule: 'communication_channels',
|
|
147
|
+
isEnabled: true,
|
|
148
|
+
})
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.warn(
|
|
151
|
+
'[communication_channels] Failed to register module schedules:',
|
|
152
|
+
error instanceof Error ? error.message : error,
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default setup
|
package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
3
|
+
import { CommunicationChannel } from '../data/entities'
|
|
4
|
+
import { notificationTypes } from '../notifications'
|
|
5
|
+
import {
|
|
6
|
+
buildNotificationFromType,
|
|
7
|
+
buildFeatureNotificationFromType,
|
|
8
|
+
} from '../../notifications/lib/notificationBuilder'
|
|
9
|
+
import { resolveNotificationService } from '../../notifications/lib/notificationService'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Subscriber: re-authentication notification.
|
|
13
|
+
*
|
|
14
|
+
* Listens to `communication_channels.channel.requires_reauth` (emitted by the
|
|
15
|
+
* poll worker and outbound delivery when an
|
|
16
|
+
* adapter loses authorization) and raises the in-app
|
|
17
|
+
* `communication_channels.channel.requires_reauth` notification so the affected
|
|
18
|
+
* user sees a bell entry + reconnect CTA and the reactive toast handler
|
|
19
|
+
* (`notifications.handlers.ts`) fires. This is the producer half of the
|
|
20
|
+
* notification contract declared in `notifications.ts` /
|
|
21
|
+
* `notifications.client.ts` — without it the notification is never persisted.
|
|
22
|
+
*
|
|
23
|
+
* Recipient: per-user channels notify the channel owner (`channel.userId`) — the
|
|
24
|
+
* only person who can complete their own OAuth/credential reconnect. Tenant-wide
|
|
25
|
+
* (shared) channels have no owner, so operators holding
|
|
26
|
+
* `communication_channels.manage` are notified instead.
|
|
27
|
+
*
|
|
28
|
+
* Idempotency: `groupKey = channelId` collapses repeated reauth events for the
|
|
29
|
+
* same channel onto one notification (the notification service dedupes by
|
|
30
|
+
* tenant/org/recipient/type/groupKey under an advisory lock), so a flapping
|
|
31
|
+
* channel or a subscriber retry never spams the bell.
|
|
32
|
+
*/
|
|
33
|
+
export const metadata = {
|
|
34
|
+
event: 'communication_channels.channel.requires_reauth',
|
|
35
|
+
persistent: true,
|
|
36
|
+
id: 'communication_channels:channel-requires-reauth-notification',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const NOTIFICATION_TYPE = 'communication_channels.channel.requires_reauth'
|
|
40
|
+
const MANAGE_FEATURE = 'communication_channels.manage'
|
|
41
|
+
|
|
42
|
+
type RequiresReauthPayload = {
|
|
43
|
+
channelId?: string
|
|
44
|
+
providerKey?: string
|
|
45
|
+
channelType?: string
|
|
46
|
+
reason?: string
|
|
47
|
+
tenantId?: string
|
|
48
|
+
organizationId?: string | null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type SubscriberContext = {
|
|
52
|
+
resolve: <T = unknown>(name: string) => T
|
|
53
|
+
container?: { resolve: <T = unknown>(name: string) => T }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveFromCtx<T = unknown>(ctx: SubscriberContext, name: string): T {
|
|
57
|
+
if (typeof ctx?.resolve === 'function') return ctx.resolve<T>(name)
|
|
58
|
+
if (ctx?.container && typeof ctx.container.resolve === 'function') {
|
|
59
|
+
return ctx.container.resolve<T>(name)
|
|
60
|
+
}
|
|
61
|
+
throw new Error(
|
|
62
|
+
`channel-requires-reauth-notification: subscriber context has no resolver (looking for '${name}')`,
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default async function handler(
|
|
67
|
+
payload: RequiresReauthPayload,
|
|
68
|
+
ctx: SubscriberContext,
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
if (!payload?.channelId || !payload.tenantId) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const typeDef = notificationTypes.find((type) => type.type === NOTIFICATION_TYPE)
|
|
75
|
+
if (!typeDef) return
|
|
76
|
+
|
|
77
|
+
const scope = {
|
|
78
|
+
tenantId: payload.tenantId,
|
|
79
|
+
organizationId: payload.organizationId ?? null,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const em = resolveFromCtx<EntityManager>(ctx, 'em').fork()
|
|
83
|
+
const channel = await findOneWithDecryption(
|
|
84
|
+
em,
|
|
85
|
+
CommunicationChannel,
|
|
86
|
+
{
|
|
87
|
+
id: payload.channelId,
|
|
88
|
+
tenantId: payload.tenantId,
|
|
89
|
+
organizationId: payload.organizationId ?? null,
|
|
90
|
+
deletedAt: null,
|
|
91
|
+
},
|
|
92
|
+
undefined,
|
|
93
|
+
scope,
|
|
94
|
+
)
|
|
95
|
+
if (!channel) return
|
|
96
|
+
|
|
97
|
+
const notificationService = resolveNotificationService(ctx)
|
|
98
|
+
const common = {
|
|
99
|
+
sourceEntityType: 'communication_channel',
|
|
100
|
+
sourceEntityId: payload.channelId,
|
|
101
|
+
groupKey: payload.channelId,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (channel.userId) {
|
|
105
|
+
await notificationService.create(
|
|
106
|
+
buildNotificationFromType(typeDef, { ...common, recipientUserId: channel.userId }),
|
|
107
|
+
scope,
|
|
108
|
+
)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Tenant-wide (shared) channel — no single owner. Notify operators who can
|
|
113
|
+
// reconnect it on behalf of the tenant.
|
|
114
|
+
await notificationService.createForFeature(
|
|
115
|
+
buildFeatureNotificationFromType(typeDef, { ...common, requiredFeature: MANAGE_FEATURE }),
|
|
116
|
+
scope,
|
|
117
|
+
)
|
|
118
|
+
}
|