@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
|
@@ -125,6 +125,39 @@
|
|
|
125
125
|
"communication_channels.profile.tableTitle": "내 채널",
|
|
126
126
|
"communication_channels.profile.title": "내 통신 채널",
|
|
127
127
|
"communication_channels.push.button.reregister": "푸시 다시 등록",
|
|
128
|
+
"communication_channels.push.connect.blocked": "유효성 검사로 인해 연결이 차단되었습니다",
|
|
129
|
+
"communication_channels.push.connect.button.apns": "APNs 연결",
|
|
130
|
+
"communication_channels.push.connect.button.expo": "Expo 연결",
|
|
131
|
+
"communication_channels.push.connect.button.fcm": "FCM 연결",
|
|
132
|
+
"communication_channels.push.connect.cancel": "취소",
|
|
133
|
+
"communication_channels.push.connect.connected": "푸시 제공자가 연결되었습니다.",
|
|
134
|
+
"communication_channels.push.connect.connecting": "연결하는 중…",
|
|
135
|
+
"communication_channels.push.connect.description": "이 채널은 테넌트 전체가 공유합니다 — 이 워크스페이스에 속한 모든 사용자의 기기가 이 채널을 통해 푸시를 받습니다.",
|
|
136
|
+
"communication_channels.push.connect.displayName": "표시 이름",
|
|
137
|
+
"communication_channels.push.connect.errors.fields.invalid": "값이 올바르지 않습니다.",
|
|
138
|
+
"communication_channels.push.connect.errors.fields.invalid_bundle_id": "번들 ID는 com.example.app 형식입니다.",
|
|
139
|
+
"communication_channels.push.connect.errors.fields.invalid_json": "유효한 JSON이 아닙니다. Firebase에서 생성한 서비스 계정 파일을 그대로 붙여넣으세요.",
|
|
140
|
+
"communication_channels.push.connect.errors.fields.invalid_key_id": "APNs 키 ID는 영숫자 10자입니다.",
|
|
141
|
+
"communication_channels.push.connect.errors.fields.invalid_p8": "읽을 수 있는 APNs .p8 서명 키가 아닙니다. BEGIN과 END 줄을 포함해 파일 전체를 붙여넣으세요.",
|
|
142
|
+
"communication_channels.push.connect.errors.fields.invalid_team_id": "Apple 팀 ID는 영숫자 10자입니다.",
|
|
143
|
+
"communication_channels.push.connect.errors.fields.missing_fields": "서비스 계정 JSON에 필수 항목(project_id, client_email, private_key)이 없습니다.",
|
|
144
|
+
"communication_channels.push.connect.errors.fields.required": "필수 입력 항목입니다.",
|
|
145
|
+
"communication_channels.push.connect.errors.mailbox_already_connected": "이 채널은 이미 다른 제공자를 통해 연결되어 있습니다. 다시 연결하기 전에 해당 제공자에서 연결을 해제하세요.",
|
|
146
|
+
"communication_channels.push.connect.errors.provider_not_tenant_scoped": "이 제공자는 사용자별 채널을 연결합니다. 개인 채널 연결 절차를 이용하세요.",
|
|
147
|
+
"communication_channels.push.connect.errors.wrong_scope_for_route": "예상치 못한 채널 범위입니다. 다시 시도하거나 지원팀에 문의하세요.",
|
|
148
|
+
"communication_channels.push.connect.failed": "푸시 제공자를 연결하지 못했습니다.",
|
|
149
|
+
"communication_channels.push.connect.fields.apns.bundleId": "번들 ID",
|
|
150
|
+
"communication_channels.push.connect.fields.apns.keyId": "키 ID",
|
|
151
|
+
"communication_channels.push.connect.fields.apns.p8Key": "APNs .p8 키",
|
|
152
|
+
"communication_channels.push.connect.fields.apns.production": "프로덕션 환경",
|
|
153
|
+
"communication_channels.push.connect.fields.apns.teamId": "팀 ID",
|
|
154
|
+
"communication_channels.push.connect.fields.expo.accessToken": "액세스 토큰 (선택 사항)",
|
|
155
|
+
"communication_channels.push.connect.fields.fcm.appName": "앱 이름 (선택 사항)",
|
|
156
|
+
"communication_channels.push.connect.fields.fcm.serviceAccountJson": "서비스 계정 JSON",
|
|
157
|
+
"communication_channels.push.connect.save": "연결",
|
|
158
|
+
"communication_channels.push.connect.title.apns": "Apple 푸시 알림 서비스 연결",
|
|
159
|
+
"communication_channels.push.connect.title.expo": "Expo 푸시 연결",
|
|
160
|
+
"communication_channels.push.connect.title.fcm": "Firebase 클라우드 메시징 연결",
|
|
128
161
|
"communication_channels.push.flash.registerFailed": "푸시 등록에 실패했습니다",
|
|
129
162
|
"communication_channels.push.status.active": "푸시 활성",
|
|
130
163
|
"communication_channels.push.status.failed": "푸시 실패 — 폴링 사용 중",
|
|
@@ -140,5 +173,6 @@
|
|
|
140
173
|
"communication_channels.status.disconnected": "연결 끊김",
|
|
141
174
|
"communication_channels.status.error": "오류",
|
|
142
175
|
"communication_channels.status.inactive": "비활성",
|
|
143
|
-
"communication_channels.status.requiresReauth": "재연결 필요"
|
|
176
|
+
"communication_channels.status.requiresReauth": "재연결 필요",
|
|
177
|
+
"notifications.categories.communication_channels": "커뮤니케이션"
|
|
144
178
|
}
|
|
@@ -125,6 +125,39 @@
|
|
|
125
125
|
"communication_channels.profile.tableTitle": "Twoje kanały",
|
|
126
126
|
"communication_channels.profile.title": "Moje kanały komunikacji",
|
|
127
127
|
"communication_channels.push.button.reregister": "Zarejestruj push ponownie",
|
|
128
|
+
"communication_channels.push.connect.blocked": "Połączenie zablokowane przez walidację",
|
|
129
|
+
"communication_channels.push.connect.button.apns": "Połącz APNs",
|
|
130
|
+
"communication_channels.push.connect.button.expo": "Połącz Expo",
|
|
131
|
+
"communication_channels.push.connect.button.fcm": "Połącz FCM",
|
|
132
|
+
"communication_channels.push.connect.cancel": "Anuluj",
|
|
133
|
+
"communication_channels.push.connect.connected": "Dostawca push połączony.",
|
|
134
|
+
"communication_channels.push.connect.connecting": "Łączenie…",
|
|
135
|
+
"communication_channels.push.connect.description": "To współdzielony kanał dla całego tenanta — obsługuje urządzenia wszystkich użytkowników w tej przestrzeni.",
|
|
136
|
+
"communication_channels.push.connect.displayName": "Nazwa wyświetlana",
|
|
137
|
+
"communication_channels.push.connect.errors.fields.invalid": "Ta wartość jest nieprawidłowa.",
|
|
138
|
+
"communication_channels.push.connect.errors.fields.invalid_bundle_id": "Identyfikator pakietu wygląda tak: com.example.app.",
|
|
139
|
+
"communication_channels.push.connect.errors.fields.invalid_json": "To nie jest prawidłowy JSON. Wklej plik konta serwisowego dokładnie tak, jak wygenerował go Firebase.",
|
|
140
|
+
"communication_channels.push.connect.errors.fields.invalid_key_id": "Identyfikator klucza APNs składa się z 10 znaków alfanumerycznych.",
|
|
141
|
+
"communication_channels.push.connect.errors.fields.invalid_p8": "To nie jest czytelny klucz podpisujący APNs .p8. Wklej cały plik razem z liniami BEGIN i END.",
|
|
142
|
+
"communication_channels.push.connect.errors.fields.invalid_team_id": "Identyfikator zespołu Apple składa się z 10 znaków alfanumerycznych.",
|
|
143
|
+
"communication_channels.push.connect.errors.fields.missing_fields": "W pliku JSON konta serwisowego brakuje wymaganych pól (project_id, client_email, private_key).",
|
|
144
|
+
"communication_channels.push.connect.errors.fields.required": "To pole jest wymagane.",
|
|
145
|
+
"communication_channels.push.connect.errors.mailbox_already_connected": "Ten kanał jest już połączony przez innego dostawcę. Rozłącz go tam przed ponownym połączeniem.",
|
|
146
|
+
"communication_channels.push.connect.errors.provider_not_tenant_scoped": "Ten dostawca łączy kanały indywidualne; użyj zamiast tego procesu łączenia kanału osobistego.",
|
|
147
|
+
"communication_channels.push.connect.errors.wrong_scope_for_route": "Nieoczekiwany zakres kanału. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.",
|
|
148
|
+
"communication_channels.push.connect.failed": "Nie udało się połączyć dostawcy 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": "Klucz .p8 APNs",
|
|
152
|
+
"communication_channels.push.connect.fields.apns.production": "Środowisko produkcyjne",
|
|
153
|
+
"communication_channels.push.connect.fields.apns.teamId": "Team ID",
|
|
154
|
+
"communication_channels.push.connect.fields.expo.accessToken": "Token dostępu (opcjonalnie)",
|
|
155
|
+
"communication_channels.push.connect.fields.fcm.appName": "Nazwa aplikacji (opcjonalnie)",
|
|
156
|
+
"communication_channels.push.connect.fields.fcm.serviceAccountJson": "JSON konta usługi",
|
|
157
|
+
"communication_channels.push.connect.save": "Połącz",
|
|
158
|
+
"communication_channels.push.connect.title.apns": "Połącz Apple Push Notification service",
|
|
159
|
+
"communication_channels.push.connect.title.expo": "Połącz Expo Push",
|
|
160
|
+
"communication_channels.push.connect.title.fcm": "Połącz Firebase Cloud Messaging",
|
|
128
161
|
"communication_channels.push.flash.registerFailed": "Nie udało się zarejestrować push",
|
|
129
162
|
"communication_channels.push.status.active": "Push aktywny",
|
|
130
163
|
"communication_channels.push.status.failed": "Push nieudany — używam odpytywania",
|
|
@@ -140,5 +173,6 @@
|
|
|
140
173
|
"communication_channels.status.disconnected": "Rozłączony",
|
|
141
174
|
"communication_channels.status.error": "Błąd",
|
|
142
175
|
"communication_channels.status.inactive": "Nieaktywny",
|
|
143
|
-
"communication_channels.status.requiresReauth": "Wymaga ponownego połączenia"
|
|
176
|
+
"communication_channels.status.requiresReauth": "Wymaga ponownego połączenia",
|
|
177
|
+
"notifications.categories.communication_channels": "Komunikacja"
|
|
144
178
|
}
|
|
@@ -17,6 +17,48 @@ import { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'
|
|
|
17
17
|
|
|
18
18
|
export const ADMIN_FEATURE = 'communication_channels.admin'
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Org-scoping fragment for reading/managing channels.
|
|
22
|
+
*
|
|
23
|
+
* Tenant-wide channels are stored with `organization_id IS NULL` (the
|
|
24
|
+
* tenant-scoped push providers FCM/APNs/Expo do this deliberately — see
|
|
25
|
+
* `connect-credential-channel.ts`). They are visible and manageable from ANY
|
|
26
|
+
* org in the tenant, so a read scoped to the caller's selected org must also
|
|
27
|
+
* match the NULL rows; otherwise a channel connected while an admin had a
|
|
28
|
+
* non-null org would be invisible to every listing/detail query (and vice
|
|
29
|
+
* versa). Org-scoped channels (`organization_id = <org>`) stay scoped to their
|
|
30
|
+
* org.
|
|
31
|
+
*
|
|
32
|
+
* Spread the result into a MikroORM `where` object alongside `tenantId` etc.
|
|
33
|
+
*/
|
|
34
|
+
export function channelOrgScopeWhere(
|
|
35
|
+
orgId: string | null | undefined,
|
|
36
|
+
): Record<string, unknown> {
|
|
37
|
+
return orgId
|
|
38
|
+
? { $or: [{ organizationId: orgId }, { organizationId: null }] }
|
|
39
|
+
: { organizationId: null }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Same NULL-inclusive org scoping, expressed over a resolved
|
|
44
|
+
* `OrganizationScopeFilter` (`resolveOrganizationScopeFilter`) rather than a
|
|
45
|
+
* single org id.
|
|
46
|
+
*
|
|
47
|
+
* Read routes scope on the caller's *selected* organization (the
|
|
48
|
+
* `om_selected_org` cookie the top-bar switcher writes), not on the org baked
|
|
49
|
+
* into the session token (#5012) — that resolution yields a list of org ids, or
|
|
50
|
+
* `undefined` when the caller is unrestricted (super-admin viewing all orgs).
|
|
51
|
+
* An unrestricted caller sees every channel in the tenant, so the fragment is
|
|
52
|
+
* empty; a restricted one gets their orgs plus the tenant-wide (`NULL`) rows.
|
|
53
|
+
*/
|
|
54
|
+
export function channelOrgScopeWhereFromFilter(
|
|
55
|
+
filter: { organizationIds: string[] | undefined } | null | undefined,
|
|
56
|
+
): Record<string, unknown> {
|
|
57
|
+
const organizationIds = filter?.organizationIds
|
|
58
|
+
if (!organizationIds || organizationIds.length === 0) return {}
|
|
59
|
+
return { $or: [{ organizationId: { $in: organizationIds } }, { organizationId: null }] }
|
|
60
|
+
}
|
|
61
|
+
|
|
20
62
|
/**
|
|
21
63
|
* Throws when the caller may not access a specific channel. Returns silently
|
|
22
64
|
* when access is allowed.
|
|
@@ -486,6 +486,14 @@ export interface ValidateCredentialsResult {
|
|
|
486
486
|
ok: boolean
|
|
487
487
|
/** Field-level error messages keyed by credential field name; for `createCrudFormError`. */
|
|
488
488
|
errors?: Record<string, string>
|
|
489
|
+
/**
|
|
490
|
+
* Stable machine-readable code per failing field, keyed the same way as
|
|
491
|
+
* `errors`. Lets a UI render a localized message instead of the English
|
|
492
|
+
* prose in `errors` (which stays reserved for logs and API consumers), the
|
|
493
|
+
* same split the route-level `code` field already uses. Optional: adapters
|
|
494
|
+
* that don't emit codes keep working and callers fall back to `errors`.
|
|
495
|
+
*/
|
|
496
|
+
errorCodes?: Record<string, string>
|
|
489
497
|
}
|
|
490
498
|
|
|
491
499
|
// ── The adapter contract ─────────────────────────────────────
|
|
@@ -494,6 +502,23 @@ export interface ChannelAdapter {
|
|
|
494
502
|
readonly providerKey: string
|
|
495
503
|
readonly channelType: 'whatsapp' | 'slack' | 'email' | 'sms' | string
|
|
496
504
|
|
|
505
|
+
/**
|
|
506
|
+
* Scope of a connected channel for this provider. Governs whether the connect
|
|
507
|
+
* flow stamps `CommunicationChannel.user_id` with the connecting user or leaves
|
|
508
|
+
* it NULL (tenant-wide):
|
|
509
|
+
*
|
|
510
|
+
* - `'user'` (default when absent) — one channel per user (Gmail, IMAP). The
|
|
511
|
+
* credential belongs to the connecting user.
|
|
512
|
+
* - `'tenant'` — one shared channel per tenant (`user_id = NULL`), connected by
|
|
513
|
+
* an admin. Used by push providers (FCM/APNs/Expo) whose service account /
|
|
514
|
+
* signing key serves every device in the tenant. Reading (fan-out/delivery)
|
|
515
|
+
* is already scope-agnostic; this only affects the connect/write path.
|
|
516
|
+
*
|
|
517
|
+
* ADDITIVE-ONLY (BACKWARD_COMPATIBILITY.md): existing adapters that omit it keep
|
|
518
|
+
* their per-user behaviour unchanged.
|
|
519
|
+
*/
|
|
520
|
+
readonly channelScope?: 'tenant' | 'user'
|
|
521
|
+
|
|
497
522
|
/** Declare supported features */
|
|
498
523
|
readonly capabilities: ChannelCapabilities
|
|
499
524
|
|
|
@@ -36,7 +36,8 @@ export interface CreateConnectedChannelRowArgs {
|
|
|
36
36
|
displayName: string
|
|
37
37
|
externalIdentifier: string | null
|
|
38
38
|
credentialsRefId: string | null
|
|
39
|
-
|
|
39
|
+
/** Connecting user, or `null` for a tenant-wide channel (push: FCM/APNs/Expo). */
|
|
40
|
+
userId: string | null
|
|
40
41
|
scope: { tenantId: string; organizationId: string | null }
|
|
41
42
|
/**
|
|
42
43
|
* Explicit poll-interval override (seconds). When omitted, it is derived from
|
|
@@ -46,9 +47,11 @@ export interface CreateConnectedChannelRowArgs {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
|
-
* Create + persist
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* Create + persist a `CommunicationChannel` row for a connect flow. `userId` is
|
|
51
|
+
* the connecting user for per-user channels (Gmail/IMAP) or `null` for tenant-wide
|
|
52
|
+
* channels (push: FCM/APNs/Expo). Shared by the credential-connect command and the
|
|
53
|
+
* OAuth callback so both entry points use one channel-shape implementation instead
|
|
54
|
+
* of duplicating `em.create`.
|
|
52
55
|
*
|
|
53
56
|
* When credentials could not be persisted (`credentialsRefId === null`) the row
|
|
54
57
|
* is created in `requires_reauth` + `isActive=false` so workers don't poll a
|
|
@@ -59,6 +62,18 @@ export async function createConnectedChannelRow(
|
|
|
59
62
|
): Promise<CommunicationChannel> {
|
|
60
63
|
const { em, adapter, providerKey, displayName, externalIdentifier, credentialsRefId, userId, scope } = args
|
|
61
64
|
const credentialsAvailable = credentialsRefId !== null
|
|
65
|
+
|
|
66
|
+
// A tenant-wide push channel (FCM/APNs/Expo, `user_id = NULL`) is a mailbox-less channel keyed on
|
|
67
|
+
// (tenant, provider, channel_type='push', user_id NULL), enforced by
|
|
68
|
+
// `communication_channels_tenant_push_provider_uq`. Push credential schemas are `.passthrough()`, so a
|
|
69
|
+
// stray `email`/`username`/`fromAddress` key can leak through the connect command as an
|
|
70
|
+
// `externalIdentifier`. Left in place it would send a push reconnect down the mailbox dedup branch
|
|
71
|
+
// below — which never matches the existing push row — so the INSERT hits the tenant-push unique index,
|
|
72
|
+
// and the mailbox recovery filter can't find the winner ⇒ an unrecoverable 500. Drop the stray
|
|
73
|
+
// identifier for tenant-wide push so the guard, the dedup key, and the stored row all stay consistent
|
|
74
|
+
// with that index.
|
|
75
|
+
const isTenantWidePush = adapter.channelType === 'push' && userId === null
|
|
76
|
+
const effectiveExternalIdentifier = isTenantWidePush ? null : externalIdentifier
|
|
62
77
|
const pollIntervalSeconds =
|
|
63
78
|
args.pollIntervalSeconds !== undefined
|
|
64
79
|
? args.pollIntervalSeconds
|
|
@@ -73,8 +88,8 @@ export async function createConnectedChannelRow(
|
|
|
73
88
|
// email across channels — so every message would be ingested (and threaded)
|
|
74
89
|
// twice. Reconnecting the SAME provider/mailbox is fine (healed below); this
|
|
75
90
|
// only blocks a DIFFERENT provider for an already-connected address.
|
|
76
|
-
if (
|
|
77
|
-
const normalized =
|
|
91
|
+
if (effectiveExternalIdentifier) {
|
|
92
|
+
const normalized = effectiveExternalIdentifier.toLowerCase()
|
|
78
93
|
const userChannels = (await findWithDecryption(
|
|
79
94
|
em,
|
|
80
95
|
CommunicationChannel,
|
|
@@ -89,28 +104,48 @@ export async function createConnectedChannelRow(
|
|
|
89
104
|
existing.externalIdentifier.toLowerCase() === normalized,
|
|
90
105
|
)
|
|
91
106
|
if (conflict) {
|
|
92
|
-
throw new MailboxAlreadyConnectedError(
|
|
107
|
+
throw new MailboxAlreadyConnectedError(effectiveExternalIdentifier, conflict.providerKey)
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
// Heal-on-reconnect dedup key. Two shapes:
|
|
112
|
+
// - mailbox channels (Gmail/IMAP): keyed on (tenant, user, provider, mailbox);
|
|
113
|
+
// only rows with a known `externalIdentifier` participate.
|
|
114
|
+
// - tenant-wide push channels (FCM/APNs/Expo): no mailbox, `user_id = NULL`, so
|
|
115
|
+
// keyed on (tenant, provider, channel_type='push', user_id NULL). Without this
|
|
116
|
+
// every admin reconnect would insert a duplicate (fan-out takes the oldest per
|
|
117
|
+
// provider, so duplicates are silent noise + a concurrent-connect race).
|
|
118
|
+
// `null` ⇒ no dedup key (identifier-less non-push channel): always insert.
|
|
119
|
+
const dedupeFilter = isTenantWidePush
|
|
120
|
+
? {
|
|
121
|
+
tenantId: scope.tenantId,
|
|
122
|
+
userId: null,
|
|
123
|
+
providerKey,
|
|
124
|
+
channelType: 'push',
|
|
125
|
+
deletedAt: null,
|
|
126
|
+
}
|
|
127
|
+
: effectiveExternalIdentifier
|
|
128
|
+
? {
|
|
129
|
+
tenantId: scope.tenantId,
|
|
130
|
+
userId,
|
|
131
|
+
providerKey,
|
|
132
|
+
externalIdentifier: effectiveExternalIdentifier,
|
|
133
|
+
deletedAt: null,
|
|
134
|
+
}
|
|
135
|
+
: null
|
|
103
136
|
|
|
104
|
-
// Heal-on-reconnect: a channel for the same
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
// a duplicate would stay `isActive` and keep
|
|
108
|
-
// banners, and register a second competing push
|
|
109
|
-
//
|
|
137
|
+
// Heal-on-reconnect: a channel for the same dedup key already exists when the
|
|
138
|
+
// user re-runs OAuth / reconnects after a `requires_reauth`, or an admin
|
|
139
|
+
// re-submits a tenant push provider's credentials. Update it in place rather
|
|
140
|
+
// than inserting a duplicate row — a duplicate would stay `isActive` and keep
|
|
141
|
+
// polling + re-emitting reauth banners, and register a second competing push
|
|
142
|
+
// subscription. Backed by the partial unique indexes
|
|
143
|
+
// `communication_channels_user_provider_external_uq` (mailbox) and
|
|
144
|
+
// `communication_channels_tenant_push_provider_uq` (tenant push).
|
|
110
145
|
const applyConnectionState = (target: CommunicationChannel): void => {
|
|
111
146
|
target.channelType = adapter.channelType
|
|
112
147
|
target.displayName = displayName
|
|
113
|
-
target.externalIdentifier =
|
|
148
|
+
target.externalIdentifier = effectiveExternalIdentifier ?? null
|
|
114
149
|
target.credentialsRef = credentialsRefId
|
|
115
150
|
target.capabilities = adapter.capabilities as unknown as Record<string, unknown>
|
|
116
151
|
target.isActive = credentialsAvailable
|
|
@@ -119,8 +154,8 @@ export async function createConnectedChannelRow(
|
|
|
119
154
|
target.lastError = credentialsAvailable ? null : 'credentials_persist_failed'
|
|
120
155
|
}
|
|
121
156
|
|
|
122
|
-
if (
|
|
123
|
-
const existing = await findOneWithDecryption(em, CommunicationChannel,
|
|
157
|
+
if (dedupeFilter) {
|
|
158
|
+
const existing = await findOneWithDecryption(em, CommunicationChannel, dedupeFilter, undefined, dscope)
|
|
124
159
|
if (existing) {
|
|
125
160
|
applyConnectionState(existing)
|
|
126
161
|
await em.flush()
|
|
@@ -132,7 +167,7 @@ export async function createConnectedChannelRow(
|
|
|
132
167
|
providerKey,
|
|
133
168
|
channelType: adapter.channelType,
|
|
134
169
|
displayName,
|
|
135
|
-
externalIdentifier:
|
|
170
|
+
externalIdentifier: effectiveExternalIdentifier ?? null,
|
|
136
171
|
credentialsRef: credentialsRefId,
|
|
137
172
|
capabilities: adapter.capabilities as unknown as Record<string, unknown>,
|
|
138
173
|
isActive: credentialsAvailable,
|
|
@@ -152,9 +187,9 @@ export async function createConnectedChannelRow(
|
|
|
152
187
|
// Concurrent connect for the same mailbox won the race (partial unique index
|
|
153
188
|
// rejected ours). Re-select the winner on a clean fork and heal it so the
|
|
154
189
|
// caller still gets a single, connected channel.
|
|
155
|
-
if (!isUniqueViolation(err) || !
|
|
190
|
+
if (!isUniqueViolation(err) || !dedupeFilter) throw err
|
|
156
191
|
const reEm = em.fork()
|
|
157
|
-
const winner = await findOneWithDecryption(reEm, CommunicationChannel,
|
|
192
|
+
const winner = await findOneWithDecryption(reEm, CommunicationChannel, dedupeFilter, undefined, dscope)
|
|
158
193
|
if (!winner) throw err
|
|
159
194
|
applyConnectionState(winner)
|
|
160
195
|
await reEm.flush()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { ZodType } from 'zod'
|
|
2
|
+
import type {
|
|
3
|
+
ChannelAdapter,
|
|
4
|
+
ChannelCapabilities,
|
|
5
|
+
ChannelNativeContent,
|
|
6
|
+
ConvertOutboundInput,
|
|
7
|
+
GetMessageStatusInput,
|
|
8
|
+
InboundMessage,
|
|
9
|
+
MessageStatus,
|
|
10
|
+
NormalizedInboundMessage,
|
|
11
|
+
SendMessageInput,
|
|
12
|
+
SendMessageResult,
|
|
13
|
+
ValidateCredentialsInput,
|
|
14
|
+
ValidateCredentialsResult,
|
|
15
|
+
VerifyWebhookInput,
|
|
16
|
+
} from './adapter'
|
|
17
|
+
import { pushChannelCapabilities } from './push-capabilities'
|
|
18
|
+
import { resolvePushCredentialIssue } from './push-credential-errors'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Uniform sentinel a push adapter returns for a permanently-invalid device token.
|
|
22
|
+
* The `push_notifications` worker checks `error === DEVICE_UNREGISTERED ||
|
|
23
|
+
* metadata.unregistered === true` and soft-deletes the device — so every push
|
|
24
|
+
* provider (fcm/apns/expo + the test stub) MUST use this exact shape.
|
|
25
|
+
*/
|
|
26
|
+
export const DEVICE_UNREGISTERED = 'device_unregistered'
|
|
27
|
+
|
|
28
|
+
/** Build the uniform `device_unregistered` result. Extra metadata is merged in. */
|
|
29
|
+
export function deviceUnregisteredResult(metadata?: Record<string, unknown>): SendMessageResult {
|
|
30
|
+
return {
|
|
31
|
+
externalMessageId: '',
|
|
32
|
+
status: 'failed',
|
|
33
|
+
error: DEVICE_UNREGISTERED,
|
|
34
|
+
metadata: { unregistered: true, ...(metadata ?? {}) },
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Standard result when a send is attempted without a device token. */
|
|
39
|
+
export const MISSING_PUSH_TOKEN_RESULT: SendMessageResult = {
|
|
40
|
+
externalMessageId: '',
|
|
41
|
+
status: 'failed',
|
|
42
|
+
error: 'missing_push_token',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Outcome of checking one provider delivery receipt for a previously-accepted push.
|
|
47
|
+
* Emitted only for tickets whose receipt is already available (`resolved`); a ticket whose
|
|
48
|
+
* receipt is not yet ready is omitted so the caller retries it on a later sweep.
|
|
49
|
+
*/
|
|
50
|
+
export interface PushReceiptOutcome {
|
|
51
|
+
/** The provider ticket id returned by the synchronous send (persisted as `externalMessageId`). */
|
|
52
|
+
ticketId: string
|
|
53
|
+
/** The receipt reports the device token is permanently invalid ⇒ soft-delete the device. */
|
|
54
|
+
unregistered: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Optional capability for providers whose "device unregistered" signal arrives in an ASYNCHRONOUS
|
|
59
|
+
* receipt phase rather than the synchronous send ticket (Expo). Implemented by the Expo adapter and
|
|
60
|
+
* polled by the push_notifications receipt reaper; FCM/APNs prune synchronously in `sendMessage` and
|
|
61
|
+
* do not implement it. Additive to `ChannelAdapter` — feature-detected via `supportsReceiptChecking`.
|
|
62
|
+
*/
|
|
63
|
+
export interface PushReceiptChecker {
|
|
64
|
+
/**
|
|
65
|
+
* Fetch delivery receipts for previously-accepted push tickets and report which map to a
|
|
66
|
+
* permanently-unregistered device token. Return an entry ONLY for tickets whose receipt is ready;
|
|
67
|
+
* omit tickets with no receipt yet so the caller re-checks them later. Reuses the same tenant
|
|
68
|
+
* credentials as the send path.
|
|
69
|
+
*/
|
|
70
|
+
checkReceipts(ticketIds: string[], credentials: unknown): Promise<PushReceiptOutcome[]>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Feature-detect the optional `PushReceiptChecker` capability on a resolved channel adapter. */
|
|
74
|
+
export function supportsReceiptChecking(adapter: unknown): adapter is PushReceiptChecker {
|
|
75
|
+
return typeof (adapter as { checkReceipts?: unknown } | null | undefined)?.checkReceipts === 'function'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Extract the per-call push token from `SendMessageInput.metadata`, or null if absent. */
|
|
79
|
+
export function readPushToken(input: SendMessageInput): string | null {
|
|
80
|
+
const token = typeof input.metadata?.pushToken === 'string' ? input.metadata.pushToken : ''
|
|
81
|
+
return token.length > 0 ? token : null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Shared base for outbound-only mobile-push `ChannelAdapter`s (fcm/apns/expo and
|
|
86
|
+
* the test `push_stub`). Concrete adapters provide just `providerKey`,
|
|
87
|
+
* `credentialsSchema`, and `sendMessage`; the push capabilities baseline, the
|
|
88
|
+
* no-op inbound surface (push is outbound-only), the passthrough `convertOutbound`,
|
|
89
|
+
* and schema-driven `validateCredentials` are shared so the push family stays
|
|
90
|
+
* consistent. This is push-specific and does NOT change the per-package
|
|
91
|
+
* standalone convention used by the email channel adapters (gmail/imap).
|
|
92
|
+
*/
|
|
93
|
+
export abstract class BasePushChannelAdapter implements ChannelAdapter {
|
|
94
|
+
abstract readonly providerKey: string
|
|
95
|
+
readonly channelType = 'push'
|
|
96
|
+
// Push credentials (service account / signing key) serve every device in the
|
|
97
|
+
// tenant, so a push channel is tenant-wide (user_id = NULL), connected by an
|
|
98
|
+
// admin — never a per-user channel. Covers fcm/apns/expo + the test stub.
|
|
99
|
+
readonly channelScope = 'tenant' as const
|
|
100
|
+
readonly capabilities: ChannelCapabilities = pushChannelCapabilities
|
|
101
|
+
|
|
102
|
+
/** Zod schema for this provider's tenant-level credentials; drives `validateCredentials`. */
|
|
103
|
+
protected abstract readonly credentialsSchema: ZodType<unknown>
|
|
104
|
+
|
|
105
|
+
abstract sendMessage(input: SendMessageInput): Promise<SendMessageResult>
|
|
106
|
+
|
|
107
|
+
async convertOutbound(input: ConvertOutboundInput): Promise<ChannelNativeContent> {
|
|
108
|
+
return { content: { text: input.body, bodyFormat: input.bodyFormat }, metadata: input.channelMetadata ?? {} }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async verifyWebhook(_input: VerifyWebhookInput): Promise<InboundMessage> {
|
|
112
|
+
return { raw: {}, eventType: 'other', metadata: { reason: `${this.providerKey}-no-webhook` } }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getStatus(_input: GetMessageStatusInput): Promise<MessageStatus> {
|
|
116
|
+
return { status: 'sent' }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async normalizeInbound(_raw: InboundMessage): Promise<NormalizedInboundMessage> {
|
|
120
|
+
throw new Error(`[internal] ${this.providerKey} push adapter normalizeInbound is not used (push is outbound-only)`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async validateCredentials(input: ValidateCredentialsInput): Promise<ValidateCredentialsResult> {
|
|
124
|
+
const parsed = this.credentialsSchema.safeParse(input.credentials)
|
|
125
|
+
if (parsed.success) return { ok: true }
|
|
126
|
+
const errors: Record<string, string> = {}
|
|
127
|
+
const errorCodes: Record<string, string> = {}
|
|
128
|
+
for (const issue of parsed.error.issues) {
|
|
129
|
+
const key = issue.path[0]
|
|
130
|
+
if (typeof key !== 'string' || errors[key]) continue
|
|
131
|
+
// Schemas emit a stable code as the issue message; keep the English prose
|
|
132
|
+
// for API consumers and hand the code to the UI so it can localize.
|
|
133
|
+
const resolved = resolvePushCredentialIssue(issue.message)
|
|
134
|
+
errors[key] = resolved.message
|
|
135
|
+
errorCodes[key] = resolved.code
|
|
136
|
+
}
|
|
137
|
+
return { ok: false, errors, errorCodes }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ChannelCapabilities } from './adapter'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Baseline capability profile shared by every mobile-push channel provider
|
|
5
|
+
* (FCM / APNs / Expo) and the test-only `push_stub` adapter. Mirrors the
|
|
6
|
+
* `baseEmailCapabilities` pattern so the push contract has a single source of
|
|
7
|
+
* truth: push is a fire-and-forget outbound channel with no threading, history,
|
|
8
|
+
* reactions, or rich content, and it is real-time (`realtimePush: true`) so the
|
|
9
|
+
* hub never schedules polling for it.
|
|
10
|
+
*
|
|
11
|
+
* Providers spread this and override only what genuinely differs.
|
|
12
|
+
*/
|
|
13
|
+
export const pushChannelCapabilities: ChannelCapabilities = {
|
|
14
|
+
// Core
|
|
15
|
+
threading: false,
|
|
16
|
+
richText: false,
|
|
17
|
+
fileSharing: false,
|
|
18
|
+
readReceipts: false,
|
|
19
|
+
deliveryReceipts: false,
|
|
20
|
+
typingIndicators: false,
|
|
21
|
+
|
|
22
|
+
// Extended
|
|
23
|
+
reactions: false,
|
|
24
|
+
multiReactionPerUser: false,
|
|
25
|
+
editMessage: false,
|
|
26
|
+
deleteMessage: false,
|
|
27
|
+
presence: false,
|
|
28
|
+
richBlocks: false,
|
|
29
|
+
interactiveComponents: false,
|
|
30
|
+
inlineImages: false,
|
|
31
|
+
conversationHistory: false,
|
|
32
|
+
contactCards: false,
|
|
33
|
+
locationSharing: false,
|
|
34
|
+
voiceNotes: false,
|
|
35
|
+
stickers: false,
|
|
36
|
+
|
|
37
|
+
// Body formats
|
|
38
|
+
supportedBodyFormats: ['text'],
|
|
39
|
+
|
|
40
|
+
// Push is real-time; no polling.
|
|
41
|
+
realtimePush: true,
|
|
42
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type PushConnectErrorBody = {
|
|
2
|
+
code?: string | null
|
|
3
|
+
fieldErrors?: Record<string, string> | null
|
|
4
|
+
fieldErrorCodes?: Record<string, string> | null
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type PushConnectTranslate = (key: string, fallback: string) => string
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Map a push connect route's structured error `code`
|
|
11
|
+
* (`provider_not_tenant_scoped`, `mailbox_already_connected`,
|
|
12
|
+
* `wrong_scope_for_route`, …) to a localized
|
|
13
|
+
* `communication_channels.push.connect.errors.<code>` message, falling back to
|
|
14
|
+
* the generic `push.connect.failed` when the response carries no code or no
|
|
15
|
+
* matching translation exists. The raw English `error` string the route also
|
|
16
|
+
* returns stays reserved for logs/API consumers so the shipped locale files
|
|
17
|
+
* apply on the failure path.
|
|
18
|
+
*/
|
|
19
|
+
export function resolvePushConnectErrorMessage(
|
|
20
|
+
translate: PushConnectTranslate,
|
|
21
|
+
body: PushConnectErrorBody | undefined,
|
|
22
|
+
): string {
|
|
23
|
+
const fallback = translate('communication_channels.push.connect.failed', 'Could not connect push provider.')
|
|
24
|
+
if (!body?.code) return fallback
|
|
25
|
+
return translate(`communication_channels.push.connect.errors.${body.code}`, fallback)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Localize the per-field credential-validation errors a 422 connect response
|
|
30
|
+
* carries. Each field's `fieldErrorCodes` entry maps to
|
|
31
|
+
* `communication_channels.push.connect.errors.fields.<code>`; when a field has
|
|
32
|
+
* no code (an adapter that predates the code contract) or the locale ships no
|
|
33
|
+
* matching key, the raw English `fieldErrors` string is used so the operator
|
|
34
|
+
* still sees something actionable rather than a blank field.
|
|
35
|
+
*
|
|
36
|
+
* Returns a map ready for a widget's `setFieldErrors`.
|
|
37
|
+
*/
|
|
38
|
+
export function resolvePushConnectFieldErrors(
|
|
39
|
+
translate: PushConnectTranslate,
|
|
40
|
+
body: PushConnectErrorBody | undefined,
|
|
41
|
+
): Record<string, string> {
|
|
42
|
+
const fieldErrors = body?.fieldErrors ?? {}
|
|
43
|
+
const fieldErrorCodes = body?.fieldErrorCodes ?? {}
|
|
44
|
+
const resolved: Record<string, string> = {}
|
|
45
|
+
for (const [field, message] of Object.entries(fieldErrors)) {
|
|
46
|
+
const code = fieldErrorCodes[field]
|
|
47
|
+
resolved[field] = code
|
|
48
|
+
? translate(`communication_channels.push.connect.errors.fields.${code}`, message)
|
|
49
|
+
: message
|
|
50
|
+
}
|
|
51
|
+
return resolved
|
|
52
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable field-error codes for push credential validation (fcm/apns/expo).
|
|
3
|
+
*
|
|
4
|
+
* Push credential schemas emit one of these codes as the zod issue `message`
|
|
5
|
+
* instead of prose. {@link BasePushChannelAdapter.validateCredentials} then
|
|
6
|
+
* splits each issue into two channels:
|
|
7
|
+
*
|
|
8
|
+
* - `errors[field]` — English prose from {@link PUSH_CREDENTIAL_ERROR_MESSAGES},
|
|
9
|
+
* reserved for logs and API consumers.
|
|
10
|
+
* - `errorCodes[field]` — the code, which the connect widgets map to
|
|
11
|
+
* `communication_channels.push.connect.errors.fields.<code>`
|
|
12
|
+
* so operators see a localized message.
|
|
13
|
+
*
|
|
14
|
+
* This mirrors the route-level split already used by `push-connect-error.ts`.
|
|
15
|
+
* An issue whose message is not a known code degrades to {@link PUSH_CREDENTIAL_ERROR_INVALID}
|
|
16
|
+
* with the raw zod message kept in `errors`, so a schema change can never leak
|
|
17
|
+
* an untranslated internal string into the UI.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const PUSH_CREDENTIAL_ERROR_REQUIRED = 'required'
|
|
21
|
+
export const PUSH_CREDENTIAL_ERROR_INVALID = 'invalid'
|
|
22
|
+
export const PUSH_CREDENTIAL_ERROR_INVALID_JSON = 'invalid_json'
|
|
23
|
+
export const PUSH_CREDENTIAL_ERROR_MISSING_FIELDS = 'missing_fields'
|
|
24
|
+
export const PUSH_CREDENTIAL_ERROR_INVALID_P8 = 'invalid_p8'
|
|
25
|
+
export const PUSH_CREDENTIAL_ERROR_INVALID_KEY_ID = 'invalid_key_id'
|
|
26
|
+
export const PUSH_CREDENTIAL_ERROR_INVALID_TEAM_ID = 'invalid_team_id'
|
|
27
|
+
export const PUSH_CREDENTIAL_ERROR_INVALID_BUNDLE_ID = 'invalid_bundle_id'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* English prose per code. Kept as the `errors` payload so existing API
|
|
31
|
+
* consumers and log readers still get a human-readable string.
|
|
32
|
+
*/
|
|
33
|
+
export const PUSH_CREDENTIAL_ERROR_MESSAGES: Record<string, string> = {
|
|
34
|
+
[PUSH_CREDENTIAL_ERROR_REQUIRED]: 'This field is required.',
|
|
35
|
+
[PUSH_CREDENTIAL_ERROR_INVALID]: 'This value is invalid.',
|
|
36
|
+
[PUSH_CREDENTIAL_ERROR_INVALID_JSON]: 'This is not valid JSON. Paste the service account file exactly as Firebase generated it.',
|
|
37
|
+
[PUSH_CREDENTIAL_ERROR_MISSING_FIELDS]: 'The service account JSON is missing required fields (project_id, client_email, private_key).',
|
|
38
|
+
[PUSH_CREDENTIAL_ERROR_INVALID_P8]: 'This is not a readable APNs .p8 signing key. Paste the whole file including the BEGIN and END lines.',
|
|
39
|
+
[PUSH_CREDENTIAL_ERROR_INVALID_KEY_ID]: 'An APNs Key ID is 10 alphanumeric characters.',
|
|
40
|
+
[PUSH_CREDENTIAL_ERROR_INVALID_TEAM_ID]: 'An Apple Team ID is 10 alphanumeric characters.',
|
|
41
|
+
[PUSH_CREDENTIAL_ERROR_INVALID_BUNDLE_ID]: 'A Bundle ID looks like com.example.app.',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isPushCredentialErrorCode(value: string): boolean {
|
|
45
|
+
return Object.prototype.hasOwnProperty.call(PUSH_CREDENTIAL_ERROR_MESSAGES, value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Split a zod issue message into `{ code, message }`. Known codes resolve to
|
|
50
|
+
* their English prose; anything else is reported as {@link PUSH_CREDENTIAL_ERROR_INVALID}
|
|
51
|
+
* while preserving the original text for logs.
|
|
52
|
+
*/
|
|
53
|
+
export function resolvePushCredentialIssue(issueMessage: string): { code: string; message: string } {
|
|
54
|
+
if (isPushCredentialErrorCode(issueMessage)) {
|
|
55
|
+
return { code: issueMessage, message: PUSH_CREDENTIAL_ERROR_MESSAGES[issueMessage] }
|
|
56
|
+
}
|
|
57
|
+
return { code: PUSH_CREDENTIAL_ERROR_INVALID, message: issueMessage }
|
|
58
|
+
}
|