@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,205 @@
|
|
|
1
|
+
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
2
|
+
import { CustomerInteraction } from "../data/entities.js";
|
|
3
|
+
import { buildEmailVisibilityMikroFilter } from "./visibilityFilter.js";
|
|
4
|
+
const DEFAULT_MAX_THREADS = 50;
|
|
5
|
+
const DEFAULT_MAX_MESSAGES_PER_THREAD = 200;
|
|
6
|
+
const PREVIEW_LENGTH = 140;
|
|
7
|
+
function extractAddresses(value) {
|
|
8
|
+
const out = [];
|
|
9
|
+
const pushOne = (item) => {
|
|
10
|
+
if (typeof item === "string") {
|
|
11
|
+
const trimmed = item.trim();
|
|
12
|
+
if (trimmed) out.push({ email: trimmed, name: null });
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (item && typeof item === "object") {
|
|
16
|
+
const rec = item;
|
|
17
|
+
const address = typeof rec.address === "string" ? rec.address.trim() : null;
|
|
18
|
+
const name = typeof rec.name === "string" && rec.name.trim() ? rec.name.trim() : null;
|
|
19
|
+
if (address) out.push({ email: address, name });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
for (const item of value) pushOne(item);
|
|
24
|
+
} else {
|
|
25
|
+
pushOne(value);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
function firstString(...values) {
|
|
30
|
+
for (const value of values) {
|
|
31
|
+
if (typeof value === "string" && value.trim().length > 0) return value;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function toStringArray(...values) {
|
|
36
|
+
for (const value of values) {
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
const strings = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
39
|
+
if (strings.length > 0) return strings;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
function truncate(value) {
|
|
45
|
+
if (!value) return value;
|
|
46
|
+
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
47
|
+
if (collapsed.length <= PREVIEW_LENGTH) return collapsed;
|
|
48
|
+
return `${collapsed.slice(0, PREVIEW_LENGTH - 1)}\u2026`;
|
|
49
|
+
}
|
|
50
|
+
async function buildPersonEmailThreads(em, opts) {
|
|
51
|
+
const {
|
|
52
|
+
personId,
|
|
53
|
+
tenantId,
|
|
54
|
+
organizationId,
|
|
55
|
+
viewerUserId,
|
|
56
|
+
userFeatures,
|
|
57
|
+
maxThreads = DEFAULT_MAX_THREADS,
|
|
58
|
+
maxMessagesPerThread = DEFAULT_MAX_MESSAGES_PER_THREAD
|
|
59
|
+
} = opts;
|
|
60
|
+
const interactionWhere = {
|
|
61
|
+
entity: personId,
|
|
62
|
+
interactionType: "email",
|
|
63
|
+
deletedAt: null,
|
|
64
|
+
tenantId
|
|
65
|
+
};
|
|
66
|
+
if (organizationId) interactionWhere.organizationId = organizationId;
|
|
67
|
+
interactionWhere.$or = buildEmailVisibilityMikroFilter({
|
|
68
|
+
currentUserId: viewerUserId,
|
|
69
|
+
userFeatures
|
|
70
|
+
}).$or;
|
|
71
|
+
const dscope = { tenantId, organizationId: organizationId ?? null };
|
|
72
|
+
const interactions = await findWithDecryption(
|
|
73
|
+
em,
|
|
74
|
+
CustomerInteraction,
|
|
75
|
+
interactionWhere,
|
|
76
|
+
{ orderBy: { occurredAt: "desc", createdAt: "desc" }, limit: maxThreads * 20 },
|
|
77
|
+
dscope
|
|
78
|
+
);
|
|
79
|
+
const linkIdByInteraction = /* @__PURE__ */ new Map();
|
|
80
|
+
const linkIds = [];
|
|
81
|
+
for (const interaction of interactions) {
|
|
82
|
+
const linkId = interaction.externalMessageId;
|
|
83
|
+
if (!linkId || linkIdByInteraction.has(linkId)) continue;
|
|
84
|
+
linkIdByInteraction.set(linkId, { occurredAt: interaction.occurredAt ?? interaction.createdAt });
|
|
85
|
+
linkIds.push(linkId);
|
|
86
|
+
}
|
|
87
|
+
if (linkIds.length === 0) return [];
|
|
88
|
+
const linkWhere = { id: { $in: linkIds }, tenantId };
|
|
89
|
+
if (organizationId) linkWhere.organizationId = organizationId;
|
|
90
|
+
const links = await findWithDecryption(
|
|
91
|
+
em,
|
|
92
|
+
"MessageChannelLink",
|
|
93
|
+
linkWhere,
|
|
94
|
+
void 0,
|
|
95
|
+
dscope
|
|
96
|
+
);
|
|
97
|
+
const messageIds = Array.from(
|
|
98
|
+
new Set(
|
|
99
|
+
links.map((link) => typeof link.messageId === "string" ? link.messageId : null).filter((value) => !!value)
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
const messageById = /* @__PURE__ */ new Map();
|
|
103
|
+
if (messageIds.length > 0) {
|
|
104
|
+
const messageWhere = { id: { $in: messageIds }, tenantId };
|
|
105
|
+
if (organizationId) messageWhere.organizationId = organizationId;
|
|
106
|
+
const messages = await findWithDecryption(
|
|
107
|
+
em,
|
|
108
|
+
"Message",
|
|
109
|
+
messageWhere,
|
|
110
|
+
void 0,
|
|
111
|
+
dscope
|
|
112
|
+
);
|
|
113
|
+
for (const message of messages) {
|
|
114
|
+
if (typeof message.id === "string") messageById.set(message.id, message);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const threadsByKey = /* @__PURE__ */ new Map();
|
|
118
|
+
for (const link of links) {
|
|
119
|
+
const linkId = typeof link.id === "string" ? link.id : null;
|
|
120
|
+
if (!linkId) continue;
|
|
121
|
+
const direction = link.direction === "outbound" ? "outbound" : "inbound";
|
|
122
|
+
const providerKey = typeof link.providerKey === "string" ? link.providerKey : null;
|
|
123
|
+
const payload = link.channelPayload ?? null;
|
|
124
|
+
const meta = link.channelMetadata ?? null;
|
|
125
|
+
const messageId = typeof link.messageId === "string" ? link.messageId : null;
|
|
126
|
+
const message = messageId ? messageById.get(messageId) ?? null : null;
|
|
127
|
+
const primary = direction === "outbound" ? meta : payload;
|
|
128
|
+
const secondary = direction === "outbound" ? payload : meta;
|
|
129
|
+
const fromList = extractAddresses(primary?.from ?? secondary?.from);
|
|
130
|
+
const toList = extractAddresses(primary?.to ?? secondary?.to);
|
|
131
|
+
const ccList = extractAddresses(primary?.cc ?? secondary?.cc);
|
|
132
|
+
const subject = firstString(
|
|
133
|
+
primary?.subject,
|
|
134
|
+
secondary?.subject,
|
|
135
|
+
typeof message?.subject === "string" ? message.subject : null
|
|
136
|
+
);
|
|
137
|
+
const bodyText = firstString(
|
|
138
|
+
direction === "outbound" ? meta?.bodyText : payload?.text,
|
|
139
|
+
direction === "outbound" ? payload?.text : meta?.bodyText,
|
|
140
|
+
typeof message?.body === "string" ? message.body : null
|
|
141
|
+
);
|
|
142
|
+
const sentAtRaw = (typeof message?.sentAt === "string" || message?.sentAt instanceof Date ? message.sentAt : null) ?? (link.createdAt instanceof Date || typeof link.createdAt === "string" ? link.createdAt : null) ?? linkIdByInteraction.get(linkId)?.occurredAt ?? /* @__PURE__ */ new Date();
|
|
143
|
+
const sentAt = (sentAtRaw instanceof Date ? sentAtRaw : new Date(sentAtRaw)).toISOString();
|
|
144
|
+
const threadKey = (typeof message?.threadId === "string" && message.threadId ? message.threadId : null) ?? (messageId ? `message:${messageId}` : `link:${linkId}`);
|
|
145
|
+
const dto = {
|
|
146
|
+
id: linkId,
|
|
147
|
+
messageId,
|
|
148
|
+
rfcMessageId: firstString(primary?.messageId, secondary?.messageId),
|
|
149
|
+
references: toStringArray(primary?.references, secondary?.references),
|
|
150
|
+
direction,
|
|
151
|
+
fromName: fromList[0]?.name ?? null,
|
|
152
|
+
fromEmail: fromList[0]?.email ?? null,
|
|
153
|
+
to: toList.map((a) => a.email),
|
|
154
|
+
cc: ccList.map((a) => a.email),
|
|
155
|
+
subject,
|
|
156
|
+
bodyText,
|
|
157
|
+
sentAt,
|
|
158
|
+
providerKey
|
|
159
|
+
};
|
|
160
|
+
const existing = threadsByKey.get(threadKey);
|
|
161
|
+
if (existing) {
|
|
162
|
+
existing.messages.push(dto);
|
|
163
|
+
} else {
|
|
164
|
+
threadsByKey.set(threadKey, {
|
|
165
|
+
threadKey,
|
|
166
|
+
subject,
|
|
167
|
+
preview: null,
|
|
168
|
+
participants: [],
|
|
169
|
+
lastMessageAt: sentAt,
|
|
170
|
+
messageCount: 0,
|
|
171
|
+
providerKey,
|
|
172
|
+
lastDirection: direction,
|
|
173
|
+
messages: [dto]
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const threads = [];
|
|
178
|
+
for (const thread of threadsByKey.values()) {
|
|
179
|
+
thread.messages.sort((a, b) => a.sentAt.localeCompare(b.sentAt));
|
|
180
|
+
if (thread.messages.length > maxMessagesPerThread) {
|
|
181
|
+
thread.messages = thread.messages.slice(thread.messages.length - maxMessagesPerThread);
|
|
182
|
+
}
|
|
183
|
+
const last = thread.messages[thread.messages.length - 1];
|
|
184
|
+
const firstWithSubject = thread.messages.find((m) => m.subject);
|
|
185
|
+
const participantSet = /* @__PURE__ */ new Set();
|
|
186
|
+
for (const message of thread.messages) {
|
|
187
|
+
if (message.direction === "inbound" && message.fromEmail) participantSet.add(message.fromEmail);
|
|
188
|
+
if (message.direction === "outbound") message.to.forEach((email) => participantSet.add(email));
|
|
189
|
+
}
|
|
190
|
+
thread.subject = firstWithSubject?.subject ?? thread.subject;
|
|
191
|
+
thread.preview = truncate(last?.bodyText ?? null);
|
|
192
|
+
thread.participants = Array.from(participantSet);
|
|
193
|
+
thread.lastMessageAt = last?.sentAt ?? thread.lastMessageAt;
|
|
194
|
+
thread.lastDirection = last?.direction ?? thread.lastDirection;
|
|
195
|
+
thread.providerKey = last?.providerKey ?? thread.providerKey;
|
|
196
|
+
thread.messageCount = thread.messages.length;
|
|
197
|
+
threads.push(thread);
|
|
198
|
+
}
|
|
199
|
+
threads.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt));
|
|
200
|
+
return threads.slice(0, maxThreads);
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
buildPersonEmailThreads
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=personEmailThreads.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/lib/personEmailThreads.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CustomerInteraction } from '../data/entities'\nimport { buildEmailVisibilityMikroFilter } from './visibilityFilter'\n\n/**\n * Read model that turns a Person's email `CustomerInteraction` rows into\n * Gmail-style threads for the CRM Person page.\n *\n * The Person\u2194email anchor lives only on `CustomerInteraction`\n * (`interactionType='email'`, `externalMessageId` \u2192 `MessageChannelLink.id`).\n * The thread grouping key and the rich per-message content (From/To/Cc, body,\n * direction) live in the communication_channels hub. We read the hub entities\n * by their string class names \u2014 the same cross-module pattern used by\n * `link-channel-message-handler.ts` \u2014 so the customers module never imports the\n * hub's entity classes (root AGENTS.md: no direct ORM relationships between\n * modules).\n */\n\nexport type EmailThreadDirection = 'inbound' | 'outbound'\n\nexport type PersonEmailMessage = {\n id: string\n /** Open Mercato `messages.message` id \u2014 used as `parentMessageId` when replying so the reply joins this thread. */\n messageId: string | null\n /** RFC2822 Message-ID of this email \u2014 used as `In-Reply-To` on a reply. */\n rfcMessageId: string | null\n /** RFC2822 References chain for this email. */\n references: string[]\n direction: EmailThreadDirection\n fromName: string | null\n fromEmail: string | null\n to: string[]\n cc: string[]\n subject: string | null\n bodyText: string | null\n sentAt: string\n providerKey: string | null\n}\n\nexport type PersonEmailThread = {\n threadKey: string\n subject: string | null\n preview: string | null\n participants: string[]\n lastMessageAt: string\n messageCount: number\n providerKey: string | null\n lastDirection: EmailThreadDirection\n messages: PersonEmailMessage[]\n}\n\nexport type BuildPersonEmailThreadsOptions = {\n personId: string\n tenantId: string\n organizationId: string | null\n viewerUserId: string | null\n userFeatures: string[] | null | undefined\n maxThreads?: number\n maxMessagesPerThread?: number\n}\n\nconst DEFAULT_MAX_THREADS = 50\nconst DEFAULT_MAX_MESSAGES_PER_THREAD = 200\nconst PREVIEW_LENGTH = 140\n\ntype JsonRecord = Record<string, unknown>\n\n/** Extracts `{ email, name }[]` from the many shapes a channel address field can take. */\nfunction extractAddresses(value: unknown): Array<{ email: string; name: string | null }> {\n const out: Array<{ email: string; name: string | null }> = []\n const pushOne = (item: unknown): void => {\n if (typeof item === 'string') {\n const trimmed = item.trim()\n if (trimmed) out.push({ email: trimmed, name: null })\n return\n }\n if (item && typeof item === 'object') {\n const rec = item as JsonRecord\n const address = typeof rec.address === 'string' ? rec.address.trim() : null\n const name = typeof rec.name === 'string' && rec.name.trim() ? rec.name.trim() : null\n if (address) out.push({ email: address, name })\n }\n }\n if (Array.isArray(value)) {\n for (const item of value) pushOne(item)\n } else {\n pushOne(value)\n }\n return out\n}\n\nfunction firstString(...values: unknown[]): string | null {\n for (const value of values) {\n if (typeof value === 'string' && value.trim().length > 0) return value\n }\n return null\n}\n\nfunction toStringArray(...values: unknown[]): string[] {\n for (const value of values) {\n if (Array.isArray(value)) {\n const strings = value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0)\n if (strings.length > 0) return strings\n }\n }\n return []\n}\n\nfunction truncate(value: string | null): string | null {\n if (!value) return value\n const collapsed = value.replace(/\\s+/g, ' ').trim()\n if (collapsed.length <= PREVIEW_LENGTH) return collapsed\n return `${collapsed.slice(0, PREVIEW_LENGTH - 1)}\u2026`\n}\n\n/**\n * Builds the per-Person email-thread read model. Pure data assembly \u2014 callers\n * are responsible for auth and scoping (tenantId/organizationId required).\n */\nexport async function buildPersonEmailThreads(\n em: EntityManager,\n opts: BuildPersonEmailThreadsOptions,\n): Promise<PersonEmailThread[]> {\n const {\n personId,\n tenantId,\n organizationId,\n viewerUserId,\n userFeatures,\n maxThreads = DEFAULT_MAX_THREADS,\n maxMessagesPerThread = DEFAULT_MAX_MESSAGES_PER_THREAD,\n } = opts\n\n // \u2500\u2500 (1) Load the Person's email interactions (the Person\u2194email anchor) \u2500\u2500\n const interactionWhere: JsonRecord = {\n entity: personId,\n interactionType: 'email',\n deletedAt: null,\n tenantId,\n }\n if (organizationId) interactionWhere.organizationId = organizationId\n\n // Per-email visibility (v1: strict owner-only, no admin bypass) \u2014 the CRM\n // Person page applies the SAME rule as every other interactions read path via\n // `buildEmailVisibilityMikroFilter`, so the Emails tab and the `/interactions`\n // timeline can never disagree about who sees an email:\n // - `visibility = 'shared'` is visible to every user with CRM access to this\n // Person (lets a teammate pick up a handed-off thread),\n // - `visibility = 'private'` is visible ONLY to its author (the mailbox\n // owner) \u2014 never to teammates, not even an admin/superadmin (team\n // oversight is a deliberate v2 follow-up),\n // - legacy/unset rows (`visibility IS NULL`) stay visible so pre-existing\n // CRM history is never silently hidden.\n // Fail-closed: a null viewer (API-key caller) never matches the author arm, so\n // it only ever sees shared/legacy rows \u2014 never anyone's private email.\n interactionWhere.$or = buildEmailVisibilityMikroFilter({\n currentUserId: viewerUserId,\n userFeatures,\n }).$or\n\n // `customer_interaction.title`/`body` are encrypted at rest, so reads go\n // through `findWithDecryption` even though we only consume non-encrypted\n // columns here \u2014 this keeps the encrypted-entity contract intact.\n const dscope = { tenantId, organizationId: organizationId ?? null }\n const interactions = (await findWithDecryption(\n em,\n CustomerInteraction,\n interactionWhere as never,\n { orderBy: { occurredAt: 'desc', createdAt: 'desc' }, limit: maxThreads * 20 },\n dscope,\n )) as CustomerInteraction[]\n\n const linkIdByInteraction = new Map<string, { occurredAt: Date }>()\n const linkIds: string[] = []\n for (const interaction of interactions) {\n const linkId = interaction.externalMessageId\n if (!linkId || linkIdByInteraction.has(linkId)) continue\n linkIdByInteraction.set(linkId, { occurredAt: interaction.occurredAt ?? interaction.createdAt })\n linkIds.push(linkId)\n }\n if (linkIds.length === 0) return []\n\n // \u2500\u2500 (2) Resolve MessageChannelLink rows (rich content + direction) \u2500\u2500\u2500\u2500\u2500\u2500\n const linkWhere: JsonRecord = { id: { $in: linkIds }, tenantId }\n if (organizationId) linkWhere.organizationId = organizationId\n const links = (await findWithDecryption(\n em,\n 'MessageChannelLink' as never,\n linkWhere as never,\n undefined,\n dscope,\n )) as JsonRecord[]\n\n // \u2500\u2500 (3) Resolve hub Message rows (the authoritative thread grouping) \u2500\u2500\u2500\u2500\n const messageIds = Array.from(\n new Set(\n links\n .map((link) => (typeof link.messageId === 'string' ? (link.messageId as string) : null))\n .filter((value): value is string => !!value),\n ),\n )\n const messageById = new Map<string, JsonRecord>()\n if (messageIds.length > 0) {\n const messageWhere: JsonRecord = { id: { $in: messageIds }, tenantId }\n if (organizationId) messageWhere.organizationId = organizationId\n const messages = (await findWithDecryption(\n em,\n 'Message' as never,\n messageWhere as never,\n undefined,\n dscope,\n )) as JsonRecord[]\n for (const message of messages) {\n if (typeof message.id === 'string') messageById.set(message.id, message)\n }\n }\n\n // \u2500\u2500 (4) Build per-message DTOs grouped by thread \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const threadsByKey = new Map<string, PersonEmailThread>()\n\n for (const link of links) {\n const linkId = typeof link.id === 'string' ? (link.id as string) : null\n if (!linkId) continue\n const direction: EmailThreadDirection = link.direction === 'outbound' ? 'outbound' : 'inbound'\n const providerKey = typeof link.providerKey === 'string' ? (link.providerKey as string) : null\n const payload = (link.channelPayload ?? null) as JsonRecord | null\n const meta = (link.channelMetadata ?? null) as JsonRecord | null\n const messageId = typeof link.messageId === 'string' ? (link.messageId as string) : null\n const message = messageId ? messageById.get(messageId) ?? null : null\n\n // Outbound addresses live in channelMetadata; inbound in channelPayload.\n // Prefer the direction-appropriate source, fall back to the other.\n const primary = direction === 'outbound' ? meta : payload\n const secondary = direction === 'outbound' ? payload : meta\n const fromList = extractAddresses(primary?.from ?? secondary?.from)\n const toList = extractAddresses(primary?.to ?? secondary?.to)\n const ccList = extractAddresses(primary?.cc ?? secondary?.cc)\n\n const subject = firstString(\n primary?.subject,\n secondary?.subject,\n typeof message?.subject === 'string' ? message.subject : null,\n )\n const bodyText = firstString(\n direction === 'outbound' ? meta?.bodyText : payload?.text,\n direction === 'outbound' ? payload?.text : meta?.bodyText,\n typeof message?.body === 'string' ? message.body : null,\n )\n\n const sentAtRaw =\n (typeof message?.sentAt === 'string' || message?.sentAt instanceof Date\n ? message.sentAt\n : null) ??\n (link.createdAt instanceof Date || typeof link.createdAt === 'string' ? link.createdAt : null) ??\n linkIdByInteraction.get(linkId)?.occurredAt ??\n new Date()\n const sentAt = (sentAtRaw instanceof Date ? sentAtRaw : new Date(sentAtRaw)).toISOString()\n\n const threadKey =\n (typeof message?.threadId === 'string' && message.threadId ? message.threadId : null) ??\n (messageId ? `message:${messageId}` : `link:${linkId}`)\n\n const dto: PersonEmailMessage = {\n id: linkId,\n messageId,\n rfcMessageId: firstString(primary?.messageId, secondary?.messageId),\n references: toStringArray(primary?.references, secondary?.references),\n direction,\n fromName: fromList[0]?.name ?? null,\n fromEmail: fromList[0]?.email ?? null,\n to: toList.map((a) => a.email),\n cc: ccList.map((a) => a.email),\n subject,\n bodyText,\n sentAt,\n providerKey,\n }\n\n const existing = threadsByKey.get(threadKey)\n if (existing) {\n existing.messages.push(dto)\n } else {\n threadsByKey.set(threadKey, {\n threadKey,\n subject,\n preview: null,\n participants: [],\n lastMessageAt: sentAt,\n messageCount: 0,\n providerKey,\n lastDirection: direction,\n messages: [dto],\n })\n }\n }\n\n // \u2500\u2500 (5) Finalize thread summaries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const threads: PersonEmailThread[] = []\n for (const thread of threadsByKey.values()) {\n thread.messages.sort((a, b) => a.sentAt.localeCompare(b.sentAt))\n if (thread.messages.length > maxMessagesPerThread) {\n thread.messages = thread.messages.slice(thread.messages.length - maxMessagesPerThread)\n }\n const last = thread.messages[thread.messages.length - 1]\n const firstWithSubject = thread.messages.find((m) => m.subject)\n const participantSet = new Set<string>()\n for (const message of thread.messages) {\n // External counterpart = the \"from\" for inbound, the \"to\" for outbound.\n if (message.direction === 'inbound' && message.fromEmail) participantSet.add(message.fromEmail)\n if (message.direction === 'outbound') message.to.forEach((email) => participantSet.add(email))\n }\n thread.subject = firstWithSubject?.subject ?? thread.subject\n thread.preview = truncate(last?.bodyText ?? null)\n thread.participants = Array.from(participantSet)\n thread.lastMessageAt = last?.sentAt ?? thread.lastMessageAt\n thread.lastDirection = last?.direction ?? thread.lastDirection\n thread.providerKey = last?.providerKey ?? thread.providerKey\n thread.messageCount = thread.messages.length\n threads.push(thread)\n }\n\n threads.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt))\n return threads.slice(0, maxThreads)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { hasFeature } from "@open-mercato/shared/security/features";
|
|
2
|
+
const EMAIL_VIEW_PRIVATE_FEATURE = "customers.email.view_private";
|
|
3
|
+
function callerHasEmailViewPrivate(userFeatures) {
|
|
4
|
+
if (!Array.isArray(userFeatures) || userFeatures.length === 0) return false;
|
|
5
|
+
return hasFeature(userFeatures, EMAIL_VIEW_PRIVATE_FEATURE);
|
|
6
|
+
}
|
|
7
|
+
function canChangeEmailVisibility(opts) {
|
|
8
|
+
if (opts.interactionType !== "email") return true;
|
|
9
|
+
if ((opts.nextVisibility ?? null) === (opts.currentVisibility ?? null)) return true;
|
|
10
|
+
return Boolean(opts.actorUserId) && opts.authorUserId === opts.actorUserId;
|
|
11
|
+
}
|
|
12
|
+
function applyEmailVisibilityFilter(query, opts) {
|
|
13
|
+
const currentUserId = opts.currentUserId;
|
|
14
|
+
return query.where(
|
|
15
|
+
(eb) => eb.or([
|
|
16
|
+
eb("interaction_type", "!=", "email"),
|
|
17
|
+
eb("visibility", "is", null),
|
|
18
|
+
eb("visibility", "!=", "private"),
|
|
19
|
+
currentUserId ? eb("author_user_id", "=", currentUserId) : eb.val(false)
|
|
20
|
+
])
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
async function resolveCallerEmailFeatures(container, userId, tenantId, organizationId) {
|
|
24
|
+
if (!userId) return void 0;
|
|
25
|
+
try {
|
|
26
|
+
const rbac = container.resolve("rbacService");
|
|
27
|
+
if (!rbac?.getGrantedFeatures) return void 0;
|
|
28
|
+
return await rbac.getGrantedFeatures(userId, { tenantId, organizationId });
|
|
29
|
+
} catch {
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function buildEmailVisibilityMikroFilter(opts) {
|
|
34
|
+
return {
|
|
35
|
+
$or: [
|
|
36
|
+
{ interactionType: { $ne: "email" } },
|
|
37
|
+
{ visibility: null },
|
|
38
|
+
{ visibility: { $ne: "private" } },
|
|
39
|
+
...opts.currentUserId ? [{ authorUserId: opts.currentUserId }] : []
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
EMAIL_VIEW_PRIVATE_FEATURE,
|
|
45
|
+
applyEmailVisibilityFilter,
|
|
46
|
+
buildEmailVisibilityMikroFilter,
|
|
47
|
+
callerHasEmailViewPrivate,
|
|
48
|
+
canChangeEmailVisibility,
|
|
49
|
+
resolveCallerEmailFeatures
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=visibilityFilter.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/lib/visibilityFilter.ts"],
|
|
4
|
+
"sourcesContent": ["import type { FilterQuery } from '@mikro-orm/postgresql'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport { CustomerInteraction } from '../data/entities'\n\n/**\n * The ACL feature that grants admins the right to see private emails authored\n * by other users. Declared in `acl.ts` but granted to NO role in v1 (reserved\n * for the v2 oversight feature \u2014 see `callerHasEmailViewPrivate`).\n */\nexport const EMAIL_VIEW_PRIVATE_FEATURE = 'customers.email.view_private'\n\n/**\n * Returns true when the caller holds the admin override to see ALL private email\n * interactions. Honours wildcards (`customers.*`, `*`).\n *\n * RESERVED FOR v2 \u2014 NOT wired in v1. The v1 model is strict owner-only with no\n * admin bypass: the visibility filters and `canChangeEmailVisibility` ignore\n * caller features, and `customers.email.view_private` is granted to no role.\n * Kept (with {@link EMAIL_VIEW_PRIVATE_FEATURE}) so v2 oversight can opt back in\n * without re-introducing the helper. Do NOT wire this into a read path without\n * an explicit v2 spec.\n */\nexport function callerHasEmailViewPrivate(userFeatures: string[] | null | undefined): boolean {\n if (!Array.isArray(userFeatures) || userFeatures.length === 0) return false\n return hasFeature(userFeatures, EMAIL_VIEW_PRIVATE_FEATURE)\n}\n\n/**\n * Authorization predicate for CHANGING an email interaction's visibility.\n *\n * Personal mailbox privacy (v1: strict owner-only): ONLY the interaction's\n * author may flip their own email between private/shared \u2014 there is no admin\n * bypass. Non-email rows and no-op changes are always allowed. Mirrors the gate\n * in the dedicated `PATCH .../visibility` route so the generic interaction-update\n * path cannot bypass the privacy control. `userFeatures` is reserved for v2.\n */\nexport function canChangeEmailVisibility(opts: {\n interactionType: string\n currentVisibility: string | null | undefined\n nextVisibility: string | null | undefined\n authorUserId: string | null | undefined\n actorUserId: string | null | undefined\n userFeatures: string[] | null | undefined\n}): boolean {\n if (opts.interactionType !== 'email') return true\n if ((opts.nextVisibility ?? null) === (opts.currentVisibility ?? null)) return true\n return Boolean(opts.actorUserId) && opts.authorUserId === opts.actorUserId\n}\n\nexport interface ApplyEmailVisibilityFilterOptions {\n currentUserId: string | null\n userFeatures: string[] | null | undefined\n}\n\n/**\n * Adds a `WHERE` predicate to a kysely query so that:\n * - Non-email interactions (calls, meetings, tasks) pass through unchanged.\n * - Email interactions with `visibility = 'shared'` are visible to all.\n * - Email interactions with `visibility = 'private'` are visible ONLY to the\n * `authorUserId` (channel owner).\n *\n * Personal mailbox privacy (v1: strict owner-only) \u2014 there is NO admin bypass:\n * a private email is hidden from everyone except its author, including\n * admins/superadmins. `opts.userFeatures` is retained for signature stability\n * and reserved for the v2 admin-oversight feature.\n *\n * The function expects a kysely-style builder whose `.where()` accepts an\n * expression-builder callback. Returns the same builder for chaining.\n */\nexport function applyEmailVisibilityFilter<T extends { where: (...args: any[]) => T }>(\n query: T,\n opts: ApplyEmailVisibilityFilterOptions,\n): T {\n const currentUserId = opts.currentUserId\n // A row is hidden ONLY when it is an email explicitly marked `private` and the\n // caller is not its author. Everything else stays visible, including:\n // - non-email interactions (calls, meetings, tasks),\n // - emails marked `shared`,\n // - legacy/unset rows where `visibility IS NULL` (e.g. email-log entries\n // created before per-email visibility shipped) \u2014 these must remain\n // visible to avoid silently hiding pre-existing CRM history.\n return query.where((eb: any) =>\n eb.or([\n eb('interaction_type', '!=', 'email'),\n eb('visibility', 'is', null),\n eb('visibility', '!=', 'private'),\n currentUserId\n ? eb('author_user_id', '=', currentUserId)\n : eb.val(false),\n ]),\n )\n}\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[] | undefined>\n}\n\n/**\n * Resolve the caller's granted features (wildcard-aware downstream) so a v2\n * visibility filter could honour the `customers.email.view_private` admin\n * override. Returns `undefined` when there is no user or the RBAC service is\n * unavailable \u2014 callers MUST treat `undefined` as \"no bypass\" (fail closed).\n *\n * RESERVED FOR v2 \u2014 NOT called by any v1 read path. v1 is strict owner-only, so\n * the read routes pass `userFeatures: undefined` to the filters rather than\n * resolving features here (which would be a wasted RBAC round-trip). Re-wire\n * only under an explicit v2 oversight spec.\n */\nexport async function resolveCallerEmailFeatures(\n container: { resolve: (name: string) => unknown },\n userId: string | null,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<string[] | undefined> {\n if (!userId) return undefined\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.getGrantedFeatures) return undefined\n return await rbac.getGrantedFeatures(userId, { tenantId, organizationId })\n } catch {\n return undefined\n }\n}\n\n/**\n * MikroORM equivalent of {@link applyEmailVisibilityFilter}. Returns a\n * `FilterQuery` fragment to merge (implicit AND) into a `CustomerInteraction`\n * where-clause so private email rows are excluded for non-owner, non-admin\n * callers on MikroORM read paths (`findWithDecryption`/`em.find`/`em.count`).\n *\n * Mirrors the kysely predicate exactly, including the legacy `visibility IS NULL`\n * passthrough so pre-existing CRM history is never hidden. Personal mailbox\n * privacy (v1: strict owner-only): no admin bypass \u2014 a private email is hidden\n * from everyone except its author. `opts.userFeatures` is reserved for v2.\n */\nexport type EmailVisibilityMikroFilter = { $or?: FilterQuery<CustomerInteraction>[] }\n\nexport function buildEmailVisibilityMikroFilter(\n opts: ApplyEmailVisibilityFilterOptions,\n): EmailVisibilityMikroFilter {\n return {\n $or: [\n { interactionType: { $ne: 'email' } },\n { visibility: null },\n { visibility: { $ne: 'private' } },\n ...(opts.currentUserId ? [{ authorUserId: opts.currentUserId }] : []),\n ],\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,kBAAkB;AAQpB,MAAM,6BAA6B;AAanC,SAAS,0BAA0B,cAAoD;AAC5F,MAAI,CAAC,MAAM,QAAQ,YAAY,KAAK,aAAa,WAAW,EAAG,QAAO;AACtE,SAAO,WAAW,cAAc,0BAA0B;AAC5D;AAWO,SAAS,yBAAyB,MAO7B;AACV,MAAI,KAAK,oBAAoB,QAAS,QAAO;AAC7C,OAAK,KAAK,kBAAkB,WAAW,KAAK,qBAAqB,MAAO,QAAO;AAC/E,SAAO,QAAQ,KAAK,WAAW,KAAK,KAAK,iBAAiB,KAAK;AACjE;AAsBO,SAAS,2BACd,OACA,MACG;AACH,QAAM,gBAAgB,KAAK;AAQ3B,SAAO,MAAM;AAAA,IAAM,CAAC,OAClB,GAAG,GAAG;AAAA,MACJ,GAAG,oBAAoB,MAAM,OAAO;AAAA,MACpC,GAAG,cAAc,MAAM,IAAI;AAAA,MAC3B,GAAG,cAAc,MAAM,SAAS;AAAA,MAChC,gBACI,GAAG,kBAAkB,KAAK,aAAa,IACvC,GAAG,IAAI,KAAK;AAAA,IAClB,CAAC;AAAA,EACH;AACF;AAoBA,eAAsB,2BACpB,WACA,QACA,UACA,gBAC+B;AAC/B,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,UAAM,OAAO,UAAU,QAAQ,aAAa;AAC5C,QAAI,CAAC,MAAM,mBAAoB,QAAO;AACtC,WAAO,MAAM,KAAK,mBAAmB,QAAQ,EAAE,UAAU,eAAe,CAAC;AAAA,EAC3E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeO,SAAS,gCACd,MAC4B;AAC5B,SAAO;AAAA,IACL,KAAK;AAAA,MACH,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE;AAAA,MACpC,EAAE,YAAY,KAAK;AAAA,MACnB,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE;AAAA,MACjC,GAAI,KAAK,gBAAgB,CAAC,EAAE,cAAc,KAAK,cAAc,CAAC,IAAI,CAAC;AAAA,IACrE;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260527012240_customers extends Migration {
|
|
3
|
+
up() {
|
|
4
|
+
this.addSql(`alter table "customer_interactions" add "external_message_id" uuid null, add "channel_provider_key" text null;`);
|
|
5
|
+
this.addSql(`update "customer_interactions" set "visibility" = 'shared' where "interaction_type" = 'email' and "visibility" is null and "external_message_id" is null;`);
|
|
6
|
+
this.addSql(`create index "customer_interactions_external_msg_idx" on "customer_interactions" ("external_message_id") where "external_message_id" is not null;`);
|
|
7
|
+
this.addSql(`create unique index "customer_interactions_email_dedupe_uq" on "customer_interactions" ("entity_id", "external_message_id") where "external_message_id" is not null and "deleted_at" is null;`);
|
|
8
|
+
this.addSql(`create index "customer_interactions_email_visibility_idx" on "customer_interactions" ("entity_id", "interaction_type", "visibility", "author_user_id") where "interaction_type" = 'email' and "deleted_at" is null;`);
|
|
9
|
+
}
|
|
10
|
+
down() {
|
|
11
|
+
this.addSql(`drop index if exists "customer_interactions_email_visibility_idx";`);
|
|
12
|
+
this.addSql(`drop index if exists "customer_interactions_email_dedupe_uq";`);
|
|
13
|
+
this.addSql(`drop index if exists "customer_interactions_external_msg_idx";`);
|
|
14
|
+
this.addSql(`alter table "customer_interactions" drop column "external_message_id", drop column "channel_provider_key";`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
Migration20260527012240_customers
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=Migration20260527012240_customers.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/migrations/Migration20260527012240_customers.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260527012240_customers extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"customer_interactions\" add \"external_message_id\" uuid null, add \"channel_provider_key\" text null;`);\n // Backfill: pre-existing email-log interactions predate per-email visibility\n // and have null visibility. Mark them 'shared' so they keep their prior\n // visible-to-all behavior once the visibility filter is enabled.\n this.addSql(`update \"customer_interactions\" set \"visibility\" = 'shared' where \"interaction_type\" = 'email' and \"visibility\" is null and \"external_message_id\" is null;`);\n this.addSql(`create index \"customer_interactions_external_msg_idx\" on \"customer_interactions\" (\"external_message_id\") where \"external_message_id\" is not null;`);\n this.addSql(`create unique index \"customer_interactions_email_dedupe_uq\" on \"customer_interactions\" (\"entity_id\", \"external_message_id\") where \"external_message_id\" is not null and \"deleted_at\" is null;`);\n this.addSql(`create index \"customer_interactions_email_visibility_idx\" on \"customer_interactions\" (\"entity_id\", \"interaction_type\", \"visibility\", \"author_user_id\") where \"interaction_type\" = 'email' and \"deleted_at\" is null;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop index if exists \"customer_interactions_email_visibility_idx\";`);\n this.addSql(`drop index if exists \"customer_interactions_email_dedupe_uq\";`);\n this.addSql(`drop index if exists \"customer_interactions_external_msg_idx\";`);\n this.addSql(`alter table \"customer_interactions\" drop column \"external_message_id\", drop column \"channel_provider_key\";`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,0CAA0C,UAAU;AAAA,EAEtD,KAA2B;AAClC,SAAK,OAAO,gHAAgH;AAI5H,SAAK,OAAO,2JAA2J;AACvK,SAAK,OAAO,mJAAmJ;AAC/J,SAAK,OAAO,+LAA+L;AAC3M,SAAK,OAAO,qNAAqN;AAAA,EACnO;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,oEAAoE;AAChF,SAAK,OAAO,+DAA+D;AAC3E,SAAK,OAAO,gEAAgE;AAC5E,SAAK,OAAO,4GAA4G;AAAA,EAC1H;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/customers/setup.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { FeatureToggle } from '@open-mercato/core/modules/feature_toggles/data/entities'\nimport {\n ensureCustomerCustomFieldDefinitions,\n seedCustomerDictionaries,\n seedCurrencyDictionary,\n seedCustomerExamples,\n seedDefaultPipeline,\n} from './cli'\nimport { ensureDictionaryEntry } from './commands/shared'\nimport { DEFAULT_CUSTOMER_ROLE_TYPES } from './lib/customerRoleTypes'\n\nconst interactionFeatureToggles = [\n {\n identifier: 'customers.interactions.unified',\n name: 'Unified Interactions',\n description: 'When enabled, interactions use the unified canonical model instead of per-entity activity tracking.',\n category: 'customers',\n type: 'boolean' as const,\n defaultValue: false,\n },\n {\n identifier: 'customers.interactions.legacy-adapters',\n name: 'Interaction Legacy Adapters',\n description: 'When enabled, legacy activity/todo APIs are bridged to the canonical interaction model.',\n category: 'customers',\n type: 'boolean' as const,\n defaultValue: true,\n },\n {\n identifier: 'customers.interactions.external-sync',\n name: 'Interaction External Sync',\n description: 'When enabled, interactions can be synced from external systems (calendars, email providers).',\n category: 'customers',\n type: 'boolean' as const,\n defaultValue: false,\n },\n]\n\nasync function seedInteractionFeatureToggles(em: EntityManager): Promise<void> {\n for (const toggle of interactionFeatureToggles) {\n const existing = await em.findOne(FeatureToggle, { identifier: toggle.identifier, deletedAt: null })\n if (existing) continue\n const entity = em.create(FeatureToggle, {\n identifier: toggle.identifier,\n name: toggle.name,\n description: toggle.description,\n category: toggle.category,\n type: toggle.type,\n defaultValue: toggle.defaultValue,\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\nexport const setup: ModuleSetupConfig = {\n seedDefaults: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerDictionaries(ctx.em, scope)\n await seedCurrencyDictionary(ctx.em, scope)\n await seedDefaultPipeline(ctx.em, scope)\n await ensureCustomerCustomFieldDefinitions(ctx.em, ctx.tenantId)\n await seedInteractionFeatureToggles(ctx.em)\n for (const entry of DEFAULT_CUSTOMER_ROLE_TYPES) {\n await ensureDictionaryEntry(ctx.em, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n kind: 'customer_role_type',\n value: entry.value,\n label: entry.label,\n color: entry.color,\n icon: entry.icon,\n })\n }\n },\n\n seedExamples: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerExamples(ctx.em, ctx.container, scope)\n },\n\n defaultRoleFeatures: {\n admin: [\n 'customers.*',\n ],\n employee: [\n 'customers.people.view',\n 'customers.people.manage',\n 'customers.companies.view',\n 'customers.companies.manage',\n 'customers.deals.view',\n 'customers.deals.manage',\n 'customers.activities.view',\n 'customers.activities.manage',\n 'customers.pipelines.view',\n 'customers.interactions.view',\n 'customers.widgets.todos',\n 'customers.widgets.next-interactions',\n 'customers.widgets.new-customers',\n 'customers.widgets.new-deals',\n 'customers.roles.view',\n 'customers.roles.manage',\n ],\n },\n}\n\nexport default setup\n"],
|
|
5
|
-
"mappings": "AAEA,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAE5C,MAAM,4BAA4B;AAAA,EAChC;AAAA,IACE,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEA,eAAe,8BAA8B,IAAkC;AAC7E,aAAW,UAAU,2BAA2B;AAC9C,UAAM,WAAW,MAAM,GAAG,QAAQ,eAAe,EAAE,YAAY,OAAO,YAAY,WAAW,KAAK,CAAC;AACnG,QAAI,SAAU;AACd,UAAM,SAAS,GAAG,OAAO,eAAe;AAAA,MACtC,YAAY,OAAO;AAAA,MACnB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,cAAc,OAAO;AAAA,IACvB,CAAC;AACD,OAAG,QAAQ,MAAM;AAAA,EACnB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,MAAM,QAA2B;AAAA,EACtC,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,yBAAyB,IAAI,IAAI,KAAK;AAC5C,UAAM,uBAAuB,IAAI,IAAI,KAAK;AAC1C,UAAM,oBAAoB,IAAI,IAAI,KAAK;AACvC,UAAM,qCAAqC,IAAI,IAAI,IAAI,QAAQ;AAC/D,UAAM,8BAA8B,IAAI,EAAE;AAC1C,eAAW,SAAS,6BAA6B;AAC/C,YAAM,sBAAsB,IAAI,IAAI;AAAA,QAClC,UAAU,IAAI;AAAA,QACd,gBAAgB,IAAI;AAAA,QACpB,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,qBAAqB,IAAI,IAAI,IAAI,WAAW,KAAK;AAAA,EACzD;AAAA,EAEA,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;",
|
|
4
|
+
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { FeatureToggle } from '@open-mercato/core/modules/feature_toggles/data/entities'\nimport {\n ensureCustomerCustomFieldDefinitions,\n seedCustomerDictionaries,\n seedCurrencyDictionary,\n seedCustomerExamples,\n seedDefaultPipeline,\n} from './cli'\nimport { ensureDictionaryEntry } from './commands/shared'\nimport { DEFAULT_CUSTOMER_ROLE_TYPES } from './lib/customerRoleTypes'\n\nconst interactionFeatureToggles = [\n {\n identifier: 'customers.interactions.unified',\n name: 'Unified Interactions',\n description: 'When enabled, interactions use the unified canonical model instead of per-entity activity tracking.',\n category: 'customers',\n type: 'boolean' as const,\n defaultValue: false,\n },\n {\n identifier: 'customers.interactions.legacy-adapters',\n name: 'Interaction Legacy Adapters',\n description: 'When enabled, legacy activity/todo APIs are bridged to the canonical interaction model.',\n category: 'customers',\n type: 'boolean' as const,\n defaultValue: true,\n },\n {\n identifier: 'customers.interactions.external-sync',\n name: 'Interaction External Sync',\n description: 'When enabled, interactions can be synced from external systems (calendars, email providers).',\n category: 'customers',\n type: 'boolean' as const,\n defaultValue: false,\n },\n]\n\nasync function seedInteractionFeatureToggles(em: EntityManager): Promise<void> {\n for (const toggle of interactionFeatureToggles) {\n const existing = await em.findOne(FeatureToggle, { identifier: toggle.identifier, deletedAt: null })\n if (existing) continue\n const entity = em.create(FeatureToggle, {\n identifier: toggle.identifier,\n name: toggle.name,\n description: toggle.description,\n category: toggle.category,\n type: toggle.type,\n defaultValue: toggle.defaultValue,\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\nexport const setup: ModuleSetupConfig = {\n seedDefaults: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerDictionaries(ctx.em, scope)\n await seedCurrencyDictionary(ctx.em, scope)\n await seedDefaultPipeline(ctx.em, scope)\n await ensureCustomerCustomFieldDefinitions(ctx.em, ctx.tenantId)\n await seedInteractionFeatureToggles(ctx.em)\n for (const entry of DEFAULT_CUSTOMER_ROLE_TYPES) {\n await ensureDictionaryEntry(ctx.em, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n kind: 'customer_role_type',\n value: entry.value,\n label: entry.label,\n color: entry.color,\n icon: entry.icon,\n })\n }\n },\n\n seedExamples: async (ctx) => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n await seedCustomerExamples(ctx.em, ctx.container, scope)\n },\n\n defaultRoleFeatures: {\n admin: [\n 'customers.*',\n ],\n employee: [\n 'customers.people.view',\n 'customers.people.manage',\n 'customers.companies.view',\n 'customers.companies.manage',\n 'customers.deals.view',\n 'customers.deals.manage',\n 'customers.activities.view',\n 'customers.activities.manage',\n 'customers.pipelines.view',\n 'customers.interactions.view',\n 'customers.widgets.todos',\n 'customers.widgets.next-interactions',\n 'customers.widgets.new-customers',\n 'customers.widgets.new-deals',\n 'customers.roles.view',\n 'customers.roles.manage',\n 'customers.email.compose',\n ],\n },\n}\n\nexport default setup\n"],
|
|
5
|
+
"mappings": "AAEA,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAE5C,MAAM,4BAA4B;AAAA,EAChC;AAAA,IACE,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEA,eAAe,8BAA8B,IAAkC;AAC7E,aAAW,UAAU,2BAA2B;AAC9C,UAAM,WAAW,MAAM,GAAG,QAAQ,eAAe,EAAE,YAAY,OAAO,YAAY,WAAW,KAAK,CAAC;AACnG,QAAI,SAAU;AACd,UAAM,SAAS,GAAG,OAAO,eAAe;AAAA,MACtC,YAAY,OAAO;AAAA,MACnB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,cAAc,OAAO;AAAA,IACvB,CAAC;AACD,OAAG,QAAQ,MAAM;AAAA,EACnB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,MAAM,QAA2B;AAAA,EACtC,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,yBAAyB,IAAI,IAAI,KAAK;AAC5C,UAAM,uBAAuB,IAAI,IAAI,KAAK;AAC1C,UAAM,oBAAoB,IAAI,IAAI,KAAK;AACvC,UAAM,qCAAqC,IAAI,IAAI,IAAI,QAAQ;AAC/D,UAAM,8BAA8B,IAAI,EAAE;AAC1C,eAAW,SAAS,6BAA6B;AAC/C,YAAM,sBAAsB,IAAI,IAAI;AAAA,QAClC,UAAU,IAAI;AAAA,QACd,gBAAgB,IAAI;AAAA,QACpB,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,cAAc,OAAO,QAAQ;AAC3B,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAC3E,UAAM,qBAAqB,IAAI,IAAI,IAAI,WAAW,KAAK;AAAA,EACzD;AAAA,EAEA,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import linkChannelMessageHandler from "../lib/link-channel-message-handler.js";
|
|
2
|
+
const metadata = {
|
|
3
|
+
event: "communication_channels.message.received",
|
|
4
|
+
persistent: true,
|
|
5
|
+
id: "customers:link-channel-message-received"
|
|
6
|
+
};
|
|
7
|
+
var link_channel_message_received_default = linkChannelMessageHandler;
|
|
8
|
+
export {
|
|
9
|
+
link_channel_message_received_default as default,
|
|
10
|
+
metadata
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=link-channel-message-received.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/subscribers/link-channel-message-received.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Persistent subscriber for `communication_channels.message.received`.\n *\n * When an inbound email arrives, resolves People by address from the linked\n * MessageChannelLink and creates CustomerInteraction rows (one per match).\n * Falls back to threading-inheritance (In-Reply-To chain) when no direct\n * address match is found.\n *\n * Logic lives in `../lib/link-channel-message-handler.ts`; both this file\n * and `link-channel-message-sent.ts` delegate there so the two subscriber\n * registrations can share a single implementation.\n */\nimport linkChannelMessageHandler from '../lib/link-channel-message-handler'\n\nexport const metadata = {\n event: 'communication_channels.message.received',\n persistent: true,\n id: 'customers:link-channel-message-received',\n}\n\nexport default linkChannelMessageHandler\n"],
|
|
5
|
+
"mappings": "AAYA,OAAO,+BAA+B;AAE/B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,wCAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import linkChannelMessageHandler from "../lib/link-channel-message-handler.js";
|
|
2
|
+
const metadata = {
|
|
3
|
+
event: "communication_channels.message.sent",
|
|
4
|
+
persistent: true,
|
|
5
|
+
id: "customers:link-channel-message-sent"
|
|
6
|
+
};
|
|
7
|
+
var link_channel_message_sent_default = linkChannelMessageHandler;
|
|
8
|
+
export {
|
|
9
|
+
link_channel_message_sent_default as default,
|
|
10
|
+
metadata
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=link-channel-message-sent.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/subscribers/link-channel-message-sent.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Persistent subscriber for `communication_channels.message.sent`.\n *\n * When an outbound email is delivered, resolves People by address (or by the\n * `crmPersonId` hint written by the compose route) and creates CustomerInteraction\n * rows. Reads `crmVisibility` from `channelMetadata` to set the per-row\n * visibility ('private' | 'shared').\n *\n * Logic lives in `../lib/link-channel-message-handler.ts`; both this file\n * and `link-channel-message-received.ts` delegate there so the two subscriber\n * registrations can share a single implementation.\n */\nimport linkChannelMessageHandler from '../lib/link-channel-message-handler'\n\nexport const metadata = {\n event: 'communication_channels.message.sent',\n persistent: true,\n id: 'customers:link-channel-message-sent',\n}\n\nexport default linkChannelMessageHandler\n"],
|
|
5
|
+
"mappings": "AAYA,OAAO,+BAA+B;AAE/B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,oCAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -81,6 +81,9 @@ __decorateClass([
|
|
|
81
81
|
__decorateClass([
|
|
82
82
|
Property({ name: "tenant_id", type: "uuid" })
|
|
83
83
|
], IntegrationCredentials.prototype, "tenantId", 2);
|
|
84
|
+
__decorateClass([
|
|
85
|
+
Property({ name: "user_id", type: "uuid", nullable: true })
|
|
86
|
+
], IntegrationCredentials.prototype, "userId", 2);
|
|
84
87
|
__decorateClass([
|
|
85
88
|
Property({ name: "created_at", type: Date, onCreate: () => /* @__PURE__ */ new Date() })
|
|
86
89
|
], IntegrationCredentials.prototype, "createdAt", 2);
|
|
@@ -92,7 +95,11 @@ __decorateClass([
|
|
|
92
95
|
], IntegrationCredentials.prototype, "deletedAt", 2);
|
|
93
96
|
IntegrationCredentials = __decorateClass([
|
|
94
97
|
Entity({ tableName: "integration_credentials" }),
|
|
95
|
-
Index({ properties: ["integrationId", "organizationId", "tenantId"] })
|
|
98
|
+
Index({ properties: ["integrationId", "organizationId", "tenantId"] }),
|
|
99
|
+
Index({
|
|
100
|
+
name: "integration_credentials_user_lookup_idx",
|
|
101
|
+
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`
|
|
102
|
+
})
|
|
96
103
|
], IntegrationCredentials);
|
|
97
104
|
OptionalProps;
|
|
98
105
|
let IntegrationState = class {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/integrations/data/entities.ts"],
|
|
4
|
-
"sourcesContent": ["import { OptionalProps } from '@mikro-orm/core'\nimport { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\n\n@Entity({ tableName: 'sync_external_id_mappings' })\n@Index({ properties: ['internalEntityType', 'internalEntityId', 'organizationId'] })\n@Index({ properties: ['integrationId', 'externalId', 'organizationId'] })\nexport class SyncExternalIdMapping {\n [OptionalProps]?: 'syncStatus' | 'lastSyncedAt' | 'createdAt' | 'updatedAt' | 'deletedAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'internal_entity_type', type: 'text' })\n internalEntityType!: string\n\n @Property({ name: 'internal_entity_id', type: 'uuid' })\n internalEntityId!: string\n\n @Property({ name: 'external_id', type: 'text' })\n externalId!: string\n\n @Property({ name: 'sync_status', type: 'text', default: 'not_synced' })\n syncStatus: 'synced' | 'pending' | 'error' | 'not_synced' = 'not_synced'\n\n @Property({ name: 'last_synced_at', type: Date, nullable: true })\n lastSyncedAt?: Date | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'integration_credentials' })\n@Index({ properties: ['integrationId', 'organizationId', 'tenantId'] })\nexport class IntegrationCredentials {\n [OptionalProps]?: 'createdAt' | 'updatedAt' | 'deletedAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'credentials', type: 'json' })\n credentials!: Record<string, unknown>\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'integration_states' })\n@Index({ properties: ['integrationId', 'organizationId', 'tenantId'] })\nexport class IntegrationState {\n [OptionalProps]?: 'isEnabled' | 'apiVersion' | 'reauthRequired' | 'lastHealthStatus' | 'lastHealthCheckedAt' | 'lastHealthLatencyMs' | 'enabledAt' | 'createdAt' | 'updatedAt' | 'deletedAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'is_enabled', type: 'boolean', default: true })\n isEnabled: boolean = true\n\n @Property({ name: 'api_version', type: 'text', nullable: true })\n apiVersion?: string | null\n\n @Property({ name: 'reauth_required', type: 'boolean', default: false })\n reauthRequired: boolean = false\n\n @Property({ name: 'last_health_status', type: 'text', nullable: true })\n lastHealthStatus?: 'healthy' | 'degraded' | 'unhealthy' | null\n\n @Property({ name: 'last_health_checked_at', type: Date, nullable: true })\n lastHealthCheckedAt?: Date | null\n\n @Property({ name: 'last_health_latency_ms', type: 'int', nullable: true })\n lastHealthLatencyMs?: number | null\n\n @Property({ name: 'enabled_at', type: Date, nullable: true })\n enabledAt?: Date | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'integration_logs' })\n@Index({ properties: ['integrationId', 'organizationId', 'tenantId', 'createdAt'] })\n@Index({ properties: ['level', 'organizationId', 'tenantId', 'createdAt'] })\nexport class IntegrationLog {\n [OptionalProps]?: 'runId' | 'scopeEntityType' | 'scopeEntityId' | 'code' | 'payload' | 'createdAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'run_id', type: 'uuid', nullable: true })\n runId?: string | null\n\n @Property({ name: 'scope_entity_type', type: 'text', nullable: true })\n scopeEntityType?: string | null\n\n @Property({ name: 'scope_entity_id', type: 'uuid', nullable: true })\n scopeEntityId?: string | null\n\n @Property({ name: 'level', type: 'text' })\n level!: 'info' | 'warn' | 'error'\n\n @Property({ name: 'message', type: 'text' })\n message!: string\n\n @Property({ name: 'code', type: 'text', nullable: true })\n code?: string | null\n\n @Property({ name: 'payload', type: 'json', nullable: true })\n payload?: Record<string, unknown> | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,QAAQ,OAAO,YAAY,gBAAgB;AAMjD;AADI,IAAM,wBAAN,MAA4B;AAAA,EAA5B;AAkBL,sBAA4D;AAY5D,qBAAkB,oBAAI,KAAK;AAG3B,qBAAkB,oBAAI,KAAK;AAAA;AAI7B;AAlCE;AAAA,EADC,WAAW,EAAE,MAAM,QAAQ,YAAY,oBAAoB,CAAC;AAAA,GAFlD,sBAGX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,kBAAkB,MAAM,OAAO,CAAC;AAAA,GALvC,sBAMX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,wBAAwB,MAAM,OAAO,CAAC;AAAA,GAR7C,sBASX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,sBAAsB,MAAM,OAAO,CAAC;AAAA,GAX3C,sBAYX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,eAAe,MAAM,OAAO,CAAC;AAAA,GAdpC,sBAeX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,eAAe,MAAM,QAAQ,SAAS,aAAa,CAAC;AAAA,GAjB3D,sBAkBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,kBAAkB,MAAM,MAAM,UAAU,KAAK,CAAC;AAAA,GApBrD,sBAqBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,mBAAmB,MAAM,OAAO,CAAC;AAAA,GAvBxC,sBAwBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,GA1BlC,sBA2BX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,cAAc,MAAM,MAAM,UAAU,MAAM,oBAAI,KAAK,EAAE,CAAC;AAAA,GA7B7D,sBA8BX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,cAAc,MAAM,MAAM,UAAU,MAAM,oBAAI,KAAK,EAAE,CAAC;AAAA,GAhC7D,sBAiCX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,cAAc,MAAM,MAAM,UAAU,KAAK,CAAC;AAAA,GAnCjD,sBAoCX;AApCW,wBAAN;AAAA,EAHN,OAAO,EAAE,WAAW,4BAA4B,CAAC;AAAA,EACjD,MAAM,EAAE,YAAY,CAAC,sBAAsB,oBAAoB,gBAAgB,EAAE,CAAC;AAAA,EAClF,MAAM,EAAE,YAAY,CAAC,iBAAiB,cAAc,gBAAgB,EAAE,CAAC;AAAA,GAC3D;
|
|
4
|
+
"sourcesContent": ["import { OptionalProps } from '@mikro-orm/core'\nimport { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\n\n@Entity({ tableName: 'sync_external_id_mappings' })\n@Index({ properties: ['internalEntityType', 'internalEntityId', 'organizationId'] })\n@Index({ properties: ['integrationId', 'externalId', 'organizationId'] })\nexport class SyncExternalIdMapping {\n [OptionalProps]?: 'syncStatus' | 'lastSyncedAt' | 'createdAt' | 'updatedAt' | 'deletedAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'internal_entity_type', type: 'text' })\n internalEntityType!: string\n\n @Property({ name: 'internal_entity_id', type: 'uuid' })\n internalEntityId!: string\n\n @Property({ name: 'external_id', type: 'text' })\n externalId!: string\n\n @Property({ name: 'sync_status', type: 'text', default: 'not_synced' })\n syncStatus: 'synced' | 'pending' | 'error' | 'not_synced' = 'not_synced'\n\n @Property({ name: 'last_synced_at', type: Date, nullable: true })\n lastSyncedAt?: Date | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'integration_credentials' })\n@Index({ properties: ['integrationId', 'organizationId', 'tenantId'] })\n@Index({\n name: 'integration_credentials_user_lookup_idx',\n expression:\n `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`,\n})\nexport class IntegrationCredentials {\n [OptionalProps]?: 'createdAt' | 'updatedAt' | 'deletedAt' | 'userId'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'credentials', type: 'json' })\n credentials!: Record<string, unknown>\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n /**\n * Per-user secret scoping (additive \u2014 added by the email integration spec).\n *\n * NULL = tenant-wide secret (existing behaviour, e.g. shared WhatsApp Business token).\n * Set = per-user secret (e.g. Jane's personal Gmail OAuth refresh token).\n *\n * Cross-module link to `auth:user` is declared in\n * `packages/core/src/modules/communication_channels/data/extensions.ts` via\n * `EntityExtension` \u2014 no raw FOREIGN KEY constraint (root `AGENTS.md` rule:\n * \"No direct ORM relationships between modules\"). App-layer query callers\n * MUST scope by `user_id` on every per-user credential read.\n */\n @Property({ name: 'user_id', type: 'uuid', nullable: true })\n userId?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'integration_states' })\n@Index({ properties: ['integrationId', 'organizationId', 'tenantId'] })\nexport class IntegrationState {\n [OptionalProps]?: 'isEnabled' | 'apiVersion' | 'reauthRequired' | 'lastHealthStatus' | 'lastHealthCheckedAt' | 'lastHealthLatencyMs' | 'enabledAt' | 'createdAt' | 'updatedAt' | 'deletedAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'is_enabled', type: 'boolean', default: true })\n isEnabled: boolean = true\n\n @Property({ name: 'api_version', type: 'text', nullable: true })\n apiVersion?: string | null\n\n @Property({ name: 'reauth_required', type: 'boolean', default: false })\n reauthRequired: boolean = false\n\n @Property({ name: 'last_health_status', type: 'text', nullable: true })\n lastHealthStatus?: 'healthy' | 'degraded' | 'unhealthy' | null\n\n @Property({ name: 'last_health_checked_at', type: Date, nullable: true })\n lastHealthCheckedAt?: Date | null\n\n @Property({ name: 'last_health_latency_ms', type: 'int', nullable: true })\n lastHealthLatencyMs?: number | null\n\n @Property({ name: 'enabled_at', type: Date, nullable: true })\n enabledAt?: Date | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'integration_logs' })\n@Index({ properties: ['integrationId', 'organizationId', 'tenantId', 'createdAt'] })\n@Index({ properties: ['level', 'organizationId', 'tenantId', 'createdAt'] })\nexport class IntegrationLog {\n [OptionalProps]?: 'runId' | 'scopeEntityType' | 'scopeEntityId' | 'code' | 'payload' | 'createdAt'\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'integration_id', type: 'text' })\n integrationId!: string\n\n @Property({ name: 'run_id', type: 'uuid', nullable: true })\n runId?: string | null\n\n @Property({ name: 'scope_entity_type', type: 'text', nullable: true })\n scopeEntityType?: string | null\n\n @Property({ name: 'scope_entity_id', type: 'uuid', nullable: true })\n scopeEntityId?: string | null\n\n @Property({ name: 'level', type: 'text' })\n level!: 'info' | 'warn' | 'error'\n\n @Property({ name: 'message', type: 'text' })\n message!: string\n\n @Property({ name: 'code', type: 'text', nullable: true })\n code?: string | null\n\n @Property({ name: 'payload', type: 'json', nullable: true })\n payload?: Record<string, unknown> | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -29,6 +29,20 @@ function normalizeCredentialsRecord(value) {
|
|
|
29
29
|
const parsed = parseDecryptedFieldValue(value);
|
|
30
30
|
return isRecordValue(parsed) ? parsed : {};
|
|
31
31
|
}
|
|
32
|
+
function buildCredentialsFilter(integrationId, scope) {
|
|
33
|
+
const base = {
|
|
34
|
+
integrationId,
|
|
35
|
+
organizationId: scope.organizationId,
|
|
36
|
+
tenantId: scope.tenantId,
|
|
37
|
+
deletedAt: null
|
|
38
|
+
};
|
|
39
|
+
if (scope.userId) {
|
|
40
|
+
base.userId = scope.userId;
|
|
41
|
+
} else {
|
|
42
|
+
base.userId = null;
|
|
43
|
+
}
|
|
44
|
+
return base;
|
|
45
|
+
}
|
|
32
46
|
function createCredentialsService(em) {
|
|
33
47
|
const credentialsEncryptionSpec = [{ field: "credentials" }];
|
|
34
48
|
async function ensureCredentialsEncryptionMap(scope) {
|
|
@@ -89,18 +103,22 @@ function createCredentialsService(em) {
|
|
|
89
103
|
}
|
|
90
104
|
return {
|
|
91
105
|
async getRaw(integrationId, scope) {
|
|
92
|
-
|
|
106
|
+
let row = await findOneWithDecryption(
|
|
93
107
|
em,
|
|
94
108
|
IntegrationCredentials,
|
|
95
|
-
|
|
96
|
-
integrationId,
|
|
97
|
-
organizationId: scope.organizationId,
|
|
98
|
-
tenantId: scope.tenantId,
|
|
99
|
-
deletedAt: null
|
|
100
|
-
},
|
|
109
|
+
buildCredentialsFilter(integrationId, scope),
|
|
101
110
|
void 0,
|
|
102
111
|
scope
|
|
103
112
|
);
|
|
113
|
+
if (!row && scope.userId) {
|
|
114
|
+
row = await findOneWithDecryption(
|
|
115
|
+
em,
|
|
116
|
+
IntegrationCredentials,
|
|
117
|
+
buildCredentialsFilter(integrationId, { ...scope, userId: null }),
|
|
118
|
+
void 0,
|
|
119
|
+
scope
|
|
120
|
+
);
|
|
121
|
+
}
|
|
104
122
|
if (!row) return null;
|
|
105
123
|
return decryptCredentialsBlob(row.credentials, scope);
|
|
106
124
|
},
|
|
@@ -117,12 +135,7 @@ function createCredentialsService(em) {
|
|
|
117
135
|
const row = await findOneWithDecryption(
|
|
118
136
|
em,
|
|
119
137
|
IntegrationCredentials,
|
|
120
|
-
|
|
121
|
-
integrationId,
|
|
122
|
-
organizationId: scope.organizationId,
|
|
123
|
-
tenantId: scope.tenantId,
|
|
124
|
-
deletedAt: null
|
|
125
|
-
},
|
|
138
|
+
buildCredentialsFilter(integrationId, scope),
|
|
126
139
|
void 0,
|
|
127
140
|
scope
|
|
128
141
|
);
|
|
@@ -135,7 +148,8 @@ function createCredentialsService(em) {
|
|
|
135
148
|
integrationId,
|
|
136
149
|
credentials: encryptedCredentials,
|
|
137
150
|
organizationId: scope.organizationId,
|
|
138
|
-
tenantId: scope.tenantId
|
|
151
|
+
tenantId: scope.tenantId,
|
|
152
|
+
...scope.userId ? { userId: scope.userId } : {}
|
|
139
153
|
});
|
|
140
154
|
await em.persist(created).flush();
|
|
141
155
|
},
|
|
@@ -158,6 +172,7 @@ function createCredentialsService(em) {
|
|
|
158
172
|
}
|
|
159
173
|
export {
|
|
160
174
|
CredentialsEncryptionUnavailableError,
|
|
175
|
+
buildCredentialsFilter,
|
|
161
176
|
createCredentialsService,
|
|
162
177
|
isCredentialsEncryptionUnavailableError
|
|
163
178
|
};
|