@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4397.1.9a65481757
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
- package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
- package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
- package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
- package/dist/generated/entities/channel_thread_token/index.js +17 -0
- package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
- package/dist/generated/entities/communication_channel/index.js +43 -0
- package/dist/generated/entities/communication_channel/index.js.map +7 -0
- package/dist/generated/entities/customer_interaction/index.js +4 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entities/external_conversation/index.js +25 -0
- package/dist/generated/entities/external_conversation/index.js.map +7 -0
- package/dist/generated/entities/external_message/index.js +25 -0
- package/dist/generated/entities/external_message/index.js.map +7 -0
- package/dist/generated/entities/integration_credentials/index.js +3 -1
- package/dist/generated/entities/integration_credentials/index.js.map +2 -2
- package/dist/generated/entities/message/index.js +2 -0
- package/dist/generated/entities/message/index.js.map +2 -2
- package/dist/generated/entities/message_channel_link/index.js +33 -0
- package/dist/generated/entities/message_channel_link/index.js.map +7 -0
- package/dist/generated/entities/message_reaction/index.js +25 -0
- package/dist/generated/entities/message_reaction/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +11 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +117 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/authFixtures.js +2 -1
- package/dist/helpers/integration/authFixtures.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
- package/dist/modules/communication_channels/acl.js +47 -0
- package/dist/modules/communication_channels/acl.js.map +7 -0
- package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
- package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
- package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
- package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
- package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
- package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
- package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
- package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
- package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
- package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
- package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
- package/dist/modules/communication_channels/commands/interceptors.js +68 -0
- package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
- package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
- package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-register.js +146 -0
- package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-renew.js +23 -0
- package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
- package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
- package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
- package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
- package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
- package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
- package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
- package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
- package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
- package/dist/modules/communication_channels/data/enrichers.js +286 -0
- package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
- package/dist/modules/communication_channels/data/entities.js +447 -0
- package/dist/modules/communication_channels/data/entities.js.map +7 -0
- package/dist/modules/communication_channels/data/extensions.js +67 -0
- package/dist/modules/communication_channels/data/extensions.js.map +7 -0
- package/dist/modules/communication_channels/data/validators.js +123 -0
- package/dist/modules/communication_channels/data/validators.js.map +7 -0
- package/dist/modules/communication_channels/di.js +35 -0
- package/dist/modules/communication_channels/di.js.map +7 -0
- package/dist/modules/communication_channels/encryption.js +12 -0
- package/dist/modules/communication_channels/encryption.js.map +7 -0
- package/dist/modules/communication_channels/events.js +124 -0
- package/dist/modules/communication_channels/events.js.map +7 -0
- package/dist/modules/communication_channels/index.js +20 -0
- package/dist/modules/communication_channels/index.js.map +7 -0
- package/dist/modules/communication_channels/lib/access-control.js +43 -0
- package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
- package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
- package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter.js +1 -0
- package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
- package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
- package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
- package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
- package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
- package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
- package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-contact.js +14 -0
- package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-mime.js +269 -0
- package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
- package/dist/modules/communication_channels/lib/error-classification.js +101 -0
- package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
- package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
- package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
- package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
- package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
- package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
- package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
- package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
- package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
- package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
- package/dist/modules/communication_channels/lib/provider-health.js +24 -0
- package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-state.js +19 -0
- package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
- package/dist/modules/communication_channels/lib/queue.js +54 -0
- package/dist/modules/communication_channels/lib/queue.js.map +7 -0
- package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
- package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
- package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
- package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
- package/dist/modules/communication_channels/lib/registry.js +67 -0
- package/dist/modules/communication_channels/lib/registry.js.map +7 -0
- package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
- package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
- package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
- package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
- package/dist/modules/communication_channels/lib/system-user.js +22 -0
- package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
- package/dist/modules/communication_channels/lib/test-seed.js +68 -0
- package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
- package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
- package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
- package/dist/modules/communication_channels/lib/thread-token.js +219 -0
- package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
- package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
- package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
- package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
- package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/notifications.client.js +51 -0
- package/dist/modules/communication_channels/notifications.client.js.map +7 -0
- package/dist/modules/communication_channels/notifications.handlers.js +53 -0
- package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
- package/dist/modules/communication_channels/notifications.js +56 -0
- package/dist/modules/communication_channels/notifications.js.map +7 -0
- package/dist/modules/communication_channels/setup.js +105 -0
- package/dist/modules/communication_channels/setup.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
- package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
- package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
- package/dist/modules/communication_channels/widgets/components.js +7 -0
- package/dist/modules/communication_channels/widgets/components.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
- package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
- package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
- package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
- package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
- package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
- package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
- package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
- package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
- package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
- package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
- package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
- package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
- package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
- package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
- package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
- package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
- package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
- package/dist/modules/customers/acl.js +18 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +9 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +18 -7
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
- package/dist/modules/customers/api/interactions/counts/route.js +6 -0
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +26 -7
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
- package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
- package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/route.js +12 -4
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +46 -5
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +16 -0
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
- package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
- package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
- package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
- package/dist/modules/customers/components/detail/DealForm.js +2 -1
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +10 -0
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
- package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
- package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
- package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
- package/dist/modules/customers/data/enrichers.js +133 -2
- package/dist/modules/customers/data/enrichers.js.map +2 -2
- package/dist/modules/customers/data/entities.js +18 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/extensions.js +16 -0
- package/dist/modules/customers/data/extensions.js.map +7 -0
- package/dist/modules/customers/encryption.js +11 -0
- package/dist/modules/customers/encryption.js.map +2 -2
- package/dist/modules/customers/events.js +4 -1
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
- package/dist/modules/customers/lib/kysely.js.map +2 -2
- package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
- package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
- package/dist/modules/customers/lib/personEmailThreads.js +205 -0
- package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
- package/dist/modules/customers/lib/visibilityFilter.js +51 -0
- package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
- package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
- package/dist/modules/customers/setup.js +2 -1
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
- package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
- package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
- package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
- package/dist/modules/integrations/data/entities.js +8 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +29 -14
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
- package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
- package/dist/modules/messages/commands/messages.js +70 -8
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
- package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
- package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
- package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/data/entities.js +8 -1
- package/dist/modules/messages/data/entities.js.map +2 -2
- package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
- package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
- package/dist/modules/messages/widgets/injection-table.js +7 -0
- package/dist/modules/messages/widgets/injection-table.js.map +7 -0
- package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
- package/generated/entities/channel_thread_mapping/index.ts +11 -0
- package/generated/entities/channel_thread_token/index.ts +7 -0
- package/generated/entities/communication_channel/index.ts +20 -0
- package/generated/entities/customer_interaction/index.ts +2 -0
- package/generated/entities/external_conversation/index.ts +11 -0
- package/generated/entities/external_message/index.ts +11 -0
- package/generated/entities/integration_credentials/index.ts +1 -0
- package/generated/entities/message/index.ts +1 -0
- package/generated/entities/message_channel_link/index.ts +15 -0
- package/generated/entities/message_reaction/index.ts +11 -0
- package/generated/entities.ids.generated.ts +11 -0
- package/generated/entity-fields-registry.ts +117 -0
- package/package.json +9 -7
- package/src/helpers/integration/authFixtures.ts +4 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
- package/src/modules/communication_channels/acl.ts +43 -0
- package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
- package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
- package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
- package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
- package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
- package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
- package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
- package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
- package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
- package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
- package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
- package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
- package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
- package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
- package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
- package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
- package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
- package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
- package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
- package/src/modules/communication_channels/commands/interceptors.ts +104 -0
- package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
- package/src/modules/communication_channels/commands/push-register.ts +203 -0
- package/src/modules/communication_channels/commands/push-renew.ts +49 -0
- package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
- package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
- package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
- package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
- package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
- package/src/modules/communication_channels/data/enrichers.ts +413 -0
- package/src/modules/communication_channels/data/entities.ts +546 -0
- package/src/modules/communication_channels/data/extensions.ts +76 -0
- package/src/modules/communication_channels/data/validators.ts +138 -0
- package/src/modules/communication_channels/di.ts +40 -0
- package/src/modules/communication_channels/encryption.ts +44 -0
- package/src/modules/communication_channels/events.ts +122 -0
- package/src/modules/communication_channels/i18n/de.json +138 -0
- package/src/modules/communication_channels/i18n/en.json +138 -0
- package/src/modules/communication_channels/i18n/es.json +138 -0
- package/src/modules/communication_channels/i18n/pl.json +138 -0
- package/src/modules/communication_channels/index.ts +19 -0
- package/src/modules/communication_channels/lib/access-control.ts +110 -0
- package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
- package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
- package/src/modules/communication_channels/lib/adapter.ts +605 -0
- package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
- package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
- package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
- package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
- package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
- package/src/modules/communication_channels/lib/email-contact.ts +17 -0
- package/src/modules/communication_channels/lib/email-mime.ts +442 -0
- package/src/modules/communication_channels/lib/error-classification.ts +144 -0
- package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
- package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
- package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
- package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
- package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
- package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
- package/src/modules/communication_channels/lib/provider-health.ts +47 -0
- package/src/modules/communication_channels/lib/push-state.ts +38 -0
- package/src/modules/communication_channels/lib/queue.ts +66 -0
- package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
- package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
- package/src/modules/communication_channels/lib/registry.ts +99 -0
- package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
- package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
- package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
- package/src/modules/communication_channels/lib/system-user.ts +74 -0
- package/src/modules/communication_channels/lib/test-seed.ts +140 -0
- package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
- package/src/modules/communication_channels/lib/thread-token.ts +355 -0
- package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
- package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
- package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
- package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
- package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
- package/src/modules/communication_channels/notifications.client.ts +50 -0
- package/src/modules/communication_channels/notifications.handlers.ts +86 -0
- package/src/modules/communication_channels/notifications.ts +52 -0
- package/src/modules/communication_channels/setup.ts +158 -0
- package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
- package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
- package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
- package/src/modules/communication_channels/widgets/components.ts +36 -0
- package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
- package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
- package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
- package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
- package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
- package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
- package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
- package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
- package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
- package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
- package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
- package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
- package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
- package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
- package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
- package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
- package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
- package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
- package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
- package/src/modules/customers/acl.ts +18 -0
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/companies/[id]/route.ts +21 -1
- package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
- package/src/modules/customers/api/interactions/counts/route.ts +10 -0
- package/src/modules/customers/api/interactions/route.ts +51 -5
- package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
- package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
- package/src/modules/customers/api/people/[id]/route.ts +17 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
- package/src/modules/customers/commands/deals.ts +65 -6
- package/src/modules/customers/commands/interactions.ts +30 -0
- package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
- package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
- package/src/modules/customers/components/detail/DealForm.tsx +2 -1
- package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
- package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
- package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
- package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
- package/src/modules/customers/data/enrichers.ts +252 -1
- package/src/modules/customers/data/entities.ts +46 -1
- package/src/modules/customers/data/extensions.ts +26 -0
- package/src/modules/customers/encryption.ts +11 -0
- package/src/modules/customers/events.ts +4 -0
- package/src/modules/customers/i18n/de.json +41 -0
- package/src/modules/customers/i18n/en.json +41 -0
- package/src/modules/customers/i18n/es.json +41 -0
- package/src/modules/customers/i18n/pl.json +41 -0
- package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
- package/src/modules/customers/lib/kysely.ts +16 -0
- package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
- package/src/modules/customers/lib/personEmailThreads.ts +325 -0
- package/src/modules/customers/lib/visibilityFilter.ts +152 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
- package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
- package/src/modules/customers/setup.ts +1 -0
- package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
- package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
- package/src/modules/integrations/AGENTS.md +9 -0
- package/src/modules/integrations/data/entities.ts +21 -1
- package/src/modules/integrations/lib/credentials-service.ts +49 -13
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
- package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
- package/src/modules/messages/commands/messages.ts +101 -8
- package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
- package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
- package/src/modules/messages/data/entities.ts +11 -0
- package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
- package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
- package/src/modules/messages/widgets/injection-table.ts +29 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect } from "@playwright/test";
|
|
2
|
-
import { apiRequest } from "./api.js";
|
|
2
|
+
import { apiRequest, getAuthToken } from "./api.js";
|
|
3
3
|
import { expectId, readJsonSafe } from "./generalFixtures.js";
|
|
4
4
|
const BASE_URL = process.env.BASE_URL?.trim() || null;
|
|
5
5
|
function resolveUrl(path) {
|
|
@@ -105,6 +105,7 @@ export {
|
|
|
105
105
|
deleteOrganizationIfExists,
|
|
106
106
|
deleteRoleIfExists,
|
|
107
107
|
deleteUserIfExists,
|
|
108
|
+
getAuthToken,
|
|
108
109
|
setRoleAclFeatures,
|
|
109
110
|
setUserAclVisibility
|
|
110
111
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/helpers/integration/authFixtures.ts"],
|
|
4
|
-
"sourcesContent": ["import { expect, type APIRequestContext } from '@playwright/test';\nimport { apiRequest } from './api';\nimport { expectId, readJsonSafe } from './generalFixtures';\n\nconst BASE_URL = process.env.BASE_URL?.trim() || null;\n\nfunction resolveUrl(path: string): string {\n return BASE_URL ? `${BASE_URL}${path}` : path;\n}\n\n/**\n * Variant of {@link apiRequest} that sets the `om_selected_org` cookie so the\n * server resolves `ctx.selectedOrganizationId` to a specific organization.\n * Create routes scope new records to that organization, which lets a test place\n * a fixture in an organization other than the caller's home org.\n */\nexport async function apiRequestWithSelectedOrg(\n request: APIRequestContext,\n method: string,\n path: string,\n options: { token: string; selectedOrgId: string; data?: unknown },\n) {\n const headers = {\n Authorization: `Bearer ${options.token}`,\n 'Content-Type': 'application/json',\n Cookie: `om_selected_org=${options.selectedOrgId}`,\n };\n return request.fetch(resolveUrl(path), { method, headers, data: options.data });\n}\n\nexport async function createRoleFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = {\n name: input.name,\n };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/auth/roles', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/roles should return 201').toBe(201);\n return expectId(body?.id, 'Role creation response should include id');\n}\n\nexport async function deleteRoleIfExists(\n request: APIRequestContext,\n token: string | null,\n roleId: string | null,\n): Promise<void> {\n if (!token || !roleId) return;\n await apiRequest(request, 'DELETE', `/api/auth/roles?id=${encodeURIComponent(roleId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createUserFixture(\n request: APIRequestContext,\n token: string,\n input: { email: string; password: string; organizationId: string; roles: string[]; name?: string },\n): Promise<string> {\n const response = await apiRequest(request, 'POST', '/api/auth/users', {\n token,\n data: input,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/users should return 201').toBe(201);\n return expectId(body?.id, 'User creation response should include id');\n}\n\nexport async function deleteUserIfExists(\n request: APIRequestContext,\n token: string | null,\n userId: string | null,\n): Promise<void> {\n if (!token || !userId) return;\n await apiRequest(request, 'DELETE', `/api/auth/users?id=${encodeURIComponent(userId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createOrganizationFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = { name: input.name };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/directory/organizations', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/directory/organizations should return 201').toBe(201);\n return expectId(body?.id, 'Organization creation response should include id');\n}\n\nexport async function deleteOrganizationIfExists(\n request: APIRequestContext,\n token: string | null,\n organizationId: string | null,\n): Promise<void> {\n if (!token || !organizationId) return;\n await apiRequest(request, 'DELETE', '/api/directory/organizations', {\n token,\n data: { id: organizationId },\n }).catch(() => undefined);\n}\n\nexport async function setRoleAclFeatures(\n request: APIRequestContext,\n token: string,\n input: { roleId: string; features: string[]; organizations?: string[] | null },\n): Promise<void> {\n const payload: { roleId: string; features: string[]; organizations?: string[] | null } = {\n roleId: input.roleId,\n features: input.features,\n };\n if (input.organizations !== undefined) {\n payload.organizations = input.organizations;\n }\n const response = await apiRequest(request, 'PUT', '/api/auth/roles/acl', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ ok?: boolean }>(response);\n expect(response.status(), 'PUT /api/auth/roles/acl should return 200').toBe(200);\n expect(body?.ok, 'Role ACL update should report ok=true').toBe(true);\n}\n\nexport async function setUserAclVisibility(\n request: APIRequestContext,\n token: string,\n input: { userId: string; organizations: string[] | null; features?: string[] },\n): Promise<void> {\n const payload: { userId: string; organizations: string[] | null; features?: string[] } = {\n userId: input.userId,\n organizations: input.organizations,\n };\n if (input.features !== undefined) {\n payload.features = input.features;\n }\n const response = await apiRequest(request, 'PUT', '/api/auth/users/acl', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ ok?: boolean }>(response);\n expect(response.status(), 'PUT /api/auth/users/acl should return 200').toBe(200);\n expect(body?.ok, 'User ACL update should report ok=true').toBe(true);\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAsC;AAC/C,SAAS,
|
|
4
|
+
"sourcesContent": ["import { expect, type APIRequestContext } from '@playwright/test';\nimport { apiRequest, getAuthToken } from './api';\nimport { expectId, readJsonSafe } from './generalFixtures';\n\n// Re-exported so tests can import auth helpers from a single fixtures module.\nexport { getAuthToken };\n\nconst BASE_URL = process.env.BASE_URL?.trim() || null;\n\nfunction resolveUrl(path: string): string {\n return BASE_URL ? `${BASE_URL}${path}` : path;\n}\n\n/**\n * Variant of {@link apiRequest} that sets the `om_selected_org` cookie so the\n * server resolves `ctx.selectedOrganizationId` to a specific organization.\n * Create routes scope new records to that organization, which lets a test place\n * a fixture in an organization other than the caller's home org.\n */\nexport async function apiRequestWithSelectedOrg(\n request: APIRequestContext,\n method: string,\n path: string,\n options: { token: string; selectedOrgId: string; data?: unknown },\n) {\n const headers = {\n Authorization: `Bearer ${options.token}`,\n 'Content-Type': 'application/json',\n Cookie: `om_selected_org=${options.selectedOrgId}`,\n };\n return request.fetch(resolveUrl(path), { method, headers, data: options.data });\n}\n\nexport async function createRoleFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = {\n name: input.name,\n };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/auth/roles', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/roles should return 201').toBe(201);\n return expectId(body?.id, 'Role creation response should include id');\n}\n\nexport async function deleteRoleIfExists(\n request: APIRequestContext,\n token: string | null,\n roleId: string | null,\n): Promise<void> {\n if (!token || !roleId) return;\n await apiRequest(request, 'DELETE', `/api/auth/roles?id=${encodeURIComponent(roleId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createUserFixture(\n request: APIRequestContext,\n token: string,\n input: { email: string; password: string; organizationId: string; roles: string[]; name?: string },\n): Promise<string> {\n const response = await apiRequest(request, 'POST', '/api/auth/users', {\n token,\n data: input,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/users should return 201').toBe(201);\n return expectId(body?.id, 'User creation response should include id');\n}\n\nexport async function deleteUserIfExists(\n request: APIRequestContext,\n token: string | null,\n userId: string | null,\n): Promise<void> {\n if (!token || !userId) return;\n await apiRequest(request, 'DELETE', `/api/auth/users?id=${encodeURIComponent(userId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createOrganizationFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = { name: input.name };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/directory/organizations', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/directory/organizations should return 201').toBe(201);\n return expectId(body?.id, 'Organization creation response should include id');\n}\n\nexport async function deleteOrganizationIfExists(\n request: APIRequestContext,\n token: string | null,\n organizationId: string | null,\n): Promise<void> {\n if (!token || !organizationId) return;\n await apiRequest(request, 'DELETE', '/api/directory/organizations', {\n token,\n data: { id: organizationId },\n }).catch(() => undefined);\n}\n\nexport async function setRoleAclFeatures(\n request: APIRequestContext,\n token: string,\n input: { roleId: string; features: string[]; organizations?: string[] | null },\n): Promise<void> {\n const payload: { roleId: string; features: string[]; organizations?: string[] | null } = {\n roleId: input.roleId,\n features: input.features,\n };\n if (input.organizations !== undefined) {\n payload.organizations = input.organizations;\n }\n const response = await apiRequest(request, 'PUT', '/api/auth/roles/acl', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ ok?: boolean }>(response);\n expect(response.status(), 'PUT /api/auth/roles/acl should return 200').toBe(200);\n expect(body?.ok, 'Role ACL update should report ok=true').toBe(true);\n}\n\nexport async function setUserAclVisibility(\n request: APIRequestContext,\n token: string,\n input: { userId: string; organizations: string[] | null; features?: string[] },\n): Promise<void> {\n const payload: { userId: string; organizations: string[] | null; features?: string[] } = {\n userId: input.userId,\n organizations: input.organizations,\n };\n if (input.features !== undefined) {\n payload.features = input.features;\n }\n const response = await apiRequest(request, 'PUT', '/api/auth/users/acl', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ ok?: boolean }>(response);\n expect(response.status(), 'PUT /api/auth/users/acl should return 200').toBe(200);\n expect(body?.ok, 'User ACL update should report ok=true').toBe(true);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { expect } from "@playwright/test";
|
|
2
|
+
import { apiRequest } from "./api.js";
|
|
3
|
+
import { expectId, readJsonSafe } from "./generalFixtures.js";
|
|
4
|
+
const TEST_SEED_PATH = "/api/communication_channels/test-seed";
|
|
5
|
+
async function isChannelSeedingAvailable(request, token) {
|
|
6
|
+
const response = await apiRequest(request, "POST", TEST_SEED_PATH, {
|
|
7
|
+
token,
|
|
8
|
+
data: { action: "__probe__" }
|
|
9
|
+
});
|
|
10
|
+
return response.status() !== 404;
|
|
11
|
+
}
|
|
12
|
+
async function seedConnectedChannel(request, token, input = {}) {
|
|
13
|
+
const response = await apiRequest(request, "POST", TEST_SEED_PATH, {
|
|
14
|
+
token,
|
|
15
|
+
data: { action: "connect-channel", ...input }
|
|
16
|
+
});
|
|
17
|
+
expect(
|
|
18
|
+
response.status(),
|
|
19
|
+
"POST /api/communication_channels/test-seed (connect-channel) should return 201"
|
|
20
|
+
).toBe(201);
|
|
21
|
+
const body = await readJsonSafe(response);
|
|
22
|
+
return expectId(body?.channelId, "connect-channel response should include channelId");
|
|
23
|
+
}
|
|
24
|
+
async function seedInboundMessage(request, token, input) {
|
|
25
|
+
const response = await apiRequest(request, "POST", TEST_SEED_PATH, {
|
|
26
|
+
token,
|
|
27
|
+
data: { action: "emit-inbound", ...input }
|
|
28
|
+
});
|
|
29
|
+
expect(
|
|
30
|
+
response.status(),
|
|
31
|
+
"POST /api/communication_channels/test-seed (emit-inbound) should return 201"
|
|
32
|
+
).toBe(201);
|
|
33
|
+
const body = await readJsonSafe(response);
|
|
34
|
+
return {
|
|
35
|
+
channelLinkId: expectId(body?.channelLinkId, "emit-inbound response should include channelLinkId"),
|
|
36
|
+
messageId: expectId(body?.messageId, "emit-inbound response should include messageId"),
|
|
37
|
+
conversationId: expectId(
|
|
38
|
+
body?.conversationId,
|
|
39
|
+
"emit-inbound response should include conversationId"
|
|
40
|
+
)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function deleteChannelIfExists(request, token, channelId) {
|
|
44
|
+
if (!token || !channelId) return;
|
|
45
|
+
await apiRequest(
|
|
46
|
+
request,
|
|
47
|
+
"DELETE",
|
|
48
|
+
`/api/communication_channels/channels/${encodeURIComponent(channelId)}`,
|
|
49
|
+
{ token }
|
|
50
|
+
).catch(() => void 0);
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
deleteChannelIfExists,
|
|
54
|
+
isChannelSeedingAvailable,
|
|
55
|
+
seedConnectedChannel,
|
|
56
|
+
seedInboundMessage
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=communicationChannelsFixtures.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/helpers/integration/communicationChannelsFixtures.ts"],
|
|
4
|
+
"sourcesContent": ["import { expect, type APIRequestContext } from '@playwright/test';\nimport { apiRequest } from './api';\nimport { expectId, readJsonSafe } from './generalFixtures';\n\n/**\n * Communication-channels integration fixtures.\n *\n * These drive the TEST-ONLY seed endpoint `POST /api/communication_channels/test-seed`,\n * which is gated by `OM_ENABLE_TEST_CHANNEL_SEEDING` (inert/404 in production). Use\n * {@link isChannelSeedingAvailable} to skip tests when the gate is off rather than\n * failing them.\n */\n\nexport type SeedAddressField =\n | string\n | { address: string; name?: string }\n | Array<string | { address: string; name?: string }>;\n\nconst TEST_SEED_PATH = '/api/communication_channels/test-seed';\n\n/**\n * Probe whether the env-gated test-seed endpoint is enabled in the target app.\n * Returns false when the route answers 404 (flag off) so callers can `test.skip`.\n * The caller's token must hold `communication_channels.connect_user_channel`.\n */\nexport async function isChannelSeedingAvailable(\n request: APIRequestContext,\n token: string,\n): Promise<boolean> {\n // A malformed body returns 422 when the gate is ON and 404 when it is OFF.\n const response = await apiRequest(request, 'POST', TEST_SEED_PATH, {\n token,\n data: { action: '__probe__' },\n });\n return response.status() !== 404;\n}\n\n/**\n * Seed a connected, network-free `__test_seed__` channel owned by the caller.\n * Returns the new channel id. Tear down with {@link deleteChannelIfExists}.\n */\nexport async function seedConnectedChannel(\n request: APIRequestContext,\n token: string,\n input: { displayName?: string; externalIdentifier?: string } = {},\n): Promise<string> {\n const response = await apiRequest(request, 'POST', TEST_SEED_PATH, {\n token,\n data: { action: 'connect-channel', ...input },\n });\n expect(\n response.status(),\n 'POST /api/communication_channels/test-seed (connect-channel) should return 201',\n ).toBe(201);\n const body = await readJsonSafe<{ channelId?: string }>(response);\n return expectId(body?.channelId, 'connect-channel response should include channelId');\n}\n\n/**\n * Seed an inbound `MessageChannelLink` for `channelId` and emit\n * `communication_channels.message.received` through the real event bus. The\n * persistent customers link-channel-message-received subscriber is enqueued to\n * the `events` queue \u2014 drain it with `drainIntegrationQueue('events')`.\n *\n * Returns the created link + message ids (the message id is the platform\n * `messages.message` id, usable as `messageThreadId` to thread a follow-up).\n */\nexport async function seedInboundMessage(\n request: APIRequestContext,\n token: string,\n input: {\n channelId: string;\n from?: SeedAddressField;\n to?: SeedAddressField;\n cc?: SeedAddressField;\n subject?: string;\n bodyText?: string;\n messageId?: string;\n inReplyTo?: string;\n references?: string[];\n messageThreadId?: string;\n providerKey?: string;\n },\n): Promise<{ channelLinkId: string; messageId: string; conversationId: string }> {\n const response = await apiRequest(request, 'POST', TEST_SEED_PATH, {\n token,\n data: { action: 'emit-inbound', ...input },\n });\n expect(\n response.status(),\n 'POST /api/communication_channels/test-seed (emit-inbound) should return 201',\n ).toBe(201);\n const body = await readJsonSafe<{\n channelLinkId?: string;\n messageId?: string;\n conversationId?: string;\n }>(response);\n return {\n channelLinkId: expectId(body?.channelLinkId, 'emit-inbound response should include channelLinkId'),\n messageId: expectId(body?.messageId, 'emit-inbound response should include messageId'),\n conversationId: expectId(\n body?.conversationId,\n 'emit-inbound response should include conversationId',\n ),\n };\n}\n\n/**\n * Best-effort delete of a seeded channel via the owner-scoped DELETE route.\n * Safe to call with a null id in `finally`.\n */\nexport async function deleteChannelIfExists(\n request: APIRequestContext,\n token: string | null,\n channelId: string | null,\n): Promise<void> {\n if (!token || !channelId) return;\n await apiRequest(\n request,\n 'DELETE',\n `/api/communication_channels/channels/${encodeURIComponent(channelId)}`,\n { token },\n ).catch(() => undefined);\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAsC;AAC/C,SAAS,kBAAkB;AAC3B,SAAS,UAAU,oBAAoB;AAgBvC,MAAM,iBAAiB;AAOvB,eAAsB,0BACpB,SACA,OACkB;AAElB,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,gBAAgB;AAAA,IACjE;AAAA,IACA,MAAM,EAAE,QAAQ,YAAY;AAAA,EAC9B,CAAC;AACD,SAAO,SAAS,OAAO,MAAM;AAC/B;AAMA,eAAsB,qBACpB,SACA,OACA,QAA+D,CAAC,GAC/C;AACjB,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,gBAAgB;AAAA,IACjE;AAAA,IACA,MAAM,EAAE,QAAQ,mBAAmB,GAAG,MAAM;AAAA,EAC9C,CAAC;AACD;AAAA,IACE,SAAS,OAAO;AAAA,IAChB;AAAA,EACF,EAAE,KAAK,GAAG;AACV,QAAM,OAAO,MAAM,aAAqC,QAAQ;AAChE,SAAO,SAAS,MAAM,WAAW,mDAAmD;AACtF;AAWA,eAAsB,mBACpB,SACA,OACA,OAa+E;AAC/E,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,gBAAgB;AAAA,IACjE;AAAA,IACA,MAAM,EAAE,QAAQ,gBAAgB,GAAG,MAAM;AAAA,EAC3C,CAAC;AACD;AAAA,IACE,SAAS,OAAO;AAAA,IAChB;AAAA,EACF,EAAE,KAAK,GAAG;AACV,QAAM,OAAO,MAAM,aAIhB,QAAQ;AACX,SAAO;AAAA,IACL,eAAe,SAAS,MAAM,eAAe,oDAAoD;AAAA,IACjG,WAAW,SAAS,MAAM,WAAW,gDAAgD;AAAA,IACrF,gBAAgB;AAAA,MACd,MAAM;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;AAMA,eAAsB,sBACpB,SACA,OACA,WACe;AACf,MAAI,CAAC,SAAS,CAAC,UAAW;AAC1B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,wCAAwC,mBAAmB,SAAS,CAAC;AAAA,IACrE,EAAE,MAAM;AAAA,EACV,EAAE,MAAM,MAAM,MAAS;AACzB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const features = [
|
|
2
|
+
{ id: "communication_channels.view", title: "View communication channels", module: "communication_channels" },
|
|
3
|
+
{ id: "communication_channels.manage", title: "Manage communication channels", module: "communication_channels" },
|
|
4
|
+
{ id: "communication_channels.react", title: "React to channel messages", module: "communication_channels" },
|
|
5
|
+
{ id: "communication_channels.assign", title: "Assign channel conversations", module: "communication_channels" },
|
|
6
|
+
/**
|
|
7
|
+
* Per-user channel ownership (added by the email integration spec).
|
|
8
|
+
*
|
|
9
|
+
* Gates the "Connect my mailbox" flow on the per-user profile page. Split from
|
|
10
|
+
* `communication_channels.manage` so policy can disable new linking while
|
|
11
|
+
* preserving existing accounts (e.g. during a security incident response).
|
|
12
|
+
* Default-granted to all roles in `setup.ts`.
|
|
13
|
+
*/
|
|
14
|
+
{ id: "communication_channels.connect_user_channel", title: "Connect own communication channel", module: "communication_channels" },
|
|
15
|
+
/**
|
|
16
|
+
* Reserved for a future v2 team-oversight capability. NOT consulted in v1:
|
|
17
|
+
* personal mailboxes (`CommunicationChannel.user_id` set) follow the strict
|
|
18
|
+
* owner-only privacy model, so this feature grants NO cross-user channel view.
|
|
19
|
+
* The admin channels list (`GET /api/communication_channels/channels`) returns
|
|
20
|
+
* `user_id IS NULL` rows only; personal mailboxes surface exclusively on the
|
|
21
|
+
* owner's profile page and are never exposed to admins/superadmins in v1.
|
|
22
|
+
* Granted to `superadmin` + `admin` only so the inert grant is in place ahead
|
|
23
|
+
* of the audited v2 oversight feature that will re-activate it.
|
|
24
|
+
*/
|
|
25
|
+
{ id: "communication_channels.admin", title: "Administer all communication channels (tenant-wide)", module: "communication_channels" },
|
|
26
|
+
/**
|
|
27
|
+
* Trigger the "Import history" job for a channel — fetch older messages
|
|
28
|
+
* the channel never saw at bootstrap (Spec B § Phase B6). Separate from
|
|
29
|
+
* `manage` so policy can gate bulk historical imports during quiet hours
|
|
30
|
+
* or cost-controlled rollouts while leaving normal channel CRUD open.
|
|
31
|
+
*/
|
|
32
|
+
{ id: "communication_channels.channel.import_history", title: "Import channel history", module: "communication_channels" },
|
|
33
|
+
/**
|
|
34
|
+
* Manage provider push delivery (Spec C — Gmail Pub/Sub push
|
|
35
|
+
* subscriptions). Gates the "Re-register push" operator button and any
|
|
36
|
+
* future push-status manipulation. Granted to admin + superadmin only —
|
|
37
|
+
* regular users don't need to think about whether mail arrives via
|
|
38
|
+
* push or polling, the system handles it.
|
|
39
|
+
*/
|
|
40
|
+
{ id: "communication_channels.channel.push.manage", title: "Manage push delivery", module: "communication_channels" }
|
|
41
|
+
];
|
|
42
|
+
var acl_default = features;
|
|
43
|
+
export {
|
|
44
|
+
acl_default as default,
|
|
45
|
+
features
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=acl.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/communication_channels/acl.ts"],
|
|
4
|
+
"sourcesContent": ["export const features = [\n { id: 'communication_channels.view', title: 'View communication channels', module: 'communication_channels' },\n { id: 'communication_channels.manage', title: 'Manage communication channels', module: 'communication_channels' },\n { id: 'communication_channels.react', title: 'React to channel messages', module: 'communication_channels' },\n { id: 'communication_channels.assign', title: 'Assign channel conversations', module: 'communication_channels' },\n /**\n * Per-user channel ownership (added by the email integration spec).\n *\n * Gates the \"Connect my mailbox\" flow on the per-user profile page. Split from\n * `communication_channels.manage` so policy can disable new linking while\n * preserving existing accounts (e.g. during a security incident response).\n * Default-granted to all roles in `setup.ts`.\n */\n { id: 'communication_channels.connect_user_channel', title: 'Connect own communication channel', module: 'communication_channels' },\n /**\n * Reserved for a future v2 team-oversight capability. NOT consulted in v1:\n * personal mailboxes (`CommunicationChannel.user_id` set) follow the strict\n * owner-only privacy model, so this feature grants NO cross-user channel view.\n * The admin channels list (`GET /api/communication_channels/channels`) returns\n * `user_id IS NULL` rows only; personal mailboxes surface exclusively on the\n * owner's profile page and are never exposed to admins/superadmins in v1.\n * Granted to `superadmin` + `admin` only so the inert grant is in place ahead\n * of the audited v2 oversight feature that will re-activate it.\n */\n { id: 'communication_channels.admin', title: 'Administer all communication channels (tenant-wide)', module: 'communication_channels' },\n /**\n * Trigger the \"Import history\" job for a channel \u2014 fetch older messages\n * the channel never saw at bootstrap (Spec B \u00A7 Phase B6). Separate from\n * `manage` so policy can gate bulk historical imports during quiet hours\n * or cost-controlled rollouts while leaving normal channel CRUD open.\n */\n { id: 'communication_channels.channel.import_history', title: 'Import channel history', module: 'communication_channels' },\n /**\n * Manage provider push delivery (Spec C \u2014 Gmail Pub/Sub push\n * subscriptions). Gates the \"Re-register push\" operator button and any\n * future push-status manipulation. Granted to admin + superadmin only \u2014\n * regular users don't need to think about whether mail arrives via\n * push or polling, the system handles it.\n */\n { id: 'communication_channels.channel.push.manage', title: 'Manage push delivery', module: 'communication_channels' },\n] as const\n\nexport default features\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,+BAA+B,OAAO,+BAA+B,QAAQ,yBAAyB;AAAA,EAC5G,EAAE,IAAI,iCAAiC,OAAO,iCAAiC,QAAQ,yBAAyB;AAAA,EAChH,EAAE,IAAI,gCAAgC,OAAO,6BAA6B,QAAQ,yBAAyB;AAAA,EAC3G,EAAE,IAAI,iCAAiC,OAAO,gCAAgC,QAAQ,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS/G,EAAE,IAAI,+CAA+C,OAAO,qCAAqC,QAAQ,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWlI,EAAE,IAAI,gCAAgC,OAAO,uDAAuD,QAAQ,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrI,EAAE,IAAI,iDAAiD,OAAO,0BAA0B,QAAQ,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQzH,EAAE,IAAI,8CAA8C,OAAO,wBAAwB,QAAQ,yBAAyB;AACtH;AAEA,IAAO,cAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
|
+
import { CommunicationChannel } from "../../../../data/entities.js";
|
|
7
|
+
import { ChannelAccessDeniedError, assertCanManageChannel } from "../../../../lib/access-control.js";
|
|
8
|
+
import {
|
|
9
|
+
COMMUNICATION_CHANNELS_DELETE_CHANNEL_COMMAND_ID
|
|
10
|
+
} from "../../../../commands/delete-channel.js";
|
|
11
|
+
import { validateRouteMutationGuard } from "../../../../lib/route-mutation-guard.js";
|
|
12
|
+
const metadata = {
|
|
13
|
+
path: "/communication_channels/channels/[id]",
|
|
14
|
+
DELETE: {
|
|
15
|
+
// Owner self-service: a user may disconnect their OWN personal mailbox
|
|
16
|
+
// (gated by `connect_user_channel`). Deleting a shared/tenant-wide channel
|
|
17
|
+
// still requires `manage` — enforced per channel type by
|
|
18
|
+
// `assertCanManageChannel` in the handler.
|
|
19
|
+
requireAuth: true,
|
|
20
|
+
requireFeatures: ["communication_channels.connect_user_channel"]
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
async function DELETE(req, context) {
|
|
24
|
+
const { id } = await context.params;
|
|
25
|
+
if (!z.string().uuid().safeParse(id).success) {
|
|
26
|
+
return NextResponse.json({ error: "Invalid channel id" }, { status: 400 });
|
|
27
|
+
}
|
|
28
|
+
const auth = await getAuthFromRequest(req);
|
|
29
|
+
if (!auth?.sub || !auth?.tenantId) {
|
|
30
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
31
|
+
}
|
|
32
|
+
const container = await createRequestContainer();
|
|
33
|
+
const em = container.resolve("em").fork();
|
|
34
|
+
const organizationId = auth.orgId ?? null;
|
|
35
|
+
const dscope = { tenantId: auth.tenantId, organizationId };
|
|
36
|
+
const channel = await findOneWithDecryption(
|
|
37
|
+
em,
|
|
38
|
+
CommunicationChannel,
|
|
39
|
+
{
|
|
40
|
+
id,
|
|
41
|
+
tenantId: auth.tenantId,
|
|
42
|
+
organizationId,
|
|
43
|
+
deletedAt: null
|
|
44
|
+
},
|
|
45
|
+
void 0,
|
|
46
|
+
dscope
|
|
47
|
+
);
|
|
48
|
+
if (!channel) {
|
|
49
|
+
return NextResponse.json({ error: "Channel not found" }, { status: 404 });
|
|
50
|
+
}
|
|
51
|
+
let userFeatures = [];
|
|
52
|
+
try {
|
|
53
|
+
const rbac = container.resolve("rbacService");
|
|
54
|
+
const acl = await rbac.loadAcl(auth.sub, {
|
|
55
|
+
tenantId: auth.tenantId,
|
|
56
|
+
organizationId
|
|
57
|
+
});
|
|
58
|
+
userFeatures = acl?.isSuperAdmin ? ["*"] : Array.isArray(acl?.features) ? acl.features : [];
|
|
59
|
+
} catch {
|
|
60
|
+
userFeatures = [];
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
assertCanManageChannel(
|
|
64
|
+
{ userId: channel.userId },
|
|
65
|
+
auth.sub,
|
|
66
|
+
userFeatures,
|
|
67
|
+
"communication_channels.manage"
|
|
68
|
+
);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (err instanceof ChannelAccessDeniedError) {
|
|
71
|
+
return NextResponse.json({ error: "Channel not found" }, { status: 404 });
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
const guard = await validateRouteMutationGuard({
|
|
76
|
+
container,
|
|
77
|
+
req,
|
|
78
|
+
auth,
|
|
79
|
+
input: {
|
|
80
|
+
resourceKind: "communication_channels.channel",
|
|
81
|
+
resourceId: id,
|
|
82
|
+
operation: "delete"
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
if ("response" in guard) return guard.response;
|
|
86
|
+
const commandBus = container.resolve("commandBus");
|
|
87
|
+
const input = {
|
|
88
|
+
channelId: id,
|
|
89
|
+
userId: auth.sub,
|
|
90
|
+
scope: { tenantId: auth.tenantId, organizationId }
|
|
91
|
+
};
|
|
92
|
+
const { result } = await commandBus.execute(
|
|
93
|
+
COMMUNICATION_CHANNELS_DELETE_CHANNEL_COMMAND_ID,
|
|
94
|
+
{
|
|
95
|
+
input,
|
|
96
|
+
ctx: {
|
|
97
|
+
container,
|
|
98
|
+
auth,
|
|
99
|
+
organizationScope: null,
|
|
100
|
+
selectedOrganizationId: organizationId,
|
|
101
|
+
organizationIds: organizationId ? [organizationId] : null
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
if (result.status !== "deleted") {
|
|
106
|
+
return NextResponse.json({ error: "Channel not found" }, { status: 404 });
|
|
107
|
+
}
|
|
108
|
+
await guard.afterSuccess();
|
|
109
|
+
return new NextResponse(null, { status: 204 });
|
|
110
|
+
}
|
|
111
|
+
const openApi = {
|
|
112
|
+
tags: ["CommunicationChannels"],
|
|
113
|
+
methods: {
|
|
114
|
+
DELETE: {
|
|
115
|
+
summary: "Delete (soft-delete) a communication channel",
|
|
116
|
+
tags: ["CommunicationChannels"],
|
|
117
|
+
responses: [
|
|
118
|
+
{ status: 204, description: "Channel deleted" },
|
|
119
|
+
{ status: 400, description: "Invalid channel id" },
|
|
120
|
+
{ status: 401, description: "Unauthorized" },
|
|
121
|
+
{ status: 404, description: "Channel not found or not owned by current user" }
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var route_default = DELETE;
|
|
127
|
+
export {
|
|
128
|
+
DELETE,
|
|
129
|
+
route_default as default,
|
|
130
|
+
metadata,
|
|
131
|
+
openApi
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/communication_channels/api/delete/channels/%5Bid%5D/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CommunicationChannel } from '../../../../data/entities'\nimport { ChannelAccessDeniedError, assertCanManageChannel } from '../../../../lib/access-control'\nimport {\n COMMUNICATION_CHANNELS_DELETE_CHANNEL_COMMAND_ID,\n type DeleteChannelInput,\n type DeleteChannelResult,\n} from '../../../../commands/delete-channel'\nimport { validateRouteMutationGuard } from '../../../../lib/route-mutation-guard'\n\ntype RbacServiceLike = {\n loadAcl: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<{ isSuperAdmin: boolean; features: string[]; organizations: string[] | null }>\n}\n\n/**\n * Soft-delete (remove) a communication channel.\n *\n * Per-user access guard: only the channel owner \u2014 or an admin holding\n * `communication_channels.admin` \u2014 may delete a channel. Non-owners get a 404\n * (existence masking), consistent with the other channel routes. The declarative\n * `communication_channels.manage` feature gates the route itself; deletion is a\n * management operation alongside disconnect/set-primary.\n */\nexport const metadata = {\n path: '/communication_channels/channels/[id]',\n DELETE: {\n // Owner self-service: a user may disconnect their OWN personal mailbox\n // (gated by `connect_user_channel`). Deleting a shared/tenant-wide channel\n // still requires `manage` \u2014 enforced per channel type by\n // `assertCanManageChannel` in the handler.\n requireAuth: true,\n requireFeatures: ['communication_channels.connect_user_channel'],\n },\n}\n\ntype RouteContext = {\n params: Promise<{ id: string }> | { id: string }\n}\n\nexport async function DELETE(req: Request, context: RouteContext): Promise<Response> {\n const { id } = await context.params\n if (!z.string().uuid().safeParse(id).success) {\n return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager).fork()\n const organizationId = (auth as { orgId?: string | null }).orgId ?? null\n const dscope = { tenantId: auth.tenantId as string, organizationId }\n\n const channel = await findOneWithDecryption(\n em,\n CommunicationChannel,\n {\n id,\n tenantId: auth.tenantId as string,\n organizationId,\n deletedAt: null,\n },\n undefined,\n dscope,\n )\n if (!channel) {\n return NextResponse.json({ error: 'Channel not found' }, { status: 404 })\n }\n\n let userFeatures: string[] = []\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike\n const acl = await rbac.loadAcl(auth.sub as string, {\n tenantId: auth.tenantId as string,\n organizationId,\n })\n userFeatures = acl?.isSuperAdmin ? ['*'] : Array.isArray(acl?.features) ? acl.features : []\n } catch {\n userFeatures = []\n }\n try {\n assertCanManageChannel(\n { userId: (channel as { userId?: string | null }).userId },\n auth.sub as string,\n userFeatures,\n 'communication_channels.manage',\n )\n } catch (err) {\n if (err instanceof ChannelAccessDeniedError) {\n return NextResponse.json({ error: 'Channel not found' }, { status: 404 })\n }\n throw err\n }\n\n const guard = await validateRouteMutationGuard({\n container,\n req,\n auth,\n input: {\n resourceKind: 'communication_channels.channel',\n resourceId: id,\n operation: 'delete',\n },\n })\n if ('response' in guard) return guard.response\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const input: DeleteChannelInput = {\n channelId: id,\n userId: auth.sub as string,\n scope: { tenantId: auth.tenantId as string, organizationId },\n }\n const { result } = await commandBus.execute<DeleteChannelInput, DeleteChannelResult>(\n COMMUNICATION_CHANNELS_DELETE_CHANNEL_COMMAND_ID,\n {\n input,\n ctx: {\n container,\n auth: auth as never,\n organizationScope: null,\n selectedOrganizationId: organizationId,\n organizationIds: organizationId ? [organizationId] : null,\n },\n },\n )\n\n // 'noop' (channel vanished between the load and the command) and 'not_owner'\n // both map to 404 to avoid leaking ownership/existence.\n if (result.status !== 'deleted') {\n return NextResponse.json({ error: 'Channel not found' }, { status: 404 })\n }\n await guard.afterSuccess()\n return new NextResponse(null, { status: 204 })\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n DELETE: {\n summary: 'Delete (soft-delete) a communication channel',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 204, description: 'Channel deleted' },\n { status: 400, description: 'Invalid channel id' },\n { status: 401, description: 'Unauthorized' },\n { status: 404, description: 'Channel not found or not owned by current user' },\n ],\n },\n },\n}\n\nexport default DELETE\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
5
|
+
import {
|
|
6
|
+
COMMUNICATION_CHANNELS_TOGGLE_OUTBOUND_REACTION_COMMAND_ID
|
|
7
|
+
} from "../../../../../../commands/toggle-outbound-reaction.js";
|
|
8
|
+
import { MessageReaction } from "../../../../../../data/entities.js";
|
|
9
|
+
import { validateRouteMutationGuard } from "../../../../../../lib/route-mutation-guard.js";
|
|
10
|
+
const metadata = {
|
|
11
|
+
path: "/communication_channels/messages/[messageId]/reactions/[reactionId]",
|
|
12
|
+
DELETE: {
|
|
13
|
+
requireAuth: true,
|
|
14
|
+
requireFeatures: ["communication_channels.react"]
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
async function DELETE(_req, context) {
|
|
18
|
+
const { messageId, reactionId } = await context.params;
|
|
19
|
+
if (!messageId || !reactionId || !z.string().uuid().safeParse(messageId).success || !z.string().uuid().safeParse(reactionId).success) {
|
|
20
|
+
return NextResponse.json({ error: "Invalid params" }, { status: 400 });
|
|
21
|
+
}
|
|
22
|
+
const auth = context.auth;
|
|
23
|
+
if (!auth?.sub || !auth?.tenantId) {
|
|
24
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
25
|
+
}
|
|
26
|
+
const container = await createRequestContainer();
|
|
27
|
+
const em = container.resolve("em").fork();
|
|
28
|
+
const reaction = await findOneWithDecryption(
|
|
29
|
+
em,
|
|
30
|
+
MessageReaction,
|
|
31
|
+
{
|
|
32
|
+
id: reactionId,
|
|
33
|
+
messageId,
|
|
34
|
+
tenantId: auth.tenantId,
|
|
35
|
+
organizationId: auth.orgId ?? null
|
|
36
|
+
},
|
|
37
|
+
void 0,
|
|
38
|
+
{ tenantId: auth.tenantId, organizationId: auth.orgId ?? null }
|
|
39
|
+
);
|
|
40
|
+
if (!reaction) {
|
|
41
|
+
return NextResponse.json({ error: "Reaction not found" }, { status: 404 });
|
|
42
|
+
}
|
|
43
|
+
const guard = await validateRouteMutationGuard({
|
|
44
|
+
container,
|
|
45
|
+
req: _req,
|
|
46
|
+
auth,
|
|
47
|
+
input: {
|
|
48
|
+
resourceKind: "communication_channels.message",
|
|
49
|
+
resourceId: messageId,
|
|
50
|
+
operation: "custom",
|
|
51
|
+
mutationPayload: { reactionId, emoji: reaction.emoji, action: "remove" }
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if ("response" in guard) return guard.response;
|
|
55
|
+
const commandBus = container.resolve("commandBus");
|
|
56
|
+
const input = {
|
|
57
|
+
messageId,
|
|
58
|
+
emoji: reaction.emoji,
|
|
59
|
+
action: "remove",
|
|
60
|
+
reactionId,
|
|
61
|
+
reactedByUserId: auth.sub,
|
|
62
|
+
scope: {
|
|
63
|
+
tenantId: auth.tenantId,
|
|
64
|
+
organizationId: auth.orgId ?? null
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const { result } = await commandBus.execute(COMMUNICATION_CHANNELS_TOGGLE_OUTBOUND_REACTION_COMMAND_ID, {
|
|
68
|
+
input,
|
|
69
|
+
ctx: {
|
|
70
|
+
container,
|
|
71
|
+
auth,
|
|
72
|
+
organizationScope: null,
|
|
73
|
+
selectedOrganizationId: auth.orgId ?? null,
|
|
74
|
+
organizationIds: auth.orgId ? [auth.orgId] : null
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (result.status === "noop") {
|
|
78
|
+
return NextResponse.json({ error: result.reason }, { status: 404 });
|
|
79
|
+
}
|
|
80
|
+
if (result.status === "not_owner") {
|
|
81
|
+
return NextResponse.json({ error: result.reason }, { status: 403 });
|
|
82
|
+
}
|
|
83
|
+
if (result.status === "no_channel_link") {
|
|
84
|
+
return NextResponse.json({ error: result.reason }, { status: 409 });
|
|
85
|
+
}
|
|
86
|
+
await guard.afterSuccess();
|
|
87
|
+
return new NextResponse(null, { status: 204 });
|
|
88
|
+
}
|
|
89
|
+
const openApi = {
|
|
90
|
+
tags: ["CommunicationChannels"],
|
|
91
|
+
methods: {
|
|
92
|
+
DELETE: {
|
|
93
|
+
summary: "Remove a reaction from a channel-linked message",
|
|
94
|
+
tags: ["CommunicationChannels"],
|
|
95
|
+
responses: [
|
|
96
|
+
{ status: 204, description: "Reaction removed" },
|
|
97
|
+
{ status: 400, description: "Invalid params" },
|
|
98
|
+
{ status: 401, description: "Unauthorized" },
|
|
99
|
+
{ status: 403, description: "Channel is owned by another user" },
|
|
100
|
+
{ status: 404, description: "Reaction not found or not owned by current user" },
|
|
101
|
+
{ status: 409, description: "Message not channel-linked" }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var route_default = DELETE;
|
|
107
|
+
export {
|
|
108
|
+
DELETE,
|
|
109
|
+
route_default as default,
|
|
110
|
+
metadata,
|
|
111
|
+
openApi
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../../src/modules/communication_channels/api/delete/messages/%5BmessageId%5D/reactions/%5BreactionId%5D/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n COMMUNICATION_CHANNELS_TOGGLE_OUTBOUND_REACTION_COMMAND_ID,\n type ToggleOutboundReactionInput,\n type ToggleOutboundReactionResult,\n} from '../../../../../../commands/toggle-outbound-reaction'\nimport { MessageReaction } from '../../../../../../data/entities'\nimport { validateRouteMutationGuard } from '../../../../../../lib/route-mutation-guard'\n\nexport const metadata = {\n path: '/communication_channels/messages/[messageId]/reactions/[reactionId]',\n DELETE: {\n requireAuth: true,\n requireFeatures: ['communication_channels.react'],\n },\n}\n\ntype RouteContext = {\n params: Promise<{ messageId: string; reactionId: string }> | { messageId: string; reactionId: string }\n auth?: {\n sub?: string\n tenantId?: string\n /**\n * AuthContext (`packages/shared/src/lib/auth/server.ts`) exposes the\n * selected organization as `orgId`, not `organizationId`. Round-2 F3 fix.\n */\n orgId?: string | null\n } | null\n}\n\nexport async function DELETE(_req: Request, context: RouteContext): Promise<Response> {\n const { messageId, reactionId } = await context.params\n if (\n !messageId ||\n !reactionId ||\n !z.string().uuid().safeParse(messageId).success ||\n !z.string().uuid().safeParse(reactionId).success\n ) {\n return NextResponse.json({ error: 'Invalid params' }, { status: 400 })\n }\n\n const auth = context.auth\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager).fork()\n const reaction = await findOneWithDecryption(\n em,\n MessageReaction,\n {\n id: reactionId,\n messageId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n },\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId ?? null },\n )\n if (!reaction) {\n return NextResponse.json({ error: 'Reaction not found' }, { status: 404 })\n }\n\n const guard = await validateRouteMutationGuard({\n container,\n req: _req,\n auth,\n input: {\n resourceKind: 'communication_channels.message',\n resourceId: messageId,\n operation: 'custom',\n mutationPayload: { reactionId, emoji: reaction.emoji, action: 'remove' },\n },\n })\n if ('response' in guard) return guard.response\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const input: ToggleOutboundReactionInput = {\n messageId,\n emoji: reaction.emoji,\n action: 'remove',\n reactionId,\n reactedByUserId: auth.sub,\n scope: {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n },\n }\n\n const { result } = await commandBus.execute<\n ToggleOutboundReactionInput,\n ToggleOutboundReactionResult\n >(COMMUNICATION_CHANNELS_TOGGLE_OUTBOUND_REACTION_COMMAND_ID, {\n input,\n ctx: {\n container,\n auth: auth as never,\n organizationScope: null,\n selectedOrganizationId: auth.orgId ?? null,\n organizationIds: auth.orgId ? [auth.orgId] : null,\n },\n })\n\n if (result.status === 'noop') {\n return NextResponse.json({ error: result.reason }, { status: 404 })\n }\n if (result.status === 'not_owner') {\n // The command's ownership gate runs before the remove branch too \u2014 surface it\n // as 403 (matching the POST reactions route) rather than a false 204 success.\n return NextResponse.json({ error: result.reason }, { status: 403 })\n }\n if (result.status === 'no_channel_link') {\n return NextResponse.json({ error: result.reason }, { status: 409 })\n }\n // 'removed'\n await guard.afterSuccess()\n return new NextResponse(null, { status: 204 })\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n DELETE: {\n summary: 'Remove a reaction from a channel-linked message',\n tags: ['CommunicationChannels'],\n responses: [\n { status: 204, description: 'Reaction removed' },\n { status: 400, description: 'Invalid params' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Channel is owned by another user' },\n { status: 404, description: 'Reaction not found or not owned by current user' },\n { status: 409, description: 'Message not channel-linked' },\n ],\n },\n },\n}\nexport default DELETE\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
|
+
import { CommunicationChannel, ExternalConversation, MessageChannelLink } from "../../../../../data/entities.js";
|
|
7
|
+
import { ChannelAccessDeniedError, assertCanAccessChannel } from "../../../../../lib/access-control.js";
|
|
8
|
+
const metadata = {
|
|
9
|
+
path: "/communication_channels/channels/[id]/health",
|
|
10
|
+
GET: { requireAuth: true, requireFeatures: ["communication_channels.view"] }
|
|
11
|
+
};
|
|
12
|
+
const WINDOW_HOURS = 24;
|
|
13
|
+
const RECENT_FAILURES_LIMIT = 10;
|
|
14
|
+
async function GET(req, context) {
|
|
15
|
+
const { id } = await context.params;
|
|
16
|
+
if (!z.string().uuid().safeParse(id).success) {
|
|
17
|
+
return NextResponse.json({ error: "Invalid channel id" }, { status: 400 });
|
|
18
|
+
}
|
|
19
|
+
const auth = await getAuthFromRequest(req);
|
|
20
|
+
if (!auth?.tenantId) {
|
|
21
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
22
|
+
}
|
|
23
|
+
const container = await createRequestContainer();
|
|
24
|
+
const em = container.resolve("em").fork();
|
|
25
|
+
const dscope = {
|
|
26
|
+
tenantId: auth.tenantId,
|
|
27
|
+
organizationId: auth.orgId ?? null
|
|
28
|
+
};
|
|
29
|
+
const channel = await findOneWithDecryption(
|
|
30
|
+
em,
|
|
31
|
+
CommunicationChannel,
|
|
32
|
+
{ id, tenantId: auth.tenantId, organizationId: dscope.organizationId, deletedAt: null },
|
|
33
|
+
void 0,
|
|
34
|
+
dscope
|
|
35
|
+
);
|
|
36
|
+
if (!channel) {
|
|
37
|
+
return NextResponse.json({ error: "Channel not found" }, { status: 404 });
|
|
38
|
+
}
|
|
39
|
+
let userFeatures = [];
|
|
40
|
+
try {
|
|
41
|
+
const rbac = container.resolve("rbacService");
|
|
42
|
+
const acl = await rbac.loadAcl(auth.sub, {
|
|
43
|
+
tenantId: auth.tenantId,
|
|
44
|
+
organizationId: dscope.organizationId
|
|
45
|
+
});
|
|
46
|
+
userFeatures = acl?.isSuperAdmin ? ["*"] : Array.isArray(acl?.features) ? acl.features : [];
|
|
47
|
+
} catch {
|
|
48
|
+
userFeatures = [];
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
assertCanAccessChannel(channel, auth.sub, userFeatures);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (err instanceof ChannelAccessDeniedError) {
|
|
54
|
+
return NextResponse.json({ error: "Channel not found" }, { status: 404 });
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
const conversations = await findWithDecryption(
|
|
59
|
+
em,
|
|
60
|
+
ExternalConversation,
|
|
61
|
+
{ channelId: channel.id, tenantId: auth.tenantId },
|
|
62
|
+
void 0,
|
|
63
|
+
dscope
|
|
64
|
+
);
|
|
65
|
+
const conversationIds = conversations.map((c) => c.id).filter(Boolean);
|
|
66
|
+
let links = [];
|
|
67
|
+
let recentFailures = [];
|
|
68
|
+
if (conversationIds.length > 0) {
|
|
69
|
+
const since = new Date(Date.now() - WINDOW_HOURS * 60 * 60 * 1e3);
|
|
70
|
+
links = await findWithDecryption(
|
|
71
|
+
em,
|
|
72
|
+
MessageChannelLink,
|
|
73
|
+
{
|
|
74
|
+
externalConversationId: { $in: conversationIds },
|
|
75
|
+
tenantId: auth.tenantId,
|
|
76
|
+
createdAt: { $gte: since }
|
|
77
|
+
},
|
|
78
|
+
void 0,
|
|
79
|
+
dscope
|
|
80
|
+
);
|
|
81
|
+
recentFailures = await findWithDecryption(
|
|
82
|
+
em,
|
|
83
|
+
MessageChannelLink,
|
|
84
|
+
{
|
|
85
|
+
externalConversationId: { $in: conversationIds },
|
|
86
|
+
tenantId: auth.tenantId,
|
|
87
|
+
deliveryStatus: "failed"
|
|
88
|
+
},
|
|
89
|
+
{ limit: RECENT_FAILURES_LIMIT, orderBy: { createdAt: "desc" } },
|
|
90
|
+
dscope
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const counts = { sent: 0, delivered: 0, read: 0, failed: 0, pending: 0, queued: 0, other: 0 };
|
|
94
|
+
for (const link of links) {
|
|
95
|
+
const key = link.deliveryStatus;
|
|
96
|
+
if (key in counts) counts[key] += 1;
|
|
97
|
+
else counts.other += 1;
|
|
98
|
+
}
|
|
99
|
+
return NextResponse.json({
|
|
100
|
+
channelId: channel.id,
|
|
101
|
+
providerKey: channel.providerKey,
|
|
102
|
+
channelType: channel.channelType,
|
|
103
|
+
windowHours: WINDOW_HOURS,
|
|
104
|
+
counts,
|
|
105
|
+
totalsLast24h: links.length,
|
|
106
|
+
recentFailures: recentFailures.map((link) => ({
|
|
107
|
+
id: link.id,
|
|
108
|
+
messageId: link.messageId,
|
|
109
|
+
direction: link.direction,
|
|
110
|
+
createdAt: link.createdAt?.toISOString?.() ?? null,
|
|
111
|
+
lastError: link.channelMetadata?.lastError ?? null,
|
|
112
|
+
transient: link.channelMetadata?.transient ?? null
|
|
113
|
+
}))
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const openApi = {
|
|
117
|
+
tags: ["CommunicationChannels"],
|
|
118
|
+
methods: {
|
|
119
|
+
GET: {
|
|
120
|
+
summary: "Snapshot of channel delivery health (last 24h)",
|
|
121
|
+
tags: ["CommunicationChannels"],
|
|
122
|
+
responses: [
|
|
123
|
+
{ status: 200, description: "Health snapshot" },
|
|
124
|
+
{ status: 400, description: "Invalid channel id" },
|
|
125
|
+
{ status: 401, description: "Unauthorized" },
|
|
126
|
+
{ status: 404, description: "Channel not found" }
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var route_default = GET;
|
|
132
|
+
export {
|
|
133
|
+
GET,
|
|
134
|
+
route_default as default,
|
|
135
|
+
metadata,
|
|
136
|
+
openApi
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=route.js.map
|