@open-mercato/core 0.6.8-develop.6983.1.e4f794099e → 0.6.8-develop.6985.1.fb93574faa
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/notification/index.js +6 -0
- package/dist/generated/entities/notification/index.js.map +2 -2
- package/dist/generated/entities/notification_preference/index.js +19 -0
- package/dist/generated/entities/notification_preference/index.js.map +7 -0
- package/dist/generated/entities/notification_type/index.js +21 -0
- package/dist/generated/entities/notification_type/index.js.map +7 -0
- package/dist/generated/entities/notification_type_override/index.js +17 -0
- package/dist/generated/entities/notification_type_override/index.js.map +7 -0
- package/dist/generated/entities/push_notification_delivery/index.js +41 -0
- package/dist/generated/entities/push_notification_delivery/index.js.map +7 -0
- package/dist/generated/entities/user_device/index.js +35 -0
- package/dist/generated/entities/user_device/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +12 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +72 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/api.js +2 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/appRoot.js +9 -0
- package/dist/helpers/integration/appRoot.js.map +7 -0
- package/dist/helpers/integration/dbFixtures.js +1 -4
- package/dist/helpers/integration/dbFixtures.js.map +2 -2
- package/dist/helpers/integration/pushFake.js +126 -0
- package/dist/helpers/integration/pushFake.js.map +7 -0
- package/dist/modules/auth/notifications.js +16 -0
- package/dist/modules/auth/notifications.js.map +2 -2
- package/dist/modules/business_rules/notifications.js +2 -0
- package/dist/modules/business_rules/notifications.js.map +2 -2
- package/dist/modules/catalog/notifications.js +2 -0
- package/dist/modules/catalog/notifications.js.map +2 -2
- package/dist/modules/communication_channels/acl.js +9 -0
- package/dist/modules/communication_channels/acl.js.map +2 -2
- package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js +133 -0
- package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +6 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js +6 -2
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/channels/route.js +2 -1
- package/dist/modules/communication_channels/api/get/channels/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +10 -5
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +29 -1
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +157 -0
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +8 -1
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/admin-delete-channel.js +141 -0
- package/dist/modules/communication_channels/commands/admin-delete-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +25 -8
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-register.js +1 -1
- package/dist/modules/communication_channels/commands/push-register.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-unregister.js +4 -3
- package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
- package/dist/modules/communication_channels/data/entities.js +4 -0
- package/dist/modules/communication_channels/data/entities.js.map +2 -2
- package/dist/modules/communication_channels/data/validators.js +2 -1
- package/dist/modules/communication_channels/data/validators.js.map +2 -2
- package/dist/modules/communication_channels/lib/access-control.js +11 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/connect-channel.js +20 -12
- package/dist/modules/communication_channels/lib/connect-channel.js.map +2 -2
- package/dist/modules/communication_channels/lib/push-adapter.js +68 -0
- package/dist/modules/communication_channels/lib/push-adapter.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-capabilities.js +31 -0
- package/dist/modules/communication_channels/lib/push-capabilities.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-connect-error.js +20 -0
- package/dist/modules/communication_channels/lib/push-connect-error.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-credential-errors.js +41 -0
- package/dist/modules/communication_channels/lib/push-credential-errors.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-envelope.js +46 -0
- package/dist/modules/communication_channels/lib/push-envelope.js.map +7 -0
- package/dist/modules/communication_channels/lib/refcounted-client-cache.js +64 -0
- package/dist/modules/communication_channels/lib/refcounted-client-cache.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js +13 -0
- package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/notifications.js +4 -0
- package/dist/modules/communication_channels/notifications.js.map +2 -2
- package/dist/modules/communication_channels/setup.js +2 -0
- package/dist/modules/communication_channels/setup.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +21 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/notifications.js +12 -0
- package/dist/modules/customer_accounts/notifications.js.map +2 -2
- package/dist/modules/customers/notifications.js +4 -0
- package/dist/modules/customers/notifications.js.map +2 -2
- package/dist/modules/devices/acl.js +11 -0
- package/dist/modules/devices/acl.js.map +7 -0
- package/dist/modules/devices/api/[id]/route.js +167 -0
- package/dist/modules/devices/api/[id]/route.js.map +7 -0
- package/dist/modules/devices/api/admin/devices/[id]/route.js +219 -0
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +7 -0
- package/dist/modules/devices/api/admin/devices/route.js +140 -0
- package/dist/modules/devices/api/admin/devices/route.js.map +7 -0
- package/dist/modules/devices/api/auth.js +9 -0
- package/dist/modules/devices/api/auth.js.map +7 -0
- package/dist/modules/devices/api/deviceList.js +53 -0
- package/dist/modules/devices/api/deviceList.js.map +7 -0
- package/dist/modules/devices/api/deviceOps.js +118 -0
- package/dist/modules/devices/api/deviceOps.js.map +7 -0
- package/dist/modules/devices/api/openapi.js +19 -0
- package/dist/modules/devices/api/openapi.js.map +7 -0
- package/dist/modules/devices/api/route.js +143 -0
- package/dist/modules/devices/api/route.js.map +7 -0
- package/dist/modules/devices/backend/devices/[id]/page.js +135 -0
- package/dist/modules/devices/backend/devices/[id]/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/[id]/page.meta.js +16 -0
- package/dist/modules/devices/backend/devices/[id]/page.meta.js.map +7 -0
- package/dist/modules/devices/backend/devices/create/page.js +68 -0
- package/dist/modules/devices/backend/devices/create/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/create/page.meta.js +16 -0
- package/dist/modules/devices/backend/devices/create/page.meta.js.map +7 -0
- package/dist/modules/devices/backend/devices/page.js +235 -0
- package/dist/modules/devices/backend/devices/page.js.map +7 -0
- package/dist/modules/devices/backend/devices/page.meta.js +23 -0
- package/dist/modules/devices/backend/devices/page.meta.js.map +7 -0
- package/dist/modules/devices/commands/deactivate.js +107 -0
- package/dist/modules/devices/commands/deactivate.js.map +7 -0
- package/dist/modules/devices/commands/index.js +4 -0
- package/dist/modules/devices/commands/index.js.map +7 -0
- package/dist/modules/devices/commands/register.js +183 -0
- package/dist/modules/devices/commands/register.js.map +7 -0
- package/dist/modules/devices/commands/shared.js +74 -0
- package/dist/modules/devices/commands/shared.js.map +7 -0
- package/dist/modules/devices/commands/update.js +126 -0
- package/dist/modules/devices/commands/update.js.map +7 -0
- package/dist/modules/devices/data/entities.js +83 -0
- package/dist/modules/devices/data/entities.js.map +7 -0
- package/dist/modules/devices/data/validators.js +49 -0
- package/dist/modules/devices/data/validators.js.map +7 -0
- package/dist/modules/devices/di.js +11 -0
- package/dist/modules/devices/di.js.map +7 -0
- package/dist/modules/devices/encryption.js +12 -0
- package/dist/modules/devices/encryption.js.map +7 -0
- package/dist/modules/devices/events.js +19 -0
- package/dist/modules/devices/events.js.map +7 -0
- package/dist/modules/devices/index.js +17 -0
- package/dist/modules/devices/index.js.map +7 -0
- package/dist/modules/devices/lib/operationMetadata.js +20 -0
- package/dist/modules/devices/lib/operationMetadata.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260602120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260602120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260630120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260630120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260701120000.js +13 -0
- package/dist/modules/devices/migrations/Migration20260701120000.js.map +7 -0
- package/dist/modules/devices/migrations/Migration20260721120000.js +15 -0
- package/dist/modules/devices/migrations/Migration20260721120000.js.map +7 -0
- package/dist/modules/devices/setup.js +13 -0
- package/dist/modules/devices/setup.js.map +7 -0
- package/dist/modules/entities/cli.js +8 -3
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/entities/migrations/Migration20260722120000.js +28 -0
- package/dist/modules/entities/migrations/Migration20260722120000.js.map +7 -0
- package/dist/modules/inbox_ops/notifications.js +2 -0
- package/dist/modules/inbox_ops/notifications.js.map +2 -2
- package/dist/modules/messages/notifications.js +2 -0
- package/dist/modules/messages/notifications.js.map +2 -2
- package/dist/modules/notifications/acl.js +3 -1
- package/dist/modules/notifications/acl.js.map +2 -2
- package/dist/modules/notifications/api/admin/preferences/route.js +167 -0
- package/dist/modules/notifications/api/admin/preferences/route.js.map +7 -0
- package/dist/modules/notifications/api/channels/route.js +54 -0
- package/dist/modules/notifications/api/channels/route.js.map +7 -0
- package/dist/modules/notifications/api/preferences/route.js +126 -0
- package/dist/modules/notifications/api/preferences/route.js.map +7 -0
- package/dist/modules/notifications/api/route.js +8 -1
- package/dist/modules/notifications/api/route.js.map +2 -2
- package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js +187 -0
- package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js.map +7 -0
- package/dist/modules/notifications/api/types/route.js +225 -0
- package/dist/modules/notifications/api/types/route.js.map +7 -0
- package/dist/modules/notifications/api/unread-count/route.js +5 -1
- package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
- package/dist/modules/notifications/backend/notifications/user-preferences/page.js +10 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.js.map +7 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js +28 -0
- package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js.map +7 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.js +10 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.js.map +7 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +33 -0
- package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +7 -0
- package/dist/modules/notifications/data/entities.js +128 -1
- package/dist/modules/notifications/data/entities.js.map +2 -2
- package/dist/modules/notifications/data/validators.js +95 -2
- package/dist/modules/notifications/data/validators.js.map +2 -2
- package/dist/modules/notifications/di.js +7 -0
- package/dist/modules/notifications/di.js.map +2 -2
- package/dist/modules/notifications/events.js +17 -0
- package/dist/modules/notifications/events.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js +190 -0
- package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js +124 -0
- package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +166 -1
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js +172 -0
- package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js.map +7 -0
- package/dist/modules/notifications/generators.js +63 -0
- package/dist/modules/notifications/generators.js.map +7 -0
- package/dist/modules/notifications/lib/delivery-strategies-registry.js +19 -0
- package/dist/modules/notifications/lib/delivery-strategies-registry.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +2 -2
- package/dist/modules/notifications/lib/derive-category.js +8 -0
- package/dist/modules/notifications/lib/derive-category.js.map +7 -0
- package/dist/modules/notifications/lib/notification-channel-registry.js +35 -0
- package/dist/modules/notifications/lib/notification-channel-registry.js.map +7 -0
- package/dist/modules/notifications/lib/notification-type-registry.js +87 -0
- package/dist/modules/notifications/lib/notification-type-registry.js.map +7 -0
- package/dist/modules/notifications/lib/notificationCopy.js +14 -0
- package/dist/modules/notifications/lib/notificationCopy.js.map +7 -0
- package/dist/modules/notifications/lib/notificationFactory.js +4 -1
- package/dist/modules/notifications/lib/notificationFactory.js.map +2 -2
- package/dist/modules/notifications/lib/notificationMapper.js +2 -0
- package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
- package/dist/modules/notifications/lib/notificationPreferenceService.js +83 -0
- package/dist/modules/notifications/lib/notificationPreferenceService.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +83 -28
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/notifications/lib/notificationVisibility.js +24 -0
- package/dist/modules/notifications/lib/notificationVisibility.js.map +7 -0
- package/dist/modules/notifications/lib/routeHelpers.js +11 -1
- package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
- package/dist/modules/notifications/lib/shouldDeliver.js +46 -0
- package/dist/modules/notifications/lib/shouldDeliver.js.map +7 -0
- package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js +54 -0
- package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js.map +7 -0
- package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js +12 -0
- package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js.map +7 -0
- package/dist/modules/notifications/lib/typeCatalogue.js +71 -0
- package/dist/modules/notifications/lib/typeCatalogue.js.map +7 -0
- package/dist/modules/notifications/lib/typeChannelSettings.js +12 -0
- package/dist/modules/notifications/lib/typeChannelSettings.js.map +7 -0
- package/dist/modules/notifications/lib/typeOverrides.js +19 -0
- package/dist/modules/notifications/lib/typeOverrides.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js +14 -0
- package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js +15 -0
- package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js +15 -0
- package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js +14 -0
- package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js.map +7 -0
- package/dist/modules/notifications/notification-channels.js +26 -0
- package/dist/modules/notifications/notification-channels.js.map +7 -0
- package/dist/modules/notifications/notifications.delivery-strategies.js +12 -0
- package/dist/modules/notifications/notifications.delivery-strategies.js.map +7 -0
- package/dist/modules/notifications/setup.js +16 -1
- package/dist/modules/notifications/setup.js.map +2 -2
- package/dist/modules/notifications/subscribers/deliver-notification.js +66 -57
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
- package/dist/modules/notifications/subscribers/sync-notification-types.js +16 -0
- package/dist/modules/notifications/subscribers/sync-notification-types.js.map +7 -0
- package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js +23 -0
- package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js.map +7 -0
- package/dist/modules/notifications/widgets/injection-table.js +14 -0
- package/dist/modules/notifications/widgets/injection-table.js.map +7 -0
- package/dist/modules/push_notifications/acl.js +13 -0
- package/dist/modules/push_notifications/acl.js.map +7 -0
- package/dist/modules/push_notifications/api/custom-send/route.js +141 -0
- package/dist/modules/push_notifications/api/custom-send/route.js.map +7 -0
- package/dist/modules/push_notifications/api/deliveries/[id]/route.js +110 -0
- package/dist/modules/push_notifications/api/deliveries/[id]/route.js.map +7 -0
- package/dist/modules/push_notifications/api/deliveries/route.js +56 -0
- package/dist/modules/push_notifications/api/deliveries/route.js.map +7 -0
- package/dist/modules/push_notifications/api/openapi.js +19 -0
- package/dist/modules/push_notifications/api/openapi.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js +103 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js +13 -0
- package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.js +213 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.meta.js +23 -0
- package/dist/modules/push_notifications/backend/push_notifications/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +245 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +7 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js +26 -0
- package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js.map +7 -0
- package/dist/modules/push_notifications/data/entities.js +92 -0
- package/dist/modules/push_notifications/data/entities.js.map +7 -0
- package/dist/modules/push_notifications/data/extensions.js +23 -0
- package/dist/modules/push_notifications/data/extensions.js.map +7 -0
- package/dist/modules/push_notifications/data/validators.js +89 -0
- package/dist/modules/push_notifications/data/validators.js.map +7 -0
- package/dist/modules/push_notifications/di.js +17 -0
- package/dist/modules/push_notifications/di.js.map +7 -0
- package/dist/modules/push_notifications/events.js +24 -0
- package/dist/modules/push_notifications/events.js.map +7 -0
- package/dist/modules/push_notifications/index.js +16 -0
- package/dist/modules/push_notifications/index.js.map +7 -0
- package/dist/modules/push_notifications/lib/fake-provider-recorder.js +80 -0
- package/dist/modules/push_notifications/lib/fake-provider-recorder.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-delivery-strategy.js +54 -0
- package/dist/modules/push_notifications/lib/push-delivery-strategy.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-delivery.js +297 -0
- package/dist/modules/push_notifications/lib/push-delivery.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-fanout.js +138 -0
- package/dist/modules/push_notifications/lib/push-fanout.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-health.js +32 -0
- package/dist/modules/push_notifications/lib/push-health.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-reaper.js +87 -0
- package/dist/modules/push_notifications/lib/push-reaper.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-receipt-reaper.js +136 -0
- package/dist/modules/push_notifications/lib/push-receipt-reaper.js.map +7 -0
- package/dist/modules/push_notifications/lib/push-stub-adapter.js +63 -0
- package/dist/modules/push_notifications/lib/push-stub-adapter.js.map +7 -0
- package/dist/modules/push_notifications/lib/queue.js +53 -0
- package/dist/modules/push_notifications/lib/queue.js.map +7 -0
- package/dist/modules/push_notifications/lib/reclaim-window.js +13 -0
- package/dist/modules/push_notifications/lib/reclaim-window.js.map +7 -0
- package/dist/modules/push_notifications/lib/send-custom-push.js +48 -0
- package/dist/modules/push_notifications/lib/send-custom-push.js.map +7 -0
- package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js +16 -0
- package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js.map +7 -0
- package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js +13 -0
- package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js.map +7 -0
- package/dist/modules/push_notifications/notifications.delivery-strategies.js +8 -0
- package/dist/modules/push_notifications/notifications.delivery-strategies.js.map +7 -0
- package/dist/modules/push_notifications/notifications.js +40 -0
- package/dist/modules/push_notifications/notifications.js.map +7 -0
- package/dist/modules/push_notifications/setup.js +52 -0
- package/dist/modules/push_notifications/setup.js.map +7 -0
- package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js +57 -0
- package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js.map +7 -0
- package/dist/modules/push_notifications/workers/send-push.worker.js +27 -0
- package/dist/modules/push_notifications/workers/send-push.worker.js.map +7 -0
- package/dist/modules/sales/notifications.js +8 -0
- package/dist/modules/sales/notifications.js.map +2 -2
- package/dist/modules/staff/notifications.js +6 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/workflows/notifications.js +2 -0
- package/dist/modules/workflows/notifications.js.map +2 -2
- package/generated/entities/notification/index.ts +3 -0
- package/generated/entities/notification_preference/index.ts +8 -0
- package/generated/entities/notification_type/index.ts +9 -0
- package/generated/entities/notification_type_override/index.ts +7 -0
- package/generated/entities/push_notification_delivery/index.ts +19 -0
- package/generated/entities/user_device/index.ts +16 -0
- package/generated/entities.ids.generated.ts +12 -1
- package/generated/entity-fields-registry.ts +72 -0
- package/package.json +7 -7
- package/src/helpers/integration/api.ts +2 -1
- package/src/helpers/integration/appRoot.ts +13 -0
- package/src/helpers/integration/dbFixtures.ts +1 -5
- package/src/helpers/integration/pushFake.ts +219 -0
- package/src/modules/auth/i18n/de.json +1 -0
- package/src/modules/auth/i18n/en.json +1 -0
- package/src/modules/auth/i18n/es.json +1 -0
- package/src/modules/auth/i18n/ko.json +1 -0
- package/src/modules/auth/i18n/pl.json +1 -0
- package/src/modules/auth/notifications.ts +16 -0
- package/src/modules/business_rules/i18n/de.json +2 -1
- package/src/modules/business_rules/i18n/en.json +2 -1
- package/src/modules/business_rules/i18n/es.json +2 -1
- package/src/modules/business_rules/i18n/ko.json +2 -1
- package/src/modules/business_rules/i18n/pl.json +2 -1
- package/src/modules/business_rules/notifications.ts +2 -0
- package/src/modules/catalog/i18n/de.json +2 -1
- package/src/modules/catalog/i18n/en.json +2 -1
- package/src/modules/catalog/i18n/es.json +2 -1
- package/src/modules/catalog/i18n/ko.json +2 -1
- package/src/modules/catalog/i18n/pl.json +2 -1
- package/src/modules/catalog/notifications.ts +2 -0
- package/src/modules/communication_channels/acl.ts +9 -0
- package/src/modules/communication_channels/api/delete/admin/channels/[id]/route.ts +168 -0
- package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +6 -2
- package/src/modules/communication_channels/api/get/channels/[id]/route.ts +6 -2
- package/src/modules/communication_channels/api/get/channels/route.ts +2 -1
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +16 -5
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +37 -1
- package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +175 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +11 -1
- package/src/modules/communication_channels/commands/admin-delete-channel.ts +203 -0
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +63 -15
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +10 -2
- package/src/modules/communication_channels/commands/push-register.ts +7 -1
- package/src/modules/communication_channels/commands/push-unregister.ts +14 -3
- package/src/modules/communication_channels/data/entities.ts +10 -0
- package/src/modules/communication_channels/data/validators.ts +1 -0
- package/src/modules/communication_channels/i18n/de.json +35 -1
- package/src/modules/communication_channels/i18n/en.json +35 -1
- package/src/modules/communication_channels/i18n/es.json +35 -1
- package/src/modules/communication_channels/i18n/ko.json +35 -1
- package/src/modules/communication_channels/i18n/pl.json +35 -1
- package/src/modules/communication_channels/lib/access-control.ts +42 -0
- package/src/modules/communication_channels/lib/adapter.ts +25 -0
- package/src/modules/communication_channels/lib/connect-channel.ts +61 -26
- package/src/modules/communication_channels/lib/push-adapter.ts +139 -0
- package/src/modules/communication_channels/lib/push-capabilities.ts +42 -0
- package/src/modules/communication_channels/lib/push-connect-error.ts +52 -0
- package/src/modules/communication_channels/lib/push-credential-errors.ts +58 -0
- package/src/modules/communication_channels/lib/push-envelope.ts +104 -0
- package/src/modules/communication_channels/lib/refcounted-client-cache.ts +123 -0
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +9 -0
- package/src/modules/communication_channels/migrations/Migration20260703123630_communication_channels.ts +20 -0
- package/src/modules/communication_channels/notifications.ts +4 -0
- package/src/modules/communication_channels/setup.ts +2 -0
- package/src/modules/configs/i18n/de.json +5 -1
- package/src/modules/configs/i18n/en.json +5 -1
- package/src/modules/configs/i18n/es.json +5 -1
- package/src/modules/configs/i18n/ko.json +5 -1
- package/src/modules/configs/i18n/pl.json +5 -1
- package/src/modules/configs/lib/upgrade-actions.ts +21 -0
- package/src/modules/customer_accounts/i18n/de.json +2 -1
- package/src/modules/customer_accounts/i18n/en.json +2 -1
- package/src/modules/customer_accounts/i18n/es.json +2 -1
- package/src/modules/customer_accounts/i18n/ko.json +2 -1
- package/src/modules/customer_accounts/i18n/pl.json +2 -1
- package/src/modules/customer_accounts/notifications.ts +12 -0
- package/src/modules/customers/i18n/de.json +2 -1
- package/src/modules/customers/i18n/en.json +2 -1
- package/src/modules/customers/i18n/es.json +2 -1
- package/src/modules/customers/i18n/ko.json +2 -1
- package/src/modules/customers/i18n/pl.json +2 -1
- package/src/modules/customers/notifications.ts +4 -0
- package/src/modules/devices/AGENTS.md +101 -0
- package/src/modules/devices/acl.ts +7 -0
- package/src/modules/devices/api/[id]/route.ts +180 -0
- package/src/modules/devices/api/admin/devices/[id]/route.ts +235 -0
- package/src/modules/devices/api/admin/devices/route.ts +160 -0
- package/src/modules/devices/api/auth.ts +13 -0
- package/src/modules/devices/api/deviceList.ts +54 -0
- package/src/modules/devices/api/deviceOps.ts +186 -0
- package/src/modules/devices/api/openapi.ts +23 -0
- package/src/modules/devices/api/route.ts +165 -0
- package/src/modules/devices/backend/devices/[id]/page.meta.ts +12 -0
- package/src/modules/devices/backend/devices/[id]/page.tsx +164 -0
- package/src/modules/devices/backend/devices/create/page.meta.ts +12 -0
- package/src/modules/devices/backend/devices/create/page.tsx +81 -0
- package/src/modules/devices/backend/devices/page.meta.ts +21 -0
- package/src/modules/devices/backend/devices/page.tsx +259 -0
- package/src/modules/devices/commands/deactivate.ts +126 -0
- package/src/modules/devices/commands/index.ts +3 -0
- package/src/modules/devices/commands/register.ts +199 -0
- package/src/modules/devices/commands/shared.ts +119 -0
- package/src/modules/devices/commands/update.ts +142 -0
- package/src/modules/devices/data/entities.ts +79 -0
- package/src/modules/devices/data/validators.ts +65 -0
- package/src/modules/devices/di.ts +9 -0
- package/src/modules/devices/encryption.ts +15 -0
- package/src/modules/devices/events.ts +16 -0
- package/src/modules/devices/i18n/de.json +45 -0
- package/src/modules/devices/i18n/en.json +45 -0
- package/src/modules/devices/i18n/es.json +45 -0
- package/src/modules/devices/i18n/ko.json +45 -0
- package/src/modules/devices/i18n/pl.json +45 -0
- package/src/modules/devices/index.ts +15 -0
- package/src/modules/devices/lib/operationMetadata.ts +35 -0
- package/src/modules/devices/migrations/.snapshot-open-mercato.json +200 -0
- package/src/modules/devices/migrations/Migration20260602120000.ts +15 -0
- package/src/modules/devices/migrations/Migration20260630120000.ts +20 -0
- package/src/modules/devices/migrations/Migration20260701120000.ts +13 -0
- package/src/modules/devices/migrations/Migration20260721120000.ts +18 -0
- package/src/modules/devices/setup.ts +11 -0
- package/src/modules/entities/cli.ts +16 -2
- package/src/modules/entities/migrations/Migration20260722120000.ts +38 -0
- package/src/modules/inbox_ops/i18n/de.json +2 -1
- package/src/modules/inbox_ops/i18n/en.json +2 -1
- package/src/modules/inbox_ops/i18n/es.json +2 -1
- package/src/modules/inbox_ops/i18n/ko.json +2 -1
- package/src/modules/inbox_ops/i18n/pl.json +2 -1
- package/src/modules/inbox_ops/notifications.ts +2 -0
- package/src/modules/messages/i18n/de.json +2 -1
- package/src/modules/messages/i18n/en.json +2 -1
- package/src/modules/messages/i18n/es.json +2 -1
- package/src/modules/messages/i18n/ko.json +2 -1
- package/src/modules/messages/i18n/pl.json +2 -1
- package/src/modules/messages/notifications.ts +2 -0
- package/src/modules/notifications/AGENTS.md +54 -0
- package/src/modules/notifications/acl.ts +2 -0
- package/src/modules/notifications/api/admin/preferences/route.ts +193 -0
- package/src/modules/notifications/api/channels/route.ts +54 -0
- package/src/modules/notifications/api/preferences/route.ts +131 -0
- package/src/modules/notifications/api/route.ts +8 -1
- package/src/modules/notifications/api/types/[id]/channels/[channel]/route.ts +226 -0
- package/src/modules/notifications/api/types/route.ts +239 -0
- package/src/modules/notifications/api/unread-count/route.ts +5 -1
- package/src/modules/notifications/backend/notifications/user-preferences/page.meta.ts +26 -0
- package/src/modules/notifications/backend/notifications/user-preferences/page.tsx +12 -0
- package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +31 -0
- package/src/modules/notifications/backend/profile/notification-preferences/page.tsx +12 -0
- package/src/modules/notifications/data/entities.ts +144 -0
- package/src/modules/notifications/data/validators.ts +116 -0
- package/src/modules/notifications/di.ts +7 -0
- package/src/modules/notifications/events.ts +23 -0
- package/src/modules/notifications/frontend/NotificationPreferenceMatrix.tsx +287 -0
- package/src/modules/notifications/frontend/NotificationPreferencesPageClient.tsx +155 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +273 -2
- package/src/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.tsx +214 -0
- package/src/modules/notifications/generators.ts +82 -0
- package/src/modules/notifications/i18n/de.json +43 -0
- package/src/modules/notifications/i18n/en.json +43 -0
- package/src/modules/notifications/i18n/es.json +43 -0
- package/src/modules/notifications/i18n/ko.json +43 -0
- package/src/modules/notifications/i18n/pl.json +44 -1
- package/src/modules/notifications/lib/delivery-strategies-registry.ts +30 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +40 -4
- package/src/modules/notifications/lib/derive-category.ts +15 -0
- package/src/modules/notifications/lib/notification-channel-registry.ts +59 -0
- package/src/modules/notifications/lib/notification-type-registry.ts +182 -0
- package/src/modules/notifications/lib/notificationCopy.ts +34 -0
- package/src/modules/notifications/lib/notificationFactory.ts +5 -1
- package/src/modules/notifications/lib/notificationMapper.ts +2 -0
- package/src/modules/notifications/lib/notificationPreferenceService.ts +138 -0
- package/src/modules/notifications/lib/notificationService.ts +129 -25
- package/src/modules/notifications/lib/notificationVisibility.ts +48 -0
- package/src/modules/notifications/lib/routeHelpers.ts +14 -1
- package/src/modules/notifications/lib/shouldDeliver.ts +112 -0
- package/src/modules/notifications/lib/strategies/email-delivery-strategy.ts +63 -0
- package/src/modules/notifications/lib/strategies/in-app-delivery-strategy.ts +21 -0
- package/src/modules/notifications/lib/typeCatalogue.ts +141 -0
- package/src/modules/notifications/lib/typeChannelSettings.ts +29 -0
- package/src/modules/notifications/lib/typeOverrides.ts +46 -0
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +874 -100
- package/src/modules/notifications/migrations/Migration20260625122947_notifications.ts +14 -0
- package/src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts +15 -0
- package/src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts +13 -0
- package/src/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.ts +15 -0
- package/src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts +13 -0
- package/src/modules/notifications/migrations/Migration20260716123121_notifications.ts +14 -0
- package/src/modules/notifications/notification-channels.ts +30 -0
- package/src/modules/notifications/notifications.delivery-strategies.ts +15 -0
- package/src/modules/notifications/setup.ts +23 -1
- package/src/modules/notifications/subscribers/deliver-notification.ts +105 -71
- package/src/modules/notifications/subscribers/sync-notification-types.ts +18 -0
- package/src/modules/notifications/widgets/injection/profile-preferences-menu/widget.ts +35 -0
- package/src/modules/notifications/widgets/injection-table.ts +18 -0
- package/src/modules/push_notifications/AGENTS.md +92 -0
- package/src/modules/push_notifications/acl.ts +9 -0
- package/src/modules/push_notifications/api/custom-send/route.ts +175 -0
- package/src/modules/push_notifications/api/deliveries/[id]/route.ts +122 -0
- package/src/modules/push_notifications/api/deliveries/route.ts +58 -0
- package/src/modules/push_notifications/api/openapi.ts +22 -0
- package/src/modules/push_notifications/backend/push_notifications/[id]/page.meta.ts +9 -0
- package/src/modules/push_notifications/backend/push_notifications/[id]/page.tsx +149 -0
- package/src/modules/push_notifications/backend/push_notifications/page.meta.ts +21 -0
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +239 -0
- package/src/modules/push_notifications/backend/push_notifications/send/page.meta.ts +24 -0
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +327 -0
- package/src/modules/push_notifications/data/entities.ts +111 -0
- package/src/modules/push_notifications/data/extensions.ts +29 -0
- package/src/modules/push_notifications/data/validators.ts +102 -0
- package/src/modules/push_notifications/di.ts +20 -0
- package/src/modules/push_notifications/events.ts +21 -0
- package/src/modules/push_notifications/i18n/de.json +79 -0
- package/src/modules/push_notifications/i18n/en.json +79 -0
- package/src/modules/push_notifications/i18n/es.json +79 -0
- package/src/modules/push_notifications/i18n/ko.json +79 -0
- package/src/modules/push_notifications/i18n/pl.json +79 -0
- package/src/modules/push_notifications/index.ts +15 -0
- package/src/modules/push_notifications/lib/fake-provider-recorder.ts +154 -0
- package/src/modules/push_notifications/lib/push-delivery-strategy.ts +90 -0
- package/src/modules/push_notifications/lib/push-delivery.ts +455 -0
- package/src/modules/push_notifications/lib/push-fanout.ts +237 -0
- package/src/modules/push_notifications/lib/push-health.ts +47 -0
- package/src/modules/push_notifications/lib/push-reaper.ts +140 -0
- package/src/modules/push_notifications/lib/push-receipt-reaper.ts +210 -0
- package/src/modules/push_notifications/lib/push-stub-adapter.ts +80 -0
- package/src/modules/push_notifications/lib/queue.ts +74 -0
- package/src/modules/push_notifications/lib/reclaim-window.ts +16 -0
- package/src/modules/push_notifications/lib/send-custom-push.ts +100 -0
- package/src/modules/push_notifications/migrations/.snapshot-open-mercato.json +386 -0
- package/src/modules/push_notifications/migrations/Migration20260625150049_push_notifications.ts +16 -0
- package/src/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.ts +13 -0
- package/src/modules/push_notifications/notifications.delivery-strategies.ts +8 -0
- package/src/modules/push_notifications/notifications.ts +50 -0
- package/src/modules/push_notifications/setup.ts +92 -0
- package/src/modules/push_notifications/workers/reclaim-stuck.worker.ts +78 -0
- package/src/modules/push_notifications/workers/send-push.worker.ts +34 -0
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/ko.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
- package/src/modules/sales/notifications.ts +8 -0
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/ko.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/notifications.ts +6 -0
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/ko.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
- package/src/modules/workflows/notifications.ts +2 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import type { CommandHandler } from '@open-mercato/shared/lib/commands'
|
|
4
|
+
import { registerCommand } from '@open-mercato/shared/lib/commands'
|
|
5
|
+
import { extractUndoPayload as extractSharedUndoPayload } from '@open-mercato/shared/lib/commands/undo'
|
|
6
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
7
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
8
|
+
import { CommunicationChannel } from '../data/entities'
|
|
9
|
+
import { channelOrgScopeWhere } from '../lib/access-control'
|
|
10
|
+
import { pushUnregister } from './push-unregister'
|
|
11
|
+
import { emitCommunicationChannelsEvent } from '../events'
|
|
12
|
+
|
|
13
|
+
const logger = createLogger('communication_channels')
|
|
14
|
+
|
|
15
|
+
const adminDeleteChannelSchema = z.object({
|
|
16
|
+
channelId: z.string().uuid(),
|
|
17
|
+
scope: z.object({
|
|
18
|
+
tenantId: z.string().uuid(),
|
|
19
|
+
organizationId: z.string().uuid().nullable(),
|
|
20
|
+
}),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export type AdminDeleteChannelInput = z.infer<typeof adminDeleteChannelSchema>
|
|
24
|
+
|
|
25
|
+
export type AdminDeleteChannelResult =
|
|
26
|
+
| { status: 'deleted'; channelId: string; undo: AdminDeleteChannelUndoSnapshot }
|
|
27
|
+
| { status: 'noop'; reason: string }
|
|
28
|
+
| { status: 'not_tenant_wide'; reason: string }
|
|
29
|
+
|
|
30
|
+
export interface AdminDeleteChannelUndoSnapshot {
|
|
31
|
+
channelId: string
|
|
32
|
+
// Optional only for back-compat with any pre-existing log entries; new
|
|
33
|
+
// snapshots always set it so the undo lookup stays tenant-scoped.
|
|
34
|
+
tenantId?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const COMMUNICATION_CHANNELS_ADMIN_DELETE_CHANNEL_COMMAND_ID =
|
|
38
|
+
'communication_channels.channel.admin_delete'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Admin soft-delete for a TENANT-WIDE channel (`user_id IS NULL`) — the
|
|
42
|
+
* companion to `delete-channel.ts` (owner-only, per-user path). Tenant-wide
|
|
43
|
+
* channels (shared inboxes and the push providers FCM/APNs/Expo) have no owner,
|
|
44
|
+
* so the owner path rejects them as `not_owner`; this path deletes them under
|
|
45
|
+
* the `communication_channels.admin` feature.
|
|
46
|
+
*
|
|
47
|
+
* The channel is loaded org-agnostically (tenant-wide push channels store
|
|
48
|
+
* `organization_id IS NULL`, but shared inboxes may pin an org) via
|
|
49
|
+
* `channelOrgScopeWhere`, so it resolves from any session org in the tenant.
|
|
50
|
+
* Per-user channels are refused with `not_tenant_wide` — they MUST go through
|
|
51
|
+
* the owner path so personal-mailbox privacy holds.
|
|
52
|
+
*
|
|
53
|
+
* Provider-side push delivery is torn down best-effort BEFORE the soft-delete,
|
|
54
|
+
* keyed on the CHANNEL's own org (`channel.organizationId ?? tenantId`) so
|
|
55
|
+
* credentials resolve where they were written (see push-register.ts /
|
|
56
|
+
* connect-credential-channel.ts). Mirrors `delete-channel`'s teardown.
|
|
57
|
+
*
|
|
58
|
+
* Undoable: undo clears `deleted_at` to restore the row. Parity with
|
|
59
|
+
* `delete-channel` — same event, payload, buildLog, and undo shape.
|
|
60
|
+
*/
|
|
61
|
+
const adminDeleteChannelCommand: CommandHandler<
|
|
62
|
+
AdminDeleteChannelInput,
|
|
63
|
+
AdminDeleteChannelResult
|
|
64
|
+
> = {
|
|
65
|
+
id: COMMUNICATION_CHANNELS_ADMIN_DELETE_CHANNEL_COMMAND_ID,
|
|
66
|
+
// Explicitly undoable (the bus also infers this from `undo` below, but
|
|
67
|
+
// declaring it keeps undoability from silently dropping under a refactor).
|
|
68
|
+
isUndoable: true,
|
|
69
|
+
async execute(rawInput, ctx) {
|
|
70
|
+
const input = adminDeleteChannelSchema.parse(rawInput) as AdminDeleteChannelInput
|
|
71
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
72
|
+
const dscope = {
|
|
73
|
+
tenantId: input.scope.tenantId,
|
|
74
|
+
organizationId: input.scope.organizationId ?? null,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const channel = await findOneWithDecryption(
|
|
78
|
+
em,
|
|
79
|
+
CommunicationChannel,
|
|
80
|
+
{
|
|
81
|
+
id: input.channelId,
|
|
82
|
+
tenantId: input.scope.tenantId,
|
|
83
|
+
...channelOrgScopeWhere(input.scope.organizationId),
|
|
84
|
+
deletedAt: null,
|
|
85
|
+
},
|
|
86
|
+
undefined,
|
|
87
|
+
dscope,
|
|
88
|
+
)
|
|
89
|
+
if (!channel) {
|
|
90
|
+
return { status: 'noop', reason: 'channel not found' }
|
|
91
|
+
}
|
|
92
|
+
if (channel.userId != null) {
|
|
93
|
+
return {
|
|
94
|
+
status: 'not_tenant_wide',
|
|
95
|
+
reason: 'channel is a per-user channel; use the owner delete path',
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Tear down provider-side push delivery before the soft-delete, while
|
|
100
|
+
// credentials are still resolvable. Keyed on the channel's OWN org (push
|
|
101
|
+
// channels store credentials at `organization_id = tenantId`; shared inboxes
|
|
102
|
+
// at their pinned org). Best-effort — failures are logged inside
|
|
103
|
+
// pushUnregister and never re-raised.
|
|
104
|
+
if (channel.credentialsRef) {
|
|
105
|
+
const channelOrganizationId = channel.organizationId ?? input.scope.tenantId
|
|
106
|
+
try {
|
|
107
|
+
await pushUnregister({
|
|
108
|
+
container: ctx.container,
|
|
109
|
+
scope: {
|
|
110
|
+
tenantId: input.scope.tenantId,
|
|
111
|
+
organizationId: channelOrganizationId,
|
|
112
|
+
userId: channel.userId ?? null,
|
|
113
|
+
},
|
|
114
|
+
input: { channelId: channel.id },
|
|
115
|
+
})
|
|
116
|
+
} catch (err) {
|
|
117
|
+
logger.warn('admin-delete-channel push unregister failed', { channelId: channel.id, err })
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
channel.deletedAt = new Date()
|
|
122
|
+
channel.isPrimary = false
|
|
123
|
+
await em.flush()
|
|
124
|
+
|
|
125
|
+
// Emit AFTER flush so subscribers observe the committed soft-delete. Same
|
|
126
|
+
// event/payload shape as delete-channel so workflows/audit observe the full
|
|
127
|
+
// channel lifecycle. Persistent so the delivery can retry on failure.
|
|
128
|
+
await emitCommunicationChannelsEvent(
|
|
129
|
+
'communication_channels.channel.deleted',
|
|
130
|
+
{
|
|
131
|
+
channelId: channel.id,
|
|
132
|
+
userId: channel.userId ?? null,
|
|
133
|
+
providerKey: channel.providerKey,
|
|
134
|
+
channelType: channel.channelType,
|
|
135
|
+
tenantId: input.scope.tenantId,
|
|
136
|
+
organizationId: channel.organizationId ?? null,
|
|
137
|
+
},
|
|
138
|
+
{ persistent: true },
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
status: 'deleted',
|
|
143
|
+
channelId: channel.id,
|
|
144
|
+
undo: { channelId: channel.id, tenantId: channel.tenantId },
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
// Persist the undo snapshot into the action log. Without this, the command bus
|
|
148
|
+
// mints an undo token (so the UI offers "Undo") but the snapshot returned from
|
|
149
|
+
// execute() is never stored, and undo() (which clears deleted_at) would no-op.
|
|
150
|
+
async buildLog({ input, result }) {
|
|
151
|
+
if (result.status !== 'deleted') return null
|
|
152
|
+
return {
|
|
153
|
+
resourceKind: 'communication_channels.channel',
|
|
154
|
+
resourceId: result.channelId,
|
|
155
|
+
tenantId: result.undo.tenantId ?? input.scope.tenantId,
|
|
156
|
+
organizationId: input.scope.organizationId ?? null,
|
|
157
|
+
payload: { undo: result.undo },
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
async undo({ ctx, logEntry }) {
|
|
161
|
+
const snapshot = extractSnapshotFromLog(logEntry)
|
|
162
|
+
if (!snapshot || !snapshot.tenantId) return
|
|
163
|
+
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
164
|
+
// Re-find WITHOUT the `deletedAt: null` filter — the row we are restoring is
|
|
165
|
+
// soft-deleted. Tenant-scoped to avoid cross-tenant resolution by bare id.
|
|
166
|
+
const channel = await findOneWithDecryption(
|
|
167
|
+
em,
|
|
168
|
+
CommunicationChannel,
|
|
169
|
+
{ id: snapshot.channelId, tenantId: snapshot.tenantId },
|
|
170
|
+
undefined,
|
|
171
|
+
{ tenantId: snapshot.tenantId, organizationId: null },
|
|
172
|
+
)
|
|
173
|
+
if (!channel) return
|
|
174
|
+
channel.deletedAt = null
|
|
175
|
+
await em.flush()
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Defensive shape-validator for the delete snapshot — mirrors the helper in
|
|
181
|
+
* `delete-channel.ts` so tests can construct snapshots directly.
|
|
182
|
+
*/
|
|
183
|
+
export function extractUndoPayload(value: unknown): AdminDeleteChannelUndoSnapshot | null {
|
|
184
|
+
if (!value || typeof value !== 'object') return null
|
|
185
|
+
const candidate = (value as { undo?: unknown; channelId?: unknown }).undo ?? value
|
|
186
|
+
if (!candidate || typeof candidate !== 'object') return null
|
|
187
|
+
const obj = candidate as Record<string, unknown>
|
|
188
|
+
if (typeof obj.channelId !== 'string') return null
|
|
189
|
+
return {
|
|
190
|
+
channelId: obj.channelId,
|
|
191
|
+
tenantId: typeof obj.tenantId === 'string' ? obj.tenantId : undefined,
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function extractSnapshotFromLog(logEntry: unknown): AdminDeleteChannelUndoSnapshot | null {
|
|
196
|
+
const undo = extractSharedUndoPayload<AdminDeleteChannelUndoSnapshot>((logEntry ?? null) as never)
|
|
197
|
+
if (undo) return extractUndoPayload(undo)
|
|
198
|
+
return extractUndoPayload(logEntry)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
registerCommand(adminDeleteChannelCommand)
|
|
202
|
+
|
|
203
|
+
export default adminDeleteChannelCommand
|
|
@@ -15,7 +15,12 @@ const connectCredentialChannelSchema = z.object({
|
|
|
15
15
|
credentials: z.record(z.string(), z.unknown()),
|
|
16
16
|
/** Optional polling interval (seconds) — set when adapter is polling-based. */
|
|
17
17
|
pollIntervalSeconds: z.number().int().positive().max(86_400).optional(),
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Connecting user. `null` for tenant-wide (admin-connected) channels whose
|
|
20
|
+
* provider declares `channelScope: 'tenant'` (push: FCM/APNs/Expo) — the
|
|
21
|
+
* resulting channel + credentials are stored with `user_id = NULL`.
|
|
22
|
+
*/
|
|
23
|
+
userId: z.string().uuid().nullable(),
|
|
19
24
|
scope: z.object({
|
|
20
25
|
tenantId: z.string().uuid(),
|
|
21
26
|
organizationId: z.string().uuid().nullable(),
|
|
@@ -26,9 +31,10 @@ export type ConnectCredentialChannelInput = z.infer<typeof connectCredentialChan
|
|
|
26
31
|
|
|
27
32
|
export type ConnectCredentialChannelResult =
|
|
28
33
|
| { status: 'connected'; channelId: string; externalIdentifier: string | null }
|
|
29
|
-
| { status: 'validation_failed'; errors: Record<string, string> }
|
|
34
|
+
| { status: 'validation_failed'; errors: Record<string, string>; errorCodes?: Record<string, string> }
|
|
30
35
|
| { status: 'no_adapter'; reason: string }
|
|
31
36
|
| { status: 'duplicate_mailbox'; externalIdentifier: string; existingProviderKey: string }
|
|
37
|
+
| { status: 'wrong_scope_for_route'; providerKey: string }
|
|
32
38
|
|
|
33
39
|
export const COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID =
|
|
34
40
|
'communication_channels.channel.connect_credential'
|
|
@@ -42,21 +48,26 @@ type CredentialsServiceLike = {
|
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
|
-
* Connect a
|
|
51
|
+
* Connect a credential-based channel (IMAP, push providers, and future basic-auth
|
|
52
|
+
* providers).
|
|
46
53
|
*
|
|
47
54
|
* Flow:
|
|
48
55
|
* 1. Resolve the adapter for `providerKey`.
|
|
49
|
-
* 2.
|
|
56
|
+
* 2. Decide scope: `adapter.channelScope === 'tenant'` → the channel + credentials
|
|
57
|
+
* are stored with `user_id = NULL` (one shared channel per tenant, e.g. push
|
|
58
|
+
* FCM/APNs/Expo); otherwise they are stored under the connecting `userId`.
|
|
59
|
+
* 3. Call `adapter.validateCredentials?` — adapters that don't implement it
|
|
50
60
|
* are accepted optimistically (the hub trusts the adapter to fail on first
|
|
51
61
|
* use). Adapters with the method return field-level errors via Zod-like
|
|
52
62
|
* `{ ok: false, errors: { fieldName: 'message' } }`; we forward those to
|
|
53
63
|
* the caller for `createCrudFormError`.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
64
|
+
* 4. Persist the credentials encrypted via `integrationCredentialsService.save?` if available.
|
|
65
|
+
* 5. Create the `CommunicationChannel` row with the effective `userId`,
|
|
56
66
|
* `status = 'connected'`, and the adapter's declared capabilities snapshot.
|
|
57
67
|
*
|
|
58
|
-
* The route
|
|
59
|
-
*
|
|
68
|
+
* The per-user route binds `userId` from the authenticated session; the tenant
|
|
69
|
+
* route passes `userId: null`. Either way the command re-derives the effective
|
|
70
|
+
* scope from the adapter, so a tenant provider is never stamped per-user.
|
|
60
71
|
*/
|
|
61
72
|
const connectCredentialChannelCommand: CommandHandler<
|
|
62
73
|
ConnectCredentialChannelInput,
|
|
@@ -75,6 +86,23 @@ const connectCredentialChannelCommand: CommandHandler<
|
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
|
|
89
|
+
// Tenant-wide providers (push: FCM/APNs/Expo) store one shared channel +
|
|
90
|
+
// credential row per tenant with `user_id = NULL`; per-user providers
|
|
91
|
+
// (Gmail/IMAP) keep the connecting user's id.
|
|
92
|
+
//
|
|
93
|
+
// SECURITY: a tenant-scoped provider must only be connected through the
|
|
94
|
+
// admin-gated tenant route (which passes `userId: null`). If it arrives with
|
|
95
|
+
// a non-null `userId` it came from the per-user route (feature
|
|
96
|
+
// `connect_user_channel`, granted to manager/employee) — refuse rather than
|
|
97
|
+
// silently minting a privileged tenant-wide channel that bypasses the
|
|
98
|
+
// admin-only `connect_tenant_channel` gate. The tenant route passes
|
|
99
|
+
// `userId: null`, so legitimate tenant connects still pass.
|
|
100
|
+
const tenantScoped = adapter.channelScope === 'tenant'
|
|
101
|
+
if (tenantScoped && input.userId !== null) {
|
|
102
|
+
return { status: 'wrong_scope_for_route', providerKey: input.providerKey }
|
|
103
|
+
}
|
|
104
|
+
const effectiveUserId = tenantScoped ? null : input.userId
|
|
105
|
+
|
|
78
106
|
// Optional credential validation.
|
|
79
107
|
if (typeof adapter.validateCredentials === 'function') {
|
|
80
108
|
const validation = await adapter.validateCredentials({
|
|
@@ -89,6 +117,7 @@ const connectCredentialChannelCommand: CommandHandler<
|
|
|
89
117
|
return {
|
|
90
118
|
status: 'validation_failed',
|
|
91
119
|
errors: validation.errors ?? { _form: 'Credential validation failed' },
|
|
120
|
+
errorCodes: validation.errorCodes,
|
|
92
121
|
}
|
|
93
122
|
}
|
|
94
123
|
}
|
|
@@ -106,11 +135,24 @@ const connectCredentialChannelCommand: CommandHandler<
|
|
|
106
135
|
//
|
|
107
136
|
// `scope.userId` is set so the credentials service writes a per-user row.
|
|
108
137
|
// Without this, two users on the same tenant share one credentials row
|
|
109
|
-
// (see review R2-C1 / N1, 2026-05-26).
|
|
138
|
+
// (see review R2-C1 / N1, 2026-05-26). Tenant-wide providers pass `null`
|
|
139
|
+
// here so the service writes/reads the shared `user_id = NULL` row (same
|
|
140
|
+
// pattern as Stripe/Akeneo tenant-wide credentials).
|
|
141
|
+
//
|
|
142
|
+
// `organizationId` for tenant scope is pinned to `tenantId` (not the
|
|
143
|
+
// connecting admin's selected org). The channel dedup/heal is org-agnostic
|
|
144
|
+
// (index + heal key ignore org) and the channel row is stored with
|
|
145
|
+
// `organization_id = NULL`, so push-delivery resolves credentials at
|
|
146
|
+
// `channel.organizationId ?? tenantId` = `tenantId`. Pinning the write to the
|
|
147
|
+
// same key keeps a cross-org reconnect (e.g. key rotation from another org)
|
|
148
|
+
// overwriting the one credential row the delivery path actually reads, rather
|
|
149
|
+
// than orphaning it under a per-org key.
|
|
110
150
|
const credentialsScope = {
|
|
111
151
|
tenantId: input.scope.tenantId,
|
|
112
|
-
organizationId:
|
|
113
|
-
|
|
152
|
+
organizationId: tenantScoped
|
|
153
|
+
? input.scope.tenantId
|
|
154
|
+
: input.scope.organizationId ?? input.scope.tenantId,
|
|
155
|
+
userId: effectiveUserId,
|
|
114
156
|
}
|
|
115
157
|
let credentialsRefId: string | null = null
|
|
116
158
|
let credentialsPersisted = false
|
|
@@ -118,7 +160,7 @@ const connectCredentialChannelCommand: CommandHandler<
|
|
|
118
160
|
try {
|
|
119
161
|
await credentialsService.save(
|
|
120
162
|
`channel_${input.providerKey}`,
|
|
121
|
-
{ ...input.credentials, userId:
|
|
163
|
+
{ ...input.credentials, ...(effectiveUserId ? { userId: effectiveUserId } : {}) },
|
|
122
164
|
credentialsScope,
|
|
123
165
|
)
|
|
124
166
|
credentialsPersisted = true
|
|
@@ -182,8 +224,14 @@ const connectCredentialChannelCommand: CommandHandler<
|
|
|
182
224
|
displayName: input.displayName,
|
|
183
225
|
externalIdentifier,
|
|
184
226
|
credentialsRefId,
|
|
185
|
-
userId:
|
|
186
|
-
|
|
227
|
+
userId: effectiveUserId,
|
|
228
|
+
// Tenant-wide channels store `organization_id = NULL` (truly tenant-scoped,
|
|
229
|
+
// matching the entity's `user_id NULL` semantics) so the org-agnostic heal
|
|
230
|
+
// key stays stable across orgs and delivery reads creds at `?? tenantId`.
|
|
231
|
+
scope: {
|
|
232
|
+
tenantId: input.scope.tenantId,
|
|
233
|
+
organizationId: tenantScoped ? null : input.scope.organizationId ?? null,
|
|
234
|
+
},
|
|
187
235
|
pollIntervalSeconds: input.pollIntervalSeconds,
|
|
188
236
|
})
|
|
189
237
|
} catch (err) {
|
|
@@ -205,7 +253,7 @@ const connectCredentialChannelCommand: CommandHandler<
|
|
|
205
253
|
// covers the channel until the operator clicks "Re-register push".
|
|
206
254
|
// Imported lazily to avoid a circular module load (push-register reads
|
|
207
255
|
// the channel adapter registry, which is initialised after this module).
|
|
208
|
-
if (credentialsAvailable && input.providerKey === 'gmail') {
|
|
256
|
+
if (credentialsAvailable && input.providerKey === 'gmail' && input.userId) {
|
|
209
257
|
const adapterSupportsPush =
|
|
210
258
|
typeof adapter.registerPush === 'function' && typeof adapter.unregisterPush === 'function'
|
|
211
259
|
const organizationId = input.scope.organizationId
|
|
@@ -282,9 +282,17 @@ const deliverOutboundMessageCommand: CommandHandler<
|
|
|
282
282
|
// Per-user credentials scope: pass `channel.userId` so the credentials
|
|
283
283
|
// service returns this user's row, not whoever connected last. See
|
|
284
284
|
// review R2-C1 / N1 (2026-05-26).
|
|
285
|
+
//
|
|
286
|
+
// Key the org on the CHANNEL's own org, not the message's org. Tenant-wide
|
|
287
|
+
// channels store `organization_id = NULL` and their credentials live at
|
|
288
|
+
// `organization_id = tenantId` (see connect-credential-channel.ts), so
|
|
289
|
+
// `channel.organizationId ?? tenantId` matches the write key for both
|
|
290
|
+
// org-scoped and tenant-wide channels. Keying on the message org would
|
|
291
|
+
// resolve `{}` the moment a tenant-wide provider delivers from a message
|
|
292
|
+
// that carries a non-null org.
|
|
285
293
|
const credentialsScope = {
|
|
286
294
|
tenantId: input.scope.tenantId,
|
|
287
|
-
organizationId:
|
|
295
|
+
organizationId: channel.organizationId ?? input.scope.tenantId,
|
|
288
296
|
userId: channel.userId ?? null,
|
|
289
297
|
}
|
|
290
298
|
let credentials: Record<string, unknown> = {}
|
|
@@ -406,7 +414,7 @@ const deliverOutboundMessageCommand: CommandHandler<
|
|
|
406
414
|
credentials,
|
|
407
415
|
scope: {
|
|
408
416
|
tenantId: input.scope.tenantId,
|
|
409
|
-
organizationId:
|
|
417
|
+
organizationId: channel.organizationId ?? input.scope.tenantId,
|
|
410
418
|
},
|
|
411
419
|
metadata: converted.metadata,
|
|
412
420
|
})
|
|
@@ -113,9 +113,15 @@ export async function pushRegister(params: {
|
|
|
113
113
|
} catch {
|
|
114
114
|
credentialsService = null
|
|
115
115
|
}
|
|
116
|
+
// Resolve credentials at the channel's OWN org, not the caller's scope org.
|
|
117
|
+
// Tenant-wide channels store `organization_id = NULL` and their credentials
|
|
118
|
+
// live at `organization_id = tenantId` (see connect-credential-channel.ts).
|
|
119
|
+
// Keying on `channel.organizationId ?? tenantId` matches the write key for
|
|
120
|
+
// both org-scoped (per-user) and tenant-wide channels; keying on the caller's
|
|
121
|
+
// scope org would silently resolve `{}` for a tenant-wide channel.
|
|
116
122
|
const credentialsScope = {
|
|
117
123
|
tenantId: scope.tenantId,
|
|
118
|
-
organizationId: scope.
|
|
124
|
+
organizationId: channel.organizationId ?? scope.tenantId,
|
|
119
125
|
userId: channel.userId ?? null,
|
|
120
126
|
}
|
|
121
127
|
let credentials: Record<string, unknown> = {}
|
|
@@ -3,6 +3,7 @@ import type { AwilixContainer } from 'awilix'
|
|
|
3
3
|
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
4
4
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
5
5
|
import { CommunicationChannel } from '../data/entities'
|
|
6
|
+
import { channelOrgScopeWhere } from '../lib/access-control'
|
|
6
7
|
import { getChannelAdapterRegistry } from '../lib/adapter-registry-singleton'
|
|
7
8
|
import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
|
|
8
9
|
import { POLLING_ONLY_DEFAULT_INTERVAL_SECONDS } from '../lib/connect-channel'
|
|
@@ -64,7 +65,14 @@ export async function pushUnregister(params: {
|
|
|
64
65
|
const { container, scope } = params
|
|
65
66
|
|
|
66
67
|
const em = (container.resolve('em') as EntityManager).fork()
|
|
67
|
-
|
|
68
|
+
// The channel ROW and the credentials store use DIFFERENT orgs. A tenant-wide push channel is
|
|
69
|
+
// stored with `organization_id = NULL` (its encrypted columns are keyed at org=null) even though
|
|
70
|
+
// its credentials are pinned at `organization_id = tenantId`. `scope.organizationId` here is the
|
|
71
|
+
// credentials/delivery org (tenantId for tenant-wide) — so it must NOT be used as an exact row
|
|
72
|
+
// filter (it would never match a null-org row) nor as the row decryption org. Look the row up
|
|
73
|
+
// null-aware and decrypt at the row's own org (null column ⇒ null fallback); credential resolution
|
|
74
|
+
// below keeps using the credentials org.
|
|
75
|
+
const dscope = { tenantId: scope.tenantId, organizationId: null }
|
|
68
76
|
|
|
69
77
|
const channel = await findOneWithDecryption(
|
|
70
78
|
em,
|
|
@@ -72,7 +80,7 @@ export async function pushUnregister(params: {
|
|
|
72
80
|
{
|
|
73
81
|
id: input.channelId,
|
|
74
82
|
tenantId: scope.tenantId,
|
|
75
|
-
|
|
83
|
+
...channelOrgScopeWhere(scope.organizationId),
|
|
76
84
|
},
|
|
77
85
|
undefined,
|
|
78
86
|
dscope,
|
|
@@ -98,9 +106,12 @@ export async function pushUnregister(params: {
|
|
|
98
106
|
} catch {
|
|
99
107
|
credentialsService = null
|
|
100
108
|
}
|
|
109
|
+
// Resolve credentials at the channel's OWN org (tenant-wide channels store
|
|
110
|
+
// their credentials at `organization_id = tenantId`; see push-register.ts /
|
|
111
|
+
// connect-credential-channel.ts), never the caller's scope org.
|
|
101
112
|
const credentialsScope = {
|
|
102
113
|
tenantId: scope.tenantId,
|
|
103
|
-
organizationId: scope.
|
|
114
|
+
organizationId: channel.organizationId ?? scope.tenantId,
|
|
104
115
|
userId: channel.userId ?? null,
|
|
105
116
|
}
|
|
106
117
|
let credentials: Record<string, unknown> = {}
|
|
@@ -63,6 +63,16 @@ export type CommunicationChannelStatus =
|
|
|
63
63
|
expression:
|
|
64
64
|
`create unique index "communication_channels_user_provider_external_uq" on "communication_channels" ("tenant_id", "user_id", "provider_key", "external_identifier") where "deleted_at" is null and "user_id" is not null and "external_identifier" is not null`,
|
|
65
65
|
})
|
|
66
|
+
// One tenant-wide push channel per (tenant, provider): push providers (FCM/APNs/
|
|
67
|
+
// Expo) have no `external_identifier` and `user_id IS NULL`, so the mailbox
|
|
68
|
+
// unique index above does not cover them. This keeps an admin reconnect healing
|
|
69
|
+
// the single shared row (see `createConnectedChannelRow`) instead of inserting
|
|
70
|
+
// duplicates the fan-out would silently ignore.
|
|
71
|
+
@Index({
|
|
72
|
+
name: 'communication_channels_tenant_push_provider_uq',
|
|
73
|
+
expression:
|
|
74
|
+
`create unique index "communication_channels_tenant_push_provider_uq" on "communication_channels" ("tenant_id", "provider_key") where "channel_type" = 'push' and "user_id" is null and "deleted_at" is null`,
|
|
75
|
+
})
|
|
66
76
|
export class CommunicationChannel {
|
|
67
77
|
[OptionalProps]?:
|
|
68
78
|
| 'createdAt'
|
|
@@ -74,6 +74,7 @@ export const validateCredentialsInputSchema = z.object({
|
|
|
74
74
|
export const validateCredentialsResultSchema = z.object({
|
|
75
75
|
ok: z.boolean(),
|
|
76
76
|
errors: z.record(z.string(), z.string()).optional(),
|
|
77
|
+
errorCodes: z.record(z.string(), z.string()).optional(),
|
|
77
78
|
})
|
|
78
79
|
|
|
79
80
|
export const refreshCredentialsInputSchema = z.object({
|
|
@@ -125,6 +125,39 @@
|
|
|
125
125
|
"communication_channels.profile.tableTitle": "Ihre Kanäle",
|
|
126
126
|
"communication_channels.profile.title": "Meine Kommunikationskanäle",
|
|
127
127
|
"communication_channels.push.button.reregister": "Push neu registrieren",
|
|
128
|
+
"communication_channels.push.connect.blocked": "Verbindung durch Validierung blockiert",
|
|
129
|
+
"communication_channels.push.connect.button.apns": "APNs verbinden",
|
|
130
|
+
"communication_channels.push.connect.button.expo": "Expo verbinden",
|
|
131
|
+
"communication_channels.push.connect.button.fcm": "FCM verbinden",
|
|
132
|
+
"communication_channels.push.connect.cancel": "Abbrechen",
|
|
133
|
+
"communication_channels.push.connect.connected": "Push-Anbieter verbunden.",
|
|
134
|
+
"communication_channels.push.connect.connecting": "Verbinden…",
|
|
135
|
+
"communication_channels.push.connect.description": "Dies ist ein gemeinsamer, mandantenweiter Kanal — er bedient die Geräte aller Benutzer in diesem Arbeitsbereich.",
|
|
136
|
+
"communication_channels.push.connect.displayName": "Anzeigename",
|
|
137
|
+
"communication_channels.push.connect.errors.fields.invalid": "Dieser Wert ist ungültig.",
|
|
138
|
+
"communication_channels.push.connect.errors.fields.invalid_bundle_id": "Eine Bundle-ID sieht aus wie com.example.app.",
|
|
139
|
+
"communication_channels.push.connect.errors.fields.invalid_json": "Das ist kein gültiges JSON. Fügen Sie die Dienstkontodatei genau so ein, wie Firebase sie erzeugt hat.",
|
|
140
|
+
"communication_channels.push.connect.errors.fields.invalid_key_id": "Eine APNs-Key-ID besteht aus 10 alphanumerischen Zeichen.",
|
|
141
|
+
"communication_channels.push.connect.errors.fields.invalid_p8": "Das ist kein lesbarer APNs-.p8-Signaturschlüssel. Fügen Sie die vollständige Datei einschließlich der BEGIN- und END-Zeilen ein.",
|
|
142
|
+
"communication_channels.push.connect.errors.fields.invalid_team_id": "Eine Apple-Team-ID besteht aus 10 alphanumerischen Zeichen.",
|
|
143
|
+
"communication_channels.push.connect.errors.fields.missing_fields": "In der Dienstkonto-JSON fehlen erforderliche Felder (project_id, client_email, private_key).",
|
|
144
|
+
"communication_channels.push.connect.errors.fields.required": "Dieses Feld ist erforderlich.",
|
|
145
|
+
"communication_channels.push.connect.errors.mailbox_already_connected": "Dieser Kanal ist bereits über einen anderen Anbieter verbunden. Trennen Sie ihn dort, bevor Sie ihn erneut verbinden.",
|
|
146
|
+
"communication_channels.push.connect.errors.provider_not_tenant_scoped": "Dieser Anbieter verbindet benutzerspezifische Kanäle; verwenden Sie stattdessen den persönlichen Kanalverbindungsvorgang.",
|
|
147
|
+
"communication_channels.push.connect.errors.wrong_scope_for_route": "Unerwarteter Kanalbereich. Bitte versuchen Sie es erneut oder wenden Sie sich an den Support.",
|
|
148
|
+
"communication_channels.push.connect.failed": "Push-Anbieter konnte nicht verbunden werden.",
|
|
149
|
+
"communication_channels.push.connect.fields.apns.bundleId": "Bundle-ID",
|
|
150
|
+
"communication_channels.push.connect.fields.apns.keyId": "Key-ID",
|
|
151
|
+
"communication_channels.push.connect.fields.apns.p8Key": "APNs .p8-Schlüssel",
|
|
152
|
+
"communication_channels.push.connect.fields.apns.production": "Produktionsumgebung",
|
|
153
|
+
"communication_channels.push.connect.fields.apns.teamId": "Team-ID",
|
|
154
|
+
"communication_channels.push.connect.fields.expo.accessToken": "Zugriffstoken (optional)",
|
|
155
|
+
"communication_channels.push.connect.fields.fcm.appName": "App-Name (optional)",
|
|
156
|
+
"communication_channels.push.connect.fields.fcm.serviceAccountJson": "Dienstkonto-JSON",
|
|
157
|
+
"communication_channels.push.connect.save": "Verbinden",
|
|
158
|
+
"communication_channels.push.connect.title.apns": "Apple Push Notification service verbinden",
|
|
159
|
+
"communication_channels.push.connect.title.expo": "Expo Push verbinden",
|
|
160
|
+
"communication_channels.push.connect.title.fcm": "Firebase Cloud Messaging verbinden",
|
|
128
161
|
"communication_channels.push.flash.registerFailed": "Push-Registrierung fehlgeschlagen",
|
|
129
162
|
"communication_channels.push.status.active": "Push aktiv",
|
|
130
163
|
"communication_channels.push.status.failed": "Push fehlgeschlagen — nutze Polling",
|
|
@@ -140,5 +173,6 @@
|
|
|
140
173
|
"communication_channels.status.disconnected": "Getrennt",
|
|
141
174
|
"communication_channels.status.error": "Fehler",
|
|
142
175
|
"communication_channels.status.inactive": "Inaktiv",
|
|
143
|
-
"communication_channels.status.requiresReauth": "Erneutes Verbinden erforderlich"
|
|
176
|
+
"communication_channels.status.requiresReauth": "Erneutes Verbinden erforderlich",
|
|
177
|
+
"notifications.categories.communication_channels": "Kommunikation"
|
|
144
178
|
}
|
|
@@ -125,6 +125,39 @@
|
|
|
125
125
|
"communication_channels.profile.tableTitle": "Your channels",
|
|
126
126
|
"communication_channels.profile.title": "My communication channels",
|
|
127
127
|
"communication_channels.push.button.reregister": "Re-register push",
|
|
128
|
+
"communication_channels.push.connect.blocked": "Connection blocked by validation",
|
|
129
|
+
"communication_channels.push.connect.button.apns": "Connect APNs",
|
|
130
|
+
"communication_channels.push.connect.button.expo": "Connect Expo",
|
|
131
|
+
"communication_channels.push.connect.button.fcm": "Connect FCM",
|
|
132
|
+
"communication_channels.push.connect.cancel": "Cancel",
|
|
133
|
+
"communication_channels.push.connect.connected": "Push provider connected.",
|
|
134
|
+
"communication_channels.push.connect.connecting": "Connecting…",
|
|
135
|
+
"communication_channels.push.connect.description": "This is a shared, tenant-wide channel — every user's devices in this workspace are served by it.",
|
|
136
|
+
"communication_channels.push.connect.displayName": "Display name",
|
|
137
|
+
"communication_channels.push.connect.errors.fields.invalid": "This value is invalid.",
|
|
138
|
+
"communication_channels.push.connect.errors.fields.invalid_bundle_id": "A Bundle ID looks like com.example.app.",
|
|
139
|
+
"communication_channels.push.connect.errors.fields.invalid_json": "This is not valid JSON. Paste the service account file exactly as Firebase generated it.",
|
|
140
|
+
"communication_channels.push.connect.errors.fields.invalid_key_id": "An APNs Key ID is 10 alphanumeric characters.",
|
|
141
|
+
"communication_channels.push.connect.errors.fields.invalid_p8": "This is not a readable APNs .p8 signing key. Paste the whole file including the BEGIN and END lines.",
|
|
142
|
+
"communication_channels.push.connect.errors.fields.invalid_team_id": "An Apple Team ID is 10 alphanumeric characters.",
|
|
143
|
+
"communication_channels.push.connect.errors.fields.missing_fields": "The service account JSON is missing required fields (project_id, client_email, private_key).",
|
|
144
|
+
"communication_channels.push.connect.errors.fields.required": "This field is required.",
|
|
145
|
+
"communication_channels.push.connect.errors.mailbox_already_connected": "This channel is already connected via another provider. Disconnect it there before reconnecting.",
|
|
146
|
+
"communication_channels.push.connect.errors.provider_not_tenant_scoped": "This provider connects per-user channels; use the personal channel connect flow instead.",
|
|
147
|
+
"communication_channels.push.connect.errors.wrong_scope_for_route": "Unexpected channel scope. Please try again or contact support.",
|
|
148
|
+
"communication_channels.push.connect.failed": "Could not connect push provider.",
|
|
149
|
+
"communication_channels.push.connect.fields.apns.bundleId": "Bundle ID",
|
|
150
|
+
"communication_channels.push.connect.fields.apns.keyId": "Key ID",
|
|
151
|
+
"communication_channels.push.connect.fields.apns.p8Key": "APNs .p8 key",
|
|
152
|
+
"communication_channels.push.connect.fields.apns.production": "Production environment",
|
|
153
|
+
"communication_channels.push.connect.fields.apns.teamId": "Team ID",
|
|
154
|
+
"communication_channels.push.connect.fields.expo.accessToken": "Access token (optional)",
|
|
155
|
+
"communication_channels.push.connect.fields.fcm.appName": "App name (optional)",
|
|
156
|
+
"communication_channels.push.connect.fields.fcm.serviceAccountJson": "Service Account JSON",
|
|
157
|
+
"communication_channels.push.connect.save": "Connect",
|
|
158
|
+
"communication_channels.push.connect.title.apns": "Connect Apple Push Notification service",
|
|
159
|
+
"communication_channels.push.connect.title.expo": "Connect Expo Push",
|
|
160
|
+
"communication_channels.push.connect.title.fcm": "Connect Firebase Cloud Messaging",
|
|
128
161
|
"communication_channels.push.flash.registerFailed": "Failed to register push",
|
|
129
162
|
"communication_channels.push.status.active": "Push active",
|
|
130
163
|
"communication_channels.push.status.failed": "Push failed — using polling",
|
|
@@ -140,5 +173,6 @@
|
|
|
140
173
|
"communication_channels.status.disconnected": "Disconnected",
|
|
141
174
|
"communication_channels.status.error": "Error",
|
|
142
175
|
"communication_channels.status.inactive": "Inactive",
|
|
143
|
-
"communication_channels.status.requiresReauth": "Needs reconnection"
|
|
176
|
+
"communication_channels.status.requiresReauth": "Needs reconnection",
|
|
177
|
+
"notifications.categories.communication_channels": "Communication"
|
|
144
178
|
}
|
|
@@ -125,6 +125,39 @@
|
|
|
125
125
|
"communication_channels.profile.tableTitle": "Tus canales",
|
|
126
126
|
"communication_channels.profile.title": "Mis canales de comunicación",
|
|
127
127
|
"communication_channels.push.button.reregister": "Re-registrar push",
|
|
128
|
+
"communication_channels.push.connect.blocked": "Conexión bloqueada por la validación",
|
|
129
|
+
"communication_channels.push.connect.button.apns": "Conectar APNs",
|
|
130
|
+
"communication_channels.push.connect.button.expo": "Conectar Expo",
|
|
131
|
+
"communication_channels.push.connect.button.fcm": "Conectar FCM",
|
|
132
|
+
"communication_channels.push.connect.cancel": "Cancelar",
|
|
133
|
+
"communication_channels.push.connect.connected": "Proveedor push conectado.",
|
|
134
|
+
"communication_channels.push.connect.connecting": "Conectando…",
|
|
135
|
+
"communication_channels.push.connect.description": "Este es un canal compartido para todo el inquilino: atiende los dispositivos de todos los usuarios de este espacio.",
|
|
136
|
+
"communication_channels.push.connect.displayName": "Nombre visible",
|
|
137
|
+
"communication_channels.push.connect.errors.fields.invalid": "Este valor no es válido.",
|
|
138
|
+
"communication_channels.push.connect.errors.fields.invalid_bundle_id": "Un Bundle ID se parece a com.example.app.",
|
|
139
|
+
"communication_channels.push.connect.errors.fields.invalid_json": "Esto no es JSON válido. Pega el archivo de la cuenta de servicio tal y como lo generó Firebase.",
|
|
140
|
+
"communication_channels.push.connect.errors.fields.invalid_key_id": "Un Key ID de APNs tiene 10 caracteres alfanuméricos.",
|
|
141
|
+
"communication_channels.push.connect.errors.fields.invalid_p8": "Esta no es una clave de firma .p8 de APNs legible. Pega el archivo completo, incluidas las líneas BEGIN y END.",
|
|
142
|
+
"communication_channels.push.connect.errors.fields.invalid_team_id": "Un Team ID de Apple tiene 10 caracteres alfanuméricos.",
|
|
143
|
+
"communication_channels.push.connect.errors.fields.missing_fields": "Al JSON de la cuenta de servicio le faltan campos obligatorios (project_id, client_email, private_key).",
|
|
144
|
+
"communication_channels.push.connect.errors.fields.required": "Este campo es obligatorio.",
|
|
145
|
+
"communication_channels.push.connect.errors.mailbox_already_connected": "Este canal ya está conectado a través de otro proveedor. Desconéctalo allí antes de volver a conectarlo.",
|
|
146
|
+
"communication_channels.push.connect.errors.provider_not_tenant_scoped": "Este proveedor conecta canales por usuario; utiliza en su lugar el flujo de conexión de canal personal.",
|
|
147
|
+
"communication_channels.push.connect.errors.wrong_scope_for_route": "Ámbito de canal inesperado. Vuelve a intentarlo o contacta con soporte.",
|
|
148
|
+
"communication_channels.push.connect.failed": "No se pudo conectar el proveedor push.",
|
|
149
|
+
"communication_channels.push.connect.fields.apns.bundleId": "Bundle ID",
|
|
150
|
+
"communication_channels.push.connect.fields.apns.keyId": "Key ID",
|
|
151
|
+
"communication_channels.push.connect.fields.apns.p8Key": "Clave .p8 de APNs",
|
|
152
|
+
"communication_channels.push.connect.fields.apns.production": "Entorno de producción",
|
|
153
|
+
"communication_channels.push.connect.fields.apns.teamId": "Team ID",
|
|
154
|
+
"communication_channels.push.connect.fields.expo.accessToken": "Token de acceso (opcional)",
|
|
155
|
+
"communication_channels.push.connect.fields.fcm.appName": "Nombre de la app (opcional)",
|
|
156
|
+
"communication_channels.push.connect.fields.fcm.serviceAccountJson": "JSON de cuenta de servicio",
|
|
157
|
+
"communication_channels.push.connect.save": "Conectar",
|
|
158
|
+
"communication_channels.push.connect.title.apns": "Conectar Apple Push Notification service",
|
|
159
|
+
"communication_channels.push.connect.title.expo": "Conectar Expo Push",
|
|
160
|
+
"communication_channels.push.connect.title.fcm": "Conectar Firebase Cloud Messaging",
|
|
128
161
|
"communication_channels.push.flash.registerFailed": "No se pudo registrar push",
|
|
129
162
|
"communication_channels.push.status.active": "Push activo",
|
|
130
163
|
"communication_channels.push.status.failed": "Push fallido — usando sondeo",
|
|
@@ -140,5 +173,6 @@
|
|
|
140
173
|
"communication_channels.status.disconnected": "Desconectado",
|
|
141
174
|
"communication_channels.status.error": "Error",
|
|
142
175
|
"communication_channels.status.inactive": "Inactivo",
|
|
143
|
-
"communication_channels.status.requiresReauth": "Necesita volver a conectarse"
|
|
176
|
+
"communication_channels.status.requiresReauth": "Necesita volver a conectarse",
|
|
177
|
+
"notifications.categories.communication_channels": "Comunicación"
|
|
144
178
|
}
|