@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,187 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
6
|
+
import { isUniqueViolation } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
8
|
+
import { NotificationType } from "../../../../../data/entities.js";
|
|
9
|
+
import { syncNotificationTypes } from "../../../../../lib/notification-type-registry.js";
|
|
10
|
+
import { computeChannelsPatch } from "../../../../../lib/typeChannelSettings.js";
|
|
11
|
+
import {
|
|
12
|
+
applyTypeOverride,
|
|
13
|
+
loadTypeOverrideForUpdate,
|
|
14
|
+
resolveCatalogueTranslate,
|
|
15
|
+
resolveEffectiveChannels,
|
|
16
|
+
resolveRegisteredChannelIds,
|
|
17
|
+
typeItem
|
|
18
|
+
} from "../../../../../lib/typeCatalogue.js";
|
|
19
|
+
import { notificationTypeItemSchema } from "../../../../../data/validators.js";
|
|
20
|
+
import { errorResponseSchema } from "../../../../openapi.js";
|
|
21
|
+
import {
|
|
22
|
+
NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
23
|
+
notificationCrudErrorResponse,
|
|
24
|
+
runGuardedNotificationWrite
|
|
25
|
+
} from "../../../../../lib/routeHelpers.js";
|
|
26
|
+
const logger = createLogger("notifications").child({ component: "type-channels-api" });
|
|
27
|
+
const paramsSchema = z.object({
|
|
28
|
+
id: z.string().min(1),
|
|
29
|
+
channel: z.string().min(1)
|
|
30
|
+
});
|
|
31
|
+
const metadata = {
|
|
32
|
+
PUT: { requireAuth: true, requireFeatures: ["notifications.manage"] },
|
|
33
|
+
DELETE: { requireAuth: true, requireFeatures: ["notifications.manage"] }
|
|
34
|
+
};
|
|
35
|
+
async function readParams(ctx) {
|
|
36
|
+
const raw = await ctx.params;
|
|
37
|
+
return paramsSchema.parse({ id: raw?.id, channel: raw?.channel });
|
|
38
|
+
}
|
|
39
|
+
async function toggleChannel(req, ctx, enabled) {
|
|
40
|
+
const { t } = await resolveTranslations();
|
|
41
|
+
const auth = await getAuthFromRequest(req);
|
|
42
|
+
if (!auth?.sub || !auth.tenantId) {
|
|
43
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
44
|
+
}
|
|
45
|
+
const tenantId = auth.tenantId;
|
|
46
|
+
let params;
|
|
47
|
+
try {
|
|
48
|
+
params = await readParams(ctx);
|
|
49
|
+
} catch {
|
|
50
|
+
return NextResponse.json(
|
|
51
|
+
{ error: t("api.errors.invalidPayload", "Invalid request body") },
|
|
52
|
+
{ status: 400 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const container = await createRequestContainer();
|
|
56
|
+
try {
|
|
57
|
+
const em = container.resolve("em");
|
|
58
|
+
await syncNotificationTypes(em);
|
|
59
|
+
const row = await em.findOne(NotificationType, {
|
|
60
|
+
id: params.id,
|
|
61
|
+
$or: [{ tenantId: null }, { tenantId }]
|
|
62
|
+
});
|
|
63
|
+
if (!row) {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: t("notifications.types.unknownType", "Unknown notification type") },
|
|
66
|
+
{ status: 404 }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const registeredChannelIds = resolveRegisteredChannelIds();
|
|
70
|
+
if (!registeredChannelIds.includes(params.channel)) {
|
|
71
|
+
return NextResponse.json(
|
|
72
|
+
{ error: t("notifications.types.unknownChannel", "Unknown notification channel") },
|
|
73
|
+
{ status: 404 }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const translate = await resolveCatalogueTranslate(req);
|
|
77
|
+
const guarded = await runGuardedNotificationWrite(
|
|
78
|
+
container,
|
|
79
|
+
{
|
|
80
|
+
tenantId,
|
|
81
|
+
organizationId: auth.orgId ?? null,
|
|
82
|
+
userId: auth.sub ?? null
|
|
83
|
+
},
|
|
84
|
+
req,
|
|
85
|
+
{
|
|
86
|
+
resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
87
|
+
resourceId: row.id,
|
|
88
|
+
operation: "update",
|
|
89
|
+
payload: { id: row.id, channel: params.channel, enabled }
|
|
90
|
+
},
|
|
91
|
+
async () => {
|
|
92
|
+
const applyOnce = async () => {
|
|
93
|
+
return em.transactional(async (tem) => {
|
|
94
|
+
const existing = await loadTypeOverrideForUpdate(tem, tenantId, row.id);
|
|
95
|
+
const base = resolveEffectiveChannels(row.id, existing?.channels, registeredChannelIds);
|
|
96
|
+
const nextChannels = computeChannelsPatch(base, params.channel, enabled);
|
|
97
|
+
const override = applyTypeOverride(tem, {
|
|
98
|
+
tenantId,
|
|
99
|
+
notificationTypeId: row.id,
|
|
100
|
+
existing,
|
|
101
|
+
nextChannels,
|
|
102
|
+
nextNonOptOut: existing?.nonOptOut ?? null
|
|
103
|
+
});
|
|
104
|
+
await tem.flush();
|
|
105
|
+
return typeItem(row, override, translate);
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
try {
|
|
109
|
+
return await applyOnce();
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (!isUniqueViolation(err)) throw err;
|
|
112
|
+
em.clear();
|
|
113
|
+
return applyOnce();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
if (!guarded.ok) return guarded.response;
|
|
118
|
+
return NextResponse.json({ ok: true, item: guarded.result });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const crudResponse = notificationCrudErrorResponse(error);
|
|
121
|
+
if (crudResponse) return crudResponse;
|
|
122
|
+
logger.error("notification type channel toggle failed", {
|
|
123
|
+
typeId: params.id,
|
|
124
|
+
channel: params.channel,
|
|
125
|
+
enabled,
|
|
126
|
+
error: error instanceof Error ? error.message : String(error)
|
|
127
|
+
});
|
|
128
|
+
return NextResponse.json(
|
|
129
|
+
{ error: t("api.errors.internal", "Internal error") },
|
|
130
|
+
{ status: 500 }
|
|
131
|
+
);
|
|
132
|
+
} finally {
|
|
133
|
+
const disposable = container;
|
|
134
|
+
if (typeof disposable.dispose === "function") await disposable.dispose();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function PUT(req, ctx) {
|
|
138
|
+
return toggleChannel(req, ctx, true);
|
|
139
|
+
}
|
|
140
|
+
async function DELETE(req, ctx) {
|
|
141
|
+
return toggleChannel(req, ctx, false);
|
|
142
|
+
}
|
|
143
|
+
const toggleResponseSchema = z.object({
|
|
144
|
+
ok: z.literal(true),
|
|
145
|
+
item: notificationTypeItemSchema
|
|
146
|
+
});
|
|
147
|
+
const openApi = {
|
|
148
|
+
PUT: {
|
|
149
|
+
summary: "Enable one delivery channel for a notification type",
|
|
150
|
+
description: "Adds a single channel to the tenant's eligibility set for this notification type. The next set is derived server-side from the currently stored state under a row lock, so concurrent toggles of different channels do not overwrite each other \u2014 prefer this over `PATCH /api/notifications/types` with a full `channels` array whenever a UI toggles one cell at a time. Idempotent: enabling an already-enabled channel is a no-op. Returns the refreshed catalogue item.",
|
|
151
|
+
tags: ["Notifications"],
|
|
152
|
+
responses: {
|
|
153
|
+
200: {
|
|
154
|
+
description: "Updated notification type",
|
|
155
|
+
content: { "application/json": { schema: toggleResponseSchema } }
|
|
156
|
+
},
|
|
157
|
+
401: { description: "Unauthorized", content: { "application/json": { schema: errorResponseSchema } } },
|
|
158
|
+
404: {
|
|
159
|
+
description: "Unknown notification type or unregistered channel",
|
|
160
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
DELETE: {
|
|
165
|
+
summary: "Disable one delivery channel for a notification type",
|
|
166
|
+
description: "Removes a single channel from the tenant's eligibility set for this notification type, with the same concurrency-safe server-side derivation as `PUT`. Removing the last remaining channel clears the override entirely so the code-declared default reapplies (an empty set is never stored \u2014 it would leave the type undeliverable). Idempotent: disabling an already-disabled channel is a no-op.",
|
|
167
|
+
tags: ["Notifications"],
|
|
168
|
+
responses: {
|
|
169
|
+
200: {
|
|
170
|
+
description: "Updated notification type",
|
|
171
|
+
content: { "application/json": { schema: toggleResponseSchema } }
|
|
172
|
+
},
|
|
173
|
+
401: { description: "Unauthorized", content: { "application/json": { schema: errorResponseSchema } } },
|
|
174
|
+
404: {
|
|
175
|
+
description: "Unknown notification type or unregistered channel",
|
|
176
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
export {
|
|
182
|
+
DELETE,
|
|
183
|
+
PUT,
|
|
184
|
+
metadata,
|
|
185
|
+
openApi
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/notifications/api/types/%5Bid%5D/channels/%5Bchannel%5D/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { NotificationType } from '../../../../../data/entities'\nimport { syncNotificationTypes } from '../../../../../lib/notification-type-registry'\nimport { computeChannelsPatch } from '../../../../../lib/typeChannelSettings'\nimport {\n applyTypeOverride,\n loadTypeOverrideForUpdate,\n resolveCatalogueTranslate,\n resolveEffectiveChannels,\n resolveRegisteredChannelIds,\n typeItem,\n} from '../../../../../lib/typeCatalogue'\nimport { notificationTypeItemSchema } from '../../../../../data/validators'\nimport { errorResponseSchema } from '../../../../openapi'\nimport {\n NOTIFICATION_SETTINGS_RESOURCE_KIND,\n notificationCrudErrorResponse,\n runGuardedNotificationWrite,\n} from '../../../../../lib/routeHelpers'\n\nconst logger = createLogger('notifications').child({ component: 'type-channels-api' })\n\nconst paramsSchema = z.object({\n id: z.string().min(1),\n channel: z.string().min(1),\n})\n\nexport const metadata = {\n PUT: { requireAuth: true, requireFeatures: ['notifications.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['notifications.manage'] },\n}\n\ntype RouteContext = { params?: { id?: string; channel?: string } | Promise<{ id?: string; channel?: string }> }\n\nasync function readParams(ctx: RouteContext) {\n const raw = await ctx.params\n return paramsSchema.parse({ id: raw?.id, channel: raw?.channel })\n}\n\n/**\n * Enable (`PUT`) or disable (`DELETE`) ONE channel for a notification type in the caller's tenant.\n *\n * Why a per-channel sub-resource rather than PATCHing the whole `channels` array: the array form\n * makes the client read-modify-write the full set, so two operators editing the same type from\n * stale views silently clobber each other \u2014 and while no override row exists yet there is no\n * `updated_at` to version-lock against, so the optimistic-lock guard cannot even detect it. Here\n * the server derives the next set from the CURRENT stored state under a row lock, so a concurrent\n * toggle of a different channel survives. Same shape as the other member-collection endpoints in\n * the repo (`customers/people/{id}/companies/{companyId}`, `catalog` media attach/detach).\n *\n * The full-array `PATCH /api/notifications/types` stays supported and unchanged for callers that\n * genuinely want to set the whole set at once.\n */\nasync function toggleChannel(req: Request, ctx: RouteContext, enabled: boolean): Promise<Response> {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth.tenantId) {\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n const tenantId = auth.tenantId\n\n let params: z.infer<typeof paramsSchema>\n try {\n params = await readParams(ctx)\n } catch {\n return NextResponse.json(\n { error: t('api.errors.invalidPayload', 'Invalid request body') },\n { status: 400 },\n )\n }\n\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n await syncNotificationTypes(em)\n const row = await em.findOne(NotificationType, {\n id: params.id,\n $or: [{ tenantId: null }, { tenantId }],\n })\n if (!row) {\n return NextResponse.json(\n { error: t('notifications.types.unknownType', 'Unknown notification type') },\n { status: 404 },\n )\n }\n\n // Reject unregistered channels up front: silently storing one would put a dead entry in the\n // eligibility set that no delivery strategy can ever satisfy.\n const registeredChannelIds = resolveRegisteredChannelIds()\n if (!registeredChannelIds.includes(params.channel)) {\n return NextResponse.json(\n { error: t('notifications.types.unknownChannel', 'Unknown notification channel') },\n { status: 404 },\n )\n }\n\n const translate = await resolveCatalogueTranslate(req)\n\n const guarded = await runGuardedNotificationWrite(\n container,\n {\n tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub ?? null,\n },\n req,\n {\n resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,\n resourceId: row.id,\n operation: 'update',\n payload: { id: row.id, channel: params.channel, enabled },\n },\n async () => {\n // optimistic-lock-exempt: notification type channel add/remove. The next set is derived\n // server-side from the current stored state inside a transaction that holds a row lock on\n // the override, so a concurrent toggle cannot be lost \u2014 there is no shared aggregate for a\n // version check to protect. Mirrors the add/remove endpoints in customers/catalog/staff.\n const applyOnce = async () => {\n return em.transactional(async (tem) => {\n const existing = await loadTypeOverrideForUpdate(tem, tenantId, row.id)\n const base = resolveEffectiveChannels(row.id, existing?.channels, registeredChannelIds)\n // Unchecking the last channel maps to `null` (clear the override \u2192 the code-declared\n // default reapplies) rather than an empty set, which would black-hole the type.\n const nextChannels = computeChannelsPatch(base, params.channel, enabled)\n const override = applyTypeOverride(tem, {\n tenantId,\n notificationTypeId: row.id,\n existing,\n nextChannels,\n nextNonOptOut: existing?.nonOptOut ?? null,\n })\n await tem.flush()\n return typeItem(row, override, translate)\n })\n }\n\n try {\n return await applyOnce()\n } catch (err) {\n // First-write race: no row existed to lock, so a concurrent INSERT for this\n // (tenant, type) won and the partial unique index rejected ours. A single-channel\n // toggle is idempotent, so re-apply it on top of the winner instead of surfacing a\n // conflict the operator would only have to resolve by clicking again.\n if (!isUniqueViolation(err)) throw err\n em.clear()\n return applyOnce()\n }\n },\n )\n if (!guarded.ok) return guarded.response\n return NextResponse.json({ ok: true, item: guarded.result })\n } catch (error) {\n const crudResponse = notificationCrudErrorResponse(error)\n if (crudResponse) return crudResponse\n logger.error('notification type channel toggle failed', {\n typeId: params.id,\n channel: params.channel,\n enabled,\n error: error instanceof Error ? error.message : String(error),\n })\n return NextResponse.json(\n { error: t('api.errors.internal', 'Internal error') },\n { status: 500 },\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') await disposable.dispose()\n }\n}\n\nexport async function PUT(req: Request, ctx: RouteContext): Promise<Response> {\n return toggleChannel(req, ctx, true)\n}\n\nexport async function DELETE(req: Request, ctx: RouteContext): Promise<Response> {\n return toggleChannel(req, ctx, false)\n}\n\nconst toggleResponseSchema = z.object({\n ok: z.literal(true),\n item: notificationTypeItemSchema,\n})\n\nexport const openApi = {\n PUT: {\n summary: 'Enable one delivery channel for a notification type',\n description:\n 'Adds a single channel to the tenant\\'s eligibility set for this notification type. The next set is derived server-side from the currently stored state under a row lock, so concurrent toggles of different channels do not overwrite each other \u2014 prefer this over `PATCH /api/notifications/types` with a full `channels` array whenever a UI toggles one cell at a time. Idempotent: enabling an already-enabled channel is a no-op. Returns the refreshed catalogue item.',\n tags: ['Notifications'],\n responses: {\n 200: {\n description: 'Updated notification type',\n content: { 'application/json': { schema: toggleResponseSchema } },\n },\n 401: { description: 'Unauthorized', content: { 'application/json': { schema: errorResponseSchema } } },\n 404: {\n description: 'Unknown notification type or unregistered channel',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n },\n },\n DELETE: {\n summary: 'Disable one delivery channel for a notification type',\n description:\n 'Removes a single channel from the tenant\\'s eligibility set for this notification type, with the same concurrency-safe server-side derivation as `PUT`. Removing the last remaining channel clears the override entirely so the code-declared default reapplies (an empty set is never stored \u2014 it would leave the type undeliverable). Idempotent: disabling an already-disabled channel is a no-op.',\n tags: ['Notifications'],\n responses: {\n 200: {\n description: 'Updated notification type',\n content: { 'application/json': { schema: toggleResponseSchema } },\n },\n 401: { description: 'Unauthorized', content: { 'application/json': { schema: errorResponseSchema } } },\n 404: {\n description: 'Unknown notification type or unregistered channel',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
5
|
+
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
6
|
+
import { conflict, isUniqueViolation } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { NotificationType, NotificationTypeOverride } from "../../data/entities.js";
|
|
10
|
+
import { syncNotificationTypes } from "../../lib/notification-type-registry.js";
|
|
11
|
+
import { applyTypeOverride, resolveCatalogueTranslate, typeItem } from "../../lib/typeCatalogue.js";
|
|
12
|
+
import { notificationTypeItemSchema, updateNotificationTypeSchema } from "../../data/validators.js";
|
|
13
|
+
import { errorResponseSchema } from "../openapi.js";
|
|
14
|
+
import {
|
|
15
|
+
NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
16
|
+
notificationCrudErrorResponse,
|
|
17
|
+
runGuardedNotificationWrite
|
|
18
|
+
} from "../../lib/routeHelpers.js";
|
|
19
|
+
const logger = createLogger("notifications").child({ component: "types-api" });
|
|
20
|
+
const metadata = {
|
|
21
|
+
GET: { requireAuth: true, requireFeatures: ["notifications.view"] },
|
|
22
|
+
PATCH: { requireAuth: true, requireFeatures: ["notifications.manage"] }
|
|
23
|
+
};
|
|
24
|
+
async function GET(req) {
|
|
25
|
+
const { t } = await resolveTranslations();
|
|
26
|
+
const auth = await getAuthFromRequest(req);
|
|
27
|
+
if (!auth?.sub || !auth.tenantId) {
|
|
28
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
29
|
+
}
|
|
30
|
+
const container = await createRequestContainer();
|
|
31
|
+
try {
|
|
32
|
+
const em = container.resolve("em");
|
|
33
|
+
await syncNotificationTypes(em);
|
|
34
|
+
const rows = await em.find(
|
|
35
|
+
NotificationType,
|
|
36
|
+
{ $or: [{ tenantId: null }, { tenantId: auth.tenantId }] },
|
|
37
|
+
{ orderBy: { id: "asc" } }
|
|
38
|
+
);
|
|
39
|
+
const overrides = await em.find(NotificationTypeOverride, {
|
|
40
|
+
tenantId: auth.tenantId,
|
|
41
|
+
notificationTypeId: { $in: rows.map((row) => row.id) }
|
|
42
|
+
});
|
|
43
|
+
const overrideByType = new Map(overrides.map((override) => [override.notificationTypeId, override]));
|
|
44
|
+
const translate = await resolveCatalogueTranslate(req);
|
|
45
|
+
return NextResponse.json({
|
|
46
|
+
items: rows.map((row) => typeItem(row, overrideByType.get(row.id), translate))
|
|
47
|
+
});
|
|
48
|
+
} finally {
|
|
49
|
+
const disposable = container;
|
|
50
|
+
if (typeof disposable.dispose === "function") await disposable.dispose();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function PATCH(req) {
|
|
54
|
+
const { t, translate } = await resolveTranslations();
|
|
55
|
+
const auth = await getAuthFromRequest(req);
|
|
56
|
+
if (!auth?.sub || !auth.tenantId) {
|
|
57
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
58
|
+
}
|
|
59
|
+
const tenantId = auth.tenantId;
|
|
60
|
+
let body;
|
|
61
|
+
try {
|
|
62
|
+
body = await req.json();
|
|
63
|
+
} catch {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: t("api.errors.invalidPayload", "Invalid request body") },
|
|
66
|
+
{ status: 400 }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const parsed = updateNotificationTypeSchema.safeParse(body);
|
|
70
|
+
if (!parsed.success) {
|
|
71
|
+
return NextResponse.json(
|
|
72
|
+
{ error: t("notifications.types.invalidChannels", "Invalid channels payload") },
|
|
73
|
+
{ status: 400 }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const container = await createRequestContainer();
|
|
77
|
+
try {
|
|
78
|
+
const em = container.resolve("em");
|
|
79
|
+
await syncNotificationTypes(em);
|
|
80
|
+
const row = await em.findOne(NotificationType, {
|
|
81
|
+
id: parsed.data.id,
|
|
82
|
+
$or: [{ tenantId: null }, { tenantId }]
|
|
83
|
+
});
|
|
84
|
+
if (!row) {
|
|
85
|
+
return NextResponse.json(
|
|
86
|
+
{ error: t("notifications.types.unknownType", "Unknown notification type") },
|
|
87
|
+
{ status: 404 }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const existing = await em.findOne(NotificationTypeOverride, {
|
|
91
|
+
tenantId,
|
|
92
|
+
notificationTypeId: row.id
|
|
93
|
+
});
|
|
94
|
+
const guarded = await runGuardedNotificationWrite(
|
|
95
|
+
container,
|
|
96
|
+
{
|
|
97
|
+
tenantId,
|
|
98
|
+
organizationId: auth.orgId ?? null,
|
|
99
|
+
userId: auth.sub ?? null
|
|
100
|
+
},
|
|
101
|
+
req,
|
|
102
|
+
{
|
|
103
|
+
resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
104
|
+
resourceId: row.id,
|
|
105
|
+
operation: "update",
|
|
106
|
+
payload: parsed.data
|
|
107
|
+
},
|
|
108
|
+
async () => {
|
|
109
|
+
enforceCommandOptimisticLock({
|
|
110
|
+
resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,
|
|
111
|
+
resourceId: row.id,
|
|
112
|
+
current: existing?.updatedAt ?? null,
|
|
113
|
+
request: req
|
|
114
|
+
});
|
|
115
|
+
const nextChannels = parsed.data.channels !== void 0 ? parsed.data.channels : existing?.channels ?? null;
|
|
116
|
+
const nextNonOptOut = parsed.data.nonOptOut !== void 0 ? parsed.data.nonOptOut : existing?.nonOptOut ?? null;
|
|
117
|
+
const override = applyTypeOverride(em, {
|
|
118
|
+
tenantId,
|
|
119
|
+
notificationTypeId: row.id,
|
|
120
|
+
existing,
|
|
121
|
+
nextChannels,
|
|
122
|
+
nextNonOptOut
|
|
123
|
+
});
|
|
124
|
+
try {
|
|
125
|
+
await em.flush();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (isUniqueViolation(err)) {
|
|
128
|
+
throw conflict(
|
|
129
|
+
t(
|
|
130
|
+
"notifications.types.overrideConflict",
|
|
131
|
+
"Another operator just saved this notification type. Reload and try again."
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
return typeItem(row, override, translate);
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
if (!guarded.ok) return guarded.response;
|
|
141
|
+
return NextResponse.json({ ok: true, item: guarded.result });
|
|
142
|
+
} catch (error) {
|
|
143
|
+
const crudResponse = notificationCrudErrorResponse(error);
|
|
144
|
+
if (crudResponse) return crudResponse;
|
|
145
|
+
logger.error("notification type override update failed", {
|
|
146
|
+
typeId: parsed.data.id,
|
|
147
|
+
error: error instanceof Error ? error.message : String(error)
|
|
148
|
+
});
|
|
149
|
+
return NextResponse.json(
|
|
150
|
+
{ error: t("api.errors.internal", "Internal error") },
|
|
151
|
+
{ status: 500 }
|
|
152
|
+
);
|
|
153
|
+
} finally {
|
|
154
|
+
const disposable = container;
|
|
155
|
+
if (typeof disposable.dispose === "function") await disposable.dispose();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const openApi = {
|
|
159
|
+
GET: {
|
|
160
|
+
summary: "List notification types",
|
|
161
|
+
description: "Returns the notification type catalogue (system-wide + tenant) so clients can render a preferences screen. `channels` is the effective channel eligibility for the caller's tenant (stored override, else the code-declared set; `null` = every channel); a channel outside it never delivers in that tenant and cannot be enabled by users. `updatedAt` is the override row's optimistic-lock version (`null` when the tenant stores no override). `label`, `description` and `categoryLabel` are resolved server-side using the request locale (`?locale=`, `x-locale`, the `locale` cookie, or `Accept-Language`) so clients without the app dictionary can render them directly; `categoryLabel` falls back to the raw category key when the owning module ships no `notifications.categories.<key>` translation, so `categoryLabel === category` signals \"no server-side label\". `category` defaults to the prefix before the first dot in the type id and is the stable grouping key \u2014 group on `category`, display `categoryLabel`.",
|
|
162
|
+
tags: ["Notifications"],
|
|
163
|
+
responses: {
|
|
164
|
+
200: {
|
|
165
|
+
description: "Notification type catalogue",
|
|
166
|
+
content: {
|
|
167
|
+
"application/json": {
|
|
168
|
+
schema: z.object({ items: z.array(notificationTypeItemSchema) })
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
401: {
|
|
173
|
+
description: "Unauthorized",
|
|
174
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
PATCH: {
|
|
179
|
+
summary: "Override a notification type's channel eligibility and opt-out governance for the caller's tenant",
|
|
180
|
+
description: "Tenant-scoped operator overrides for a notification type (stored in `notification_type_overrides`; other tenants are unaffected). `channels` replaces the code-declared eligibility (a channel outside the effective set is completely off for the tenant: it beats user preferences and `nonOptOut`, and preference UIs lock the cell). `nonOptOut` overrides the code-declared opt-out governance (`true` forces the type on for users, `false` makes a required type user-editable). Omitted fields stay untouched; pass `null` to clear a stored override and inherit the code declaration. Sends the standard optimistic-lock header contract: pass the item's `updatedAt` as `x-om-ext-optimistic-lock-expected-updated-at` to detect concurrent edits (409 on mismatch).",
|
|
181
|
+
tags: ["Notifications"],
|
|
182
|
+
requestBody: {
|
|
183
|
+
required: true,
|
|
184
|
+
content: {
|
|
185
|
+
"application/json": { schema: updateNotificationTypeSchema }
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
responses: {
|
|
189
|
+
200: {
|
|
190
|
+
description: "Override saved",
|
|
191
|
+
content: {
|
|
192
|
+
"application/json": {
|
|
193
|
+
schema: z.object({
|
|
194
|
+
ok: z.literal(true),
|
|
195
|
+
item: notificationTypeItemSchema
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
400: {
|
|
201
|
+
description: "Invalid request body",
|
|
202
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
203
|
+
},
|
|
204
|
+
401: {
|
|
205
|
+
description: "Unauthorized",
|
|
206
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
207
|
+
},
|
|
208
|
+
404: {
|
|
209
|
+
description: "Unknown notification type",
|
|
210
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
211
|
+
},
|
|
212
|
+
409: {
|
|
213
|
+
description: "Optimistic-lock conflict \u2014 another operator saved a newer override",
|
|
214
|
+
content: { "application/json": { schema: errorResponseSchema } }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
export {
|
|
220
|
+
GET,
|
|
221
|
+
PATCH,
|
|
222
|
+
metadata,
|
|
223
|
+
openApi
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/notifications/api/types/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { conflict, isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { z } from 'zod'\nimport { NotificationType, NotificationTypeOverride } from '../../data/entities'\nimport { syncNotificationTypes } from '../../lib/notification-type-registry'\nimport { applyTypeOverride, resolveCatalogueTranslate, typeItem } from '../../lib/typeCatalogue'\nimport { notificationTypeItemSchema, updateNotificationTypeSchema } from '../../data/validators'\nimport { errorResponseSchema } from '../openapi'\nimport {\n NOTIFICATION_SETTINGS_RESOURCE_KIND,\n notificationCrudErrorResponse,\n runGuardedNotificationWrite,\n} from '../../lib/routeHelpers'\n\nconst logger = createLogger('notifications').child({ component: 'types-api' })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['notifications.view'] },\n PATCH: { requireAuth: true, requireFeatures: ['notifications.manage'] },\n}\n\nexport async function GET(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth.tenantId) {\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n // Read-through mirror: ensure the catalogue is reflected in the DB (once per process).\n await syncNotificationTypes(em)\n const rows = await em.find(\n NotificationType,\n { $or: [{ tenantId: null }, { tenantId: auth.tenantId }] },\n { orderBy: { id: 'asc' } },\n )\n const overrides = await em.find(NotificationTypeOverride, {\n tenantId: auth.tenantId,\n notificationTypeId: { $in: rows.map((row) => row.id) },\n })\n const overrideByType = new Map(overrides.map((override) => [override.notificationTypeId, override]))\n const translate = await resolveCatalogueTranslate(req)\n return NextResponse.json({\n items: rows.map((row) => typeItem(row, overrideByType.get(row.id), translate)),\n })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') await disposable.dispose()\n }\n}\n\nexport async function PATCH(req: Request) {\n // Admin write: the echoed item stays on the request-context locale (no `?locale=`\n // override), so the confirmation matches the operator's own session.\n const { t, translate } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth.tenantId) {\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n const tenantId = auth.tenantId\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json(\n { error: t('api.errors.invalidPayload', 'Invalid request body') },\n { status: 400 },\n )\n }\n\n const parsed = updateNotificationTypeSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json(\n { error: t('notifications.types.invalidChannels', 'Invalid channels payload') },\n { status: 400 },\n )\n }\n\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n await syncNotificationTypes(em)\n const row = await em.findOne(NotificationType, {\n id: parsed.data.id,\n $or: [{ tenantId: null }, { tenantId }],\n })\n if (!row) {\n return NextResponse.json(\n { error: t('notifications.types.unknownType', 'Unknown notification type') },\n { status: 404 },\n )\n }\n const existing = await em.findOne(NotificationTypeOverride, {\n tenantId,\n notificationTypeId: row.id,\n })\n\n const guarded = await runGuardedNotificationWrite(\n container,\n {\n tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub ?? null,\n },\n req,\n {\n resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,\n resourceId: row.id,\n operation: 'update',\n payload: parsed.data as unknown as Record<string, unknown>,\n },\n async () => {\n // Same 409 contract as the CRUD guard: a stale admin view (another operator saved\n // since this client loaded the catalogue) must not silently clobber the newer\n // override \u2014 the full `channels` array replaces, so a blind write loses edits.\n enforceCommandOptimisticLock({\n resourceKind: NOTIFICATION_SETTINGS_RESOURCE_KIND,\n resourceId: row.id,\n current: existing?.updatedAt ?? null,\n request: req,\n })\n const nextChannels = parsed.data.channels !== undefined ? parsed.data.channels : existing?.channels ?? null\n const nextNonOptOut = parsed.data.nonOptOut !== undefined ? parsed.data.nonOptOut : existing?.nonOptOut ?? null\n const override = applyTypeOverride(em, {\n tenantId,\n notificationTypeId: row.id,\n existing,\n nextChannels,\n nextNonOptOut,\n })\n try {\n await em.flush()\n } catch (err) {\n // Concurrent first-write race: another operator created the override for this (tenant, type)\n // between our read (existing === null) and this flush, so the partial unique index rejects\n // ours. There was no version to compare, so the optimistic-lock guard couldn't fire \u2014 surface\n // the same 409 it would rather than letting the unique violation fall through to a generic 500.\n if (isUniqueViolation(err)) {\n throw conflict(\n t(\n 'notifications.types.overrideConflict',\n 'Another operator just saved this notification type. Reload and try again.',\n ),\n )\n }\n throw err\n }\n return typeItem(row, override, translate)\n },\n )\n if (!guarded.ok) return guarded.response\n return NextResponse.json({ ok: true, item: guarded.result })\n } catch (error) {\n const crudResponse = notificationCrudErrorResponse(error)\n if (crudResponse) return crudResponse\n logger.error('notification type override update failed', {\n typeId: parsed.data.id,\n error: error instanceof Error ? error.message : String(error),\n })\n return NextResponse.json(\n { error: t('api.errors.internal', 'Internal error') },\n { status: 500 },\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') await disposable.dispose()\n }\n}\n\nexport const openApi = {\n GET: {\n summary: 'List notification types',\n description: 'Returns the notification type catalogue (system-wide + tenant) so clients can render a preferences screen. `channels` is the effective channel eligibility for the caller\\'s tenant (stored override, else the code-declared set; `null` = every channel); a channel outside it never delivers in that tenant and cannot be enabled by users. `updatedAt` is the override row\\'s optimistic-lock version (`null` when the tenant stores no override). `label`, `description` and `categoryLabel` are resolved server-side using the request locale (`?locale=`, `x-locale`, the `locale` cookie, or `Accept-Language`) so clients without the app dictionary can render them directly; `categoryLabel` falls back to the raw category key when the owning module ships no `notifications.categories.<key>` translation, so `categoryLabel === category` signals \"no server-side label\". `category` defaults to the prefix before the first dot in the type id and is the stable grouping key \u2014 group on `category`, display `categoryLabel`.',\n tags: ['Notifications'],\n responses: {\n 200: {\n description: 'Notification type catalogue',\n content: {\n 'application/json': {\n schema: z.object({ items: z.array(notificationTypeItemSchema) }),\n },\n },\n },\n 401: {\n description: 'Unauthorized',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n },\n },\n PATCH: {\n summary: 'Override a notification type\\'s channel eligibility and opt-out governance for the caller\\'s tenant',\n description: 'Tenant-scoped operator overrides for a notification type (stored in `notification_type_overrides`; other tenants are unaffected). `channels` replaces the code-declared eligibility (a channel outside the effective set is completely off for the tenant: it beats user preferences and `nonOptOut`, and preference UIs lock the cell). `nonOptOut` overrides the code-declared opt-out governance (`true` forces the type on for users, `false` makes a required type user-editable). Omitted fields stay untouched; pass `null` to clear a stored override and inherit the code declaration. Sends the standard optimistic-lock header contract: pass the item\\'s `updatedAt` as `x-om-ext-optimistic-lock-expected-updated-at` to detect concurrent edits (409 on mismatch).',\n tags: ['Notifications'],\n requestBody: {\n required: true,\n content: {\n 'application/json': { schema: updateNotificationTypeSchema },\n },\n },\n responses: {\n 200: {\n description: 'Override saved',\n content: {\n 'application/json': {\n schema: z.object({\n ok: z.literal(true),\n item: notificationTypeItemSchema,\n }),\n },\n },\n },\n 400: {\n description: 'Invalid request body',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n 401: {\n description: 'Unauthorized',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n 404: {\n description: 'Unknown notification type',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n 409: {\n description: 'Optimistic-lock conflict \u2014 another operator saved a newer override',\n content: { 'application/json': { schema: errorResponseSchema } },\n },\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
buildNotificationReadScopeWhere,
|
|
13
13
|
getNotificationReadScopeTagOrganizationIds
|
|
14
14
|
} from "../../lib/notificationScope.js";
|
|
15
|
+
import { inAppVisibleFilter } from "../../lib/notificationVisibility.js";
|
|
15
16
|
const metadata = {
|
|
16
17
|
GET: { requireAuth: true }
|
|
17
18
|
};
|
|
@@ -50,7 +51,10 @@ async function GET(req) {
|
|
|
50
51
|
recipientUserId: userId,
|
|
51
52
|
tenantId: scope.tenantId,
|
|
52
53
|
status: "unread",
|
|
53
|
-
|
|
54
|
+
$and: [
|
|
55
|
+
buildNotificationReadScopeWhere(scope),
|
|
56
|
+
inAppVisibleFilter()
|
|
57
|
+
]
|
|
54
58
|
});
|
|
55
59
|
if (cache && cacheKey) {
|
|
56
60
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/notifications/api/unread-count/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { createHash } from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/core'\nimport { runWithCacheTenant } from '@open-mercato/cache'\nimport {\n buildCollectionTags,\n isCrudCacheEnabled,\n resolveCrudCache,\n} from '@open-mercato/shared/lib/crud/cache'\nimport { Notification } from '../../data/entities'\nimport { unreadCountResponseSchema } from '../openapi'\nimport { resolveGuardedNotificationContext } from '../../lib/routeHelpers'\nimport {\n buildNotificationReadScopeWhere,\n getNotificationReadScopeTagOrganizationIds,\n} from '../../lib/notificationScope'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst UNREAD_COUNT_RESOURCE = 'notifications.notification'\nconst UNREAD_COUNT_TTL_MS = 10_000\n\nfunction buildUnreadCountCacheKey(params: {\n userId: string\n organizationId: string | null\n organizationIds: string[]\n}): string {\n const normalizedIds = Array.from(new Set(\n params.organizationIds.filter((value) => value.trim().length > 0),\n )).sort((left, right) => left.localeCompare(right))\n const scopeKey = normalizedIds.length === 0\n ? 'no-access'\n : `${params.organizationId ?? 'none'}:scope=${createHash('sha256')\n .update(normalizedIds.join('\\0'))\n .digest('hex')\n .slice(0, 16)}`\n return `notifications:unread-count:u=${params.userId}:org=${scopeKey}`\n}\n\nexport async function GET(req: Request) {\n const resolved = await resolveGuardedNotificationContext(req)\n if (!resolved.ok) return resolved.response\n const { scope, ctx } = resolved\n const em = ctx.container.resolve('em') as EntityManager\n\n const userId = scope.userId\n const cacheableOrganizationIds = Array.isArray(scope.organizationIds)\n ? scope.organizationIds\n : null\n // Unrestricted and omitted legacy scopes cannot be safely tagged for invalidation:\n // org-specific writes only invalidate their own collection tag. Keep those scopes\n // uncached rather than serving a stale tenant-wide total until the TTL expires.\n const cache = userId && cacheableOrganizationIds && isCrudCacheEnabled()\n ? resolveCrudCache(ctx.container)\n : null\n const cacheKey = cache && userId && cacheableOrganizationIds\n ? buildUnreadCountCacheKey({\n userId,\n organizationId: scope.organizationId,\n organizationIds: cacheableOrganizationIds,\n })\n : null\n\n if (cache && cacheKey) {\n try {\n const cached = await runWithCacheTenant(scope.tenantId, () => cache.get(cacheKey))\n if (typeof cached === 'number') {\n return Response.json({ unreadCount: cached })\n }\n } catch {}\n }\n\n const count = await em.count(Notification, {\n recipientUserId: userId,\n tenantId: scope.tenantId,\n status: 'unread',\n
|
|
5
|
-
"mappings": "AAAA,SAAS,kBAAkB;AAE3B,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,iCAAiC;AAC1C,SAAS,yCAAyC;AAClD;AAAA,EACE;AAAA,EACA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["import { createHash } from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/core'\nimport { runWithCacheTenant } from '@open-mercato/cache'\nimport {\n buildCollectionTags,\n isCrudCacheEnabled,\n resolveCrudCache,\n} from '@open-mercato/shared/lib/crud/cache'\nimport { Notification } from '../../data/entities'\nimport { unreadCountResponseSchema } from '../openapi'\nimport { resolveGuardedNotificationContext } from '../../lib/routeHelpers'\nimport {\n buildNotificationReadScopeWhere,\n getNotificationReadScopeTagOrganizationIds,\n} from '../../lib/notificationScope'\nimport { inAppVisibleFilter } from '../../lib/notificationVisibility'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst UNREAD_COUNT_RESOURCE = 'notifications.notification'\nconst UNREAD_COUNT_TTL_MS = 10_000\n\nfunction buildUnreadCountCacheKey(params: {\n userId: string\n organizationId: string | null\n organizationIds: string[]\n}): string {\n const normalizedIds = Array.from(new Set(\n params.organizationIds.filter((value) => value.trim().length > 0),\n )).sort((left, right) => left.localeCompare(right))\n const scopeKey = normalizedIds.length === 0\n ? 'no-access'\n : `${params.organizationId ?? 'none'}:scope=${createHash('sha256')\n .update(normalizedIds.join('\\0'))\n .digest('hex')\n .slice(0, 16)}`\n return `notifications:unread-count:u=${params.userId}:org=${scopeKey}`\n}\n\nexport async function GET(req: Request) {\n const resolved = await resolveGuardedNotificationContext(req)\n if (!resolved.ok) return resolved.response\n const { scope, ctx } = resolved\n const em = ctx.container.resolve('em') as EntityManager\n\n const userId = scope.userId\n const cacheableOrganizationIds = Array.isArray(scope.organizationIds)\n ? scope.organizationIds\n : null\n // Unrestricted and omitted legacy scopes cannot be safely tagged for invalidation:\n // org-specific writes only invalidate their own collection tag. Keep those scopes\n // uncached rather than serving a stale tenant-wide total until the TTL expires.\n const cache = userId && cacheableOrganizationIds && isCrudCacheEnabled()\n ? resolveCrudCache(ctx.container)\n : null\n const cacheKey = cache && userId && cacheableOrganizationIds\n ? buildUnreadCountCacheKey({\n userId,\n organizationId: scope.organizationId,\n organizationIds: cacheableOrganizationIds,\n })\n : null\n\n if (cache && cacheKey) {\n try {\n const cached = await runWithCacheTenant(scope.tenantId, () => cache.get(cacheKey))\n if (typeof cached === 'number') {\n return Response.json({ unreadCount: cached })\n }\n } catch {}\n }\n\n const count = await em.count(Notification, {\n recipientUserId: userId,\n tenantId: scope.tenantId,\n status: 'unread',\n $and: [\n buildNotificationReadScopeWhere(scope),\n inAppVisibleFilter(),\n ],\n })\n\n if (cache && cacheKey) {\n try {\n await runWithCacheTenant(scope.tenantId, () =>\n cache.set(cacheKey, count, {\n ttl: UNREAD_COUNT_TTL_MS,\n tags: buildCollectionTags(\n UNREAD_COUNT_RESOURCE,\n scope.tenantId,\n getNotificationReadScopeTagOrganizationIds(scope),\n ),\n }),\n )\n } catch {}\n }\n\n return Response.json({ unreadCount: count })\n}\n\nexport const openApi = {\n GET: {\n summary: 'Get unread notification count',\n tags: ['Notifications'],\n responses: {\n 200: {\n description: 'Unread count',\n content: {\n 'application/json': {\n schema: unreadCountResponseSchema,\n },\n },\n },\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB;AAE3B,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,iCAAiC;AAC1C,SAAS,yCAAyC;AAClD;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AAE5B,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,KAAK;AAC3B;AAEA,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAE5B,SAAS,yBAAyB,QAIvB;AACT,QAAM,gBAAgB,MAAM,KAAK,IAAI;AAAA,IACnC,OAAO,gBAAgB,OAAO,CAAC,UAAU,MAAM,KAAK,EAAE,SAAS,CAAC;AAAA,EAClE,CAAC,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAClD,QAAM,WAAW,cAAc,WAAW,IACtC,cACA,GAAG,OAAO,kBAAkB,MAAM,UAAU,WAAW,QAAQ,EAC5D,OAAO,cAAc,KAAK,IAAI,CAAC,EAC/B,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE,CAAC;AACnB,SAAO,gCAAgC,OAAO,MAAM,QAAQ,QAAQ;AACtE;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,WAAW,MAAM,kCAAkC,GAAG;AAC5D,MAAI,CAAC,SAAS,GAAI,QAAO,SAAS;AAClC,QAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AAErC,QAAM,SAAS,MAAM;AACrB,QAAM,2BAA2B,MAAM,QAAQ,MAAM,eAAe,IAChE,MAAM,kBACN;AAIJ,QAAM,QAAQ,UAAU,4BAA4B,mBAAmB,IACnE,iBAAiB,IAAI,SAAS,IAC9B;AACJ,QAAM,WAAW,SAAS,UAAU,2BAChC,yBAAyB;AAAA,IACvB;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,iBAAiB;AAAA,EACnB,CAAC,IACD;AAEJ,MAAI,SAAS,UAAU;AACrB,QAAI;AACF,YAAM,SAAS,MAAM,mBAAmB,MAAM,UAAU,MAAM,MAAM,IAAI,QAAQ,CAAC;AACjF,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO,SAAS,KAAK,EAAE,aAAa,OAAO,CAAC;AAAA,MAC9C;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,QAAM,QAAQ,MAAM,GAAG,MAAM,cAAc;AAAA,IACzC,iBAAiB;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,QAAQ;AAAA,IACR,MAAM;AAAA,MACJ,gCAAgC,KAAK;AAAA,MACrC,mBAAmB;AAAA,IACrB;AAAA,EACF,CAAC;AAED,MAAI,SAAS,UAAU;AACrB,QAAI;AACF,YAAM;AAAA,QAAmB,MAAM;AAAA,QAAU,MACvC,MAAM,IAAI,UAAU,OAAO;AAAA,UACzB,KAAK;AAAA,UACL,MAAM;AAAA,YACJ;AAAA,YACA,MAAM;AAAA,YACN,2CAA2C,KAAK;AAAA,UAClD;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,SAAO,SAAS,KAAK,EAAE,aAAa,MAAM,CAAC;AAC7C;AAEO,MAAM,UAAU;AAAA,EACrB,KAAK;AAAA,IACH,SAAS;AAAA,IACT,MAAM,CAAC,eAAe;AAAA,IACtB,WAAW;AAAA,MACT,KAAK;AAAA,QACH,aAAa;AAAA,QACb,SAAS;AAAA,UACP,oBAAoB;AAAA,YAClB,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
3
|
+
import { NotificationUserPreferencesAdminPageClient } from "../../../frontend/NotificationUserPreferencesAdminPageClient.js";
|
|
4
|
+
async function NotificationUserPreferencesAdminPage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(NotificationUserPreferencesAdminPageClient, {}) }) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
NotificationUserPreferencesAdminPage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/notifications/backend/notifications/user-preferences/page.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { NotificationUserPreferencesAdminPageClient } from '../../../frontend/NotificationUserPreferencesAdminPageClient'\n\nexport default async function NotificationUserPreferencesAdminPage() {\n return (\n <Page>\n <PageBody>\n <NotificationUserPreferencesAdminPageClient />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAOQ;AAPR,SAAS,MAAM,gBAAgB;AAC/B,SAAS,kDAAkD;AAE3D,eAAO,uCAA8D;AACnE,SACE,oBAAC,QACC,8BAAC,YACC,8BAAC,8CAA2C,GAC9C,GACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
const usersCogIcon = React.createElement(
|
|
3
|
+
"svg",
|
|
4
|
+
{ width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
5
|
+
React.createElement("path", { d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" }),
|
|
6
|
+
React.createElement("circle", { cx: 9, cy: 7, r: 4 }),
|
|
7
|
+
React.createElement("circle", { cx: 19, cy: 16, r: 2 }),
|
|
8
|
+
React.createElement("path", { d: "M19 12v1" }),
|
|
9
|
+
React.createElement("path", { d: "M19 19v1" })
|
|
10
|
+
);
|
|
11
|
+
const metadata = {
|
|
12
|
+
requireAuth: true,
|
|
13
|
+
requireFeatures: ["notifications.manage_user_preferences"],
|
|
14
|
+
pageTitle: "User Notification Preferences",
|
|
15
|
+
pageTitleKey: "notifications.preferences.admin.pageTitle",
|
|
16
|
+
pageGroup: "Auth",
|
|
17
|
+
pageGroupKey: "settings.sections.auth",
|
|
18
|
+
pageOrder: 5,
|
|
19
|
+
icon: usersCogIcon,
|
|
20
|
+
pageContext: "settings",
|
|
21
|
+
breadcrumb: [
|
|
22
|
+
{ label: "User Notification Preferences", labelKey: "notifications.preferences.admin.pageTitle" }
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
metadata
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/notifications/backend/notifications/user-preferences/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import React from 'react'\n\nconst usersCogIcon = React.createElement(\n 'svg',\n { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },\n React.createElement('path', { d: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2' }),\n React.createElement('circle', { cx: 9, cy: 7, r: 4 }),\n React.createElement('circle', { cx: 19, cy: 16, r: 2 }),\n React.createElement('path', { d: 'M19 12v1' }),\n React.createElement('path', { d: 'M19 19v1' }),\n)\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['notifications.manage_user_preferences'],\n pageTitle: 'User Notification Preferences',\n pageTitleKey: 'notifications.preferences.admin.pageTitle',\n pageGroup: 'Auth',\n pageGroupKey: 'settings.sections.auth',\n pageOrder: 5,\n icon: usersCogIcon,\n pageContext: 'settings' as const,\n breadcrumb: [\n { label: 'User Notification Preferences', labelKey: 'notifications.preferences.admin.pageTitle' },\n ],\n} as const\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAElB,MAAM,eAAe,MAAM;AAAA,EACzB;AAAA,EACA,EAAE,OAAO,IAAI,QAAQ,IAAI,SAAS,aAAa,MAAM,QAAQ,QAAQ,gBAAgB,aAAa,GAAG,eAAe,SAAS,gBAAgB,QAAQ;AAAA,EACrJ,MAAM,cAAc,QAAQ,EAAE,GAAG,4CAA4C,CAAC;AAAA,EAC9E,MAAM,cAAc,UAAU,EAAE,IAAI,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC;AAAA,EACpD,MAAM,cAAc,UAAU,EAAE,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;AAAA,EACtD,MAAM,cAAc,QAAQ,EAAE,GAAG,WAAW,CAAC;AAAA,EAC7C,MAAM,cAAc,QAAQ,EAAE,GAAG,WAAW,CAAC;AAC/C;AAEO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,uCAAuC;AAAA,EACzD,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,aAAa;AAAA,EACb,YAAY;AAAA,IACV,EAAE,OAAO,iCAAiC,UAAU,4CAA4C;AAAA,EAClG;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
3
|
+
import { NotificationPreferencesPageClient } from "../../../frontend/NotificationPreferencesPageClient.js";
|
|
4
|
+
async function NotificationPreferencesProfilePage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(NotificationPreferencesPageClient, {}) }) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
NotificationPreferencesProfilePage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/notifications/backend/profile/notification-preferences/page.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { NotificationPreferencesPageClient } from '../../../frontend/NotificationPreferencesPageClient'\n\nexport default async function NotificationPreferencesProfilePage() {\n return (\n <Page>\n <PageBody>\n <NotificationPreferencesPageClient />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAOQ;AAPR,SAAS,MAAM,gBAAgB;AAC/B,SAAS,yCAAyC;AAElD,eAAO,qCAA4D;AACjE,SACE,oBAAC,QACC,8BAAC,YACC,8BAAC,qCAAkC,GACrC,GACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|