@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,235 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
6
|
+
import { DataTable } from "@open-mercato/ui/backend/DataTable";
|
|
7
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
8
|
+
import { RowActions } from "@open-mercato/ui/backend/RowActions";
|
|
9
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
10
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
11
|
+
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
12
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
13
|
+
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
14
|
+
function formatDate(value, t) {
|
|
15
|
+
if (!value) return t("devices.list.noValue");
|
|
16
|
+
try {
|
|
17
|
+
const date = new Date(value);
|
|
18
|
+
if (Number.isNaN(date.getTime())) return t("devices.list.noValue");
|
|
19
|
+
return date.toLocaleString();
|
|
20
|
+
} catch {
|
|
21
|
+
return t("devices.list.noValue");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function DevicesAdminListPage() {
|
|
25
|
+
const [rows, setRows] = React.useState([]);
|
|
26
|
+
const [page, setPage] = React.useState(1);
|
|
27
|
+
const [total, setTotal] = React.useState(0);
|
|
28
|
+
const [totalPages, setTotalPages] = React.useState(1);
|
|
29
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
30
|
+
const [reloadToken, setReloadToken] = React.useState(0);
|
|
31
|
+
const scopeVersion = useOrganizationScopeVersion();
|
|
32
|
+
const t = useT();
|
|
33
|
+
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
34
|
+
const [filterValues, setFilterValues] = React.useState({});
|
|
35
|
+
const [userOptions, setUserOptions] = React.useState([]);
|
|
36
|
+
const loadUserOptions = React.useCallback(async (query) => {
|
|
37
|
+
const params = new URLSearchParams();
|
|
38
|
+
params.set("page", "1");
|
|
39
|
+
params.set("pageSize", "20");
|
|
40
|
+
if (query && query.trim().length > 0) params.set("search", query.trim());
|
|
41
|
+
const call = await apiCall(
|
|
42
|
+
`/api/auth/users?${params.toString()}`,
|
|
43
|
+
{ headers: { "x-om-forbidden-redirect": "0" } },
|
|
44
|
+
{ fallback: null }
|
|
45
|
+
).catch(() => null);
|
|
46
|
+
if (!call || !call.ok) return [];
|
|
47
|
+
const next = (call.result?.items ?? []).flatMap((item) => {
|
|
48
|
+
if (!item || typeof item.id !== "string" || !item.id.trim()) return [];
|
|
49
|
+
const name = typeof item.name === "string" && item.name.trim() ? item.name.trim() : null;
|
|
50
|
+
const email = typeof item.email === "string" && item.email.trim() ? item.email.trim() : null;
|
|
51
|
+
const label = name ?? email ?? item.id;
|
|
52
|
+
return [{ value: item.id, label, description: email && email !== label ? email : null }];
|
|
53
|
+
});
|
|
54
|
+
setUserOptions((prev) => {
|
|
55
|
+
const map = new Map(prev.map((opt) => [opt.value, opt]));
|
|
56
|
+
for (const opt of next) map.set(opt.value, opt);
|
|
57
|
+
return Array.from(map.values());
|
|
58
|
+
});
|
|
59
|
+
return next;
|
|
60
|
+
}, []);
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
void loadUserOptions();
|
|
63
|
+
}, [loadUserOptions, scopeVersion]);
|
|
64
|
+
const userLabelById = React.useMemo(
|
|
65
|
+
() => new Map(userOptions.map((opt) => [opt.value, opt.label])),
|
|
66
|
+
[userOptions]
|
|
67
|
+
);
|
|
68
|
+
const filters = React.useMemo(() => [
|
|
69
|
+
{
|
|
70
|
+
id: "platform",
|
|
71
|
+
label: t("devices.list.columns.platform"),
|
|
72
|
+
type: "select",
|
|
73
|
+
options: [
|
|
74
|
+
{ value: "ios", label: "iOS" },
|
|
75
|
+
{ value: "android", label: "Android" },
|
|
76
|
+
{ value: "web", label: "Web" }
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "userId",
|
|
81
|
+
label: t("devices.list.columns.user"),
|
|
82
|
+
type: "combobox",
|
|
83
|
+
options: userOptions,
|
|
84
|
+
loadOptions: loadUserOptions
|
|
85
|
+
}
|
|
86
|
+
], [t, userOptions, loadUserOptions]);
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
let cancelled = false;
|
|
89
|
+
async function load() {
|
|
90
|
+
setIsLoading(true);
|
|
91
|
+
try {
|
|
92
|
+
const params = new URLSearchParams();
|
|
93
|
+
params.set("page", String(page));
|
|
94
|
+
params.set("pageSize", "50");
|
|
95
|
+
const platform = typeof filterValues.platform === "string" ? filterValues.platform.trim() : "";
|
|
96
|
+
const userId = typeof filterValues.userId === "string" ? filterValues.userId.trim() : "";
|
|
97
|
+
if (platform) params.set("platform", platform);
|
|
98
|
+
if (userId) params.set("userId", userId);
|
|
99
|
+
const fallback = { items: [], total: 0, page, totalPages: 1 };
|
|
100
|
+
const call = await apiCall(`/api/devices/admin/devices?${params.toString()}`, void 0, { fallback });
|
|
101
|
+
if (!call.ok) {
|
|
102
|
+
const errorPayload = call.result;
|
|
103
|
+
const message = typeof errorPayload?.error === "string" ? errorPayload.error : t("devices.list.error.loadFailed");
|
|
104
|
+
flash(message, "error");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const payload = call.result ?? fallback;
|
|
108
|
+
if (!cancelled) {
|
|
109
|
+
setRows(Array.isArray(payload.items) ? payload.items : []);
|
|
110
|
+
setTotal(payload.total || 0);
|
|
111
|
+
setTotalPages(payload.totalPages || 1);
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (!cancelled) {
|
|
115
|
+
const message = error instanceof Error ? error.message : t("devices.list.error.loadFailed");
|
|
116
|
+
flash(message, "error");
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
if (!cancelled) setIsLoading(false);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
load();
|
|
123
|
+
return () => {
|
|
124
|
+
cancelled = true;
|
|
125
|
+
};
|
|
126
|
+
}, [page, reloadToken, scopeVersion, filterValues, t]);
|
|
127
|
+
const handleDeactivate = React.useCallback(async (row) => {
|
|
128
|
+
const confirmed = await confirm({
|
|
129
|
+
title: t("devices.list.confirmDeactivate"),
|
|
130
|
+
variant: "destructive"
|
|
131
|
+
});
|
|
132
|
+
if (!confirmed) return;
|
|
133
|
+
try {
|
|
134
|
+
const call = await apiCall(
|
|
135
|
+
`/api/devices/admin/devices/${encodeURIComponent(row.id)}`,
|
|
136
|
+
{ method: "DELETE" },
|
|
137
|
+
{ fallback: null }
|
|
138
|
+
);
|
|
139
|
+
if (!call.ok) {
|
|
140
|
+
const errorPayload = call.result;
|
|
141
|
+
const message = typeof errorPayload?.error === "string" ? errorPayload.error : t("devices.list.error.deactivateFailed");
|
|
142
|
+
flash(message, "error");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
flash(t("devices.list.success.deactivated"), "success");
|
|
146
|
+
setReloadToken((token) => token + 1);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const message = error instanceof Error ? error.message : t("devices.list.error.deactivateFailed");
|
|
149
|
+
flash(message, "error");
|
|
150
|
+
}
|
|
151
|
+
}, [confirm, t]);
|
|
152
|
+
const columns = React.useMemo(() => [
|
|
153
|
+
{
|
|
154
|
+
accessorKey: "device_id",
|
|
155
|
+
header: t("devices.list.columns.device"),
|
|
156
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("code", { className: "text-xs", children: row.original.device_id })
|
|
157
|
+
},
|
|
158
|
+
{ accessorKey: "platform", header: t("devices.list.columns.platform") },
|
|
159
|
+
{
|
|
160
|
+
accessorKey: "user_id",
|
|
161
|
+
header: t("devices.list.columns.user"),
|
|
162
|
+
cell: ({ row }) => {
|
|
163
|
+
const userId = row.original.user_id;
|
|
164
|
+
const label = userLabelById.get(userId);
|
|
165
|
+
return (
|
|
166
|
+
// Stop the click bubbling to the row, whose default action navigates to the device edit page.
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
Link,
|
|
169
|
+
{
|
|
170
|
+
href: `/backend/users/${encodeURIComponent(userId)}/edit`,
|
|
171
|
+
className: "text-primary hover:underline",
|
|
172
|
+
onClick: (e) => e.stopPropagation(),
|
|
173
|
+
children: label ?? /* @__PURE__ */ jsx("code", { className: "text-xs", children: userId })
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
accessorKey: "client_app_version",
|
|
181
|
+
header: t("devices.list.columns.appVersion"),
|
|
182
|
+
cell: ({ row }) => row.original.client_app_version || t("devices.list.noValue")
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
accessorKey: "os_version",
|
|
186
|
+
header: t("devices.list.columns.osVersion"),
|
|
187
|
+
cell: ({ row }) => row.original.os_version || t("devices.list.noValue")
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
accessorKey: "push_provider",
|
|
191
|
+
header: t("devices.list.columns.pushProvider"),
|
|
192
|
+
cell: ({ row }) => row.original.push_provider || t("devices.list.noValue")
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
accessorKey: "last_seen_at",
|
|
196
|
+
header: t("devices.list.columns.lastSeen"),
|
|
197
|
+
cell: ({ row }) => formatDate(row.original.last_seen_at, t)
|
|
198
|
+
}
|
|
199
|
+
], [t, userLabelById]);
|
|
200
|
+
return /* @__PURE__ */ jsxs(Page, { children: [
|
|
201
|
+
/* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
202
|
+
DataTable,
|
|
203
|
+
{
|
|
204
|
+
title: t("devices.list.title"),
|
|
205
|
+
actions: /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/backend/devices/create", children: t("devices.list.actions.register") }) }),
|
|
206
|
+
columns,
|
|
207
|
+
data: rows,
|
|
208
|
+
filters,
|
|
209
|
+
filterValues,
|
|
210
|
+
onFiltersApply: (values) => {
|
|
211
|
+
setFilterValues(values);
|
|
212
|
+
setPage(1);
|
|
213
|
+
},
|
|
214
|
+
onFiltersClear: () => {
|
|
215
|
+
setFilterValues({});
|
|
216
|
+
setPage(1);
|
|
217
|
+
},
|
|
218
|
+
perspective: { tableId: "devices.list" },
|
|
219
|
+
rowActions: (row) => /* @__PURE__ */ jsx(RowActions, { items: [
|
|
220
|
+
{ id: "edit", label: t("devices.list.actions.edit"), href: `/backend/devices/${row.id}` },
|
|
221
|
+
{ id: "deactivate", label: t("devices.list.actions.deactivate"), destructive: true, onSelect: () => {
|
|
222
|
+
void handleDeactivate(row);
|
|
223
|
+
} }
|
|
224
|
+
] }),
|
|
225
|
+
pagination: { page, pageSize: 50, total, totalPages, onPageChange: setPage },
|
|
226
|
+
isLoading
|
|
227
|
+
}
|
|
228
|
+
) }),
|
|
229
|
+
ConfirmDialogElement
|
|
230
|
+
] });
|
|
231
|
+
}
|
|
232
|
+
export {
|
|
233
|
+
DevicesAdminListPage as default
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/devices/backend/devices/page.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype Row = {\n id: string\n user_id: string\n device_id: string\n platform: string\n client_app_version: string | null\n os_version: string | null\n push_provider: string | null\n push_token_updated_at: string | null\n last_seen_at: string | null\n created_at: string | null\n}\n\ntype ResponsePayload = {\n items: Row[]\n total: number\n page?: number\n pageSize?: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null, t: (key: string) => string) {\n if (!value) return t('devices.list.noValue')\n try {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return t('devices.list.noValue')\n return date.toLocaleString()\n } catch {\n return t('devices.list.noValue')\n }\n}\n\nexport default function DevicesAdminListPage() {\n const [rows, setRows] = React.useState<Row[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [userOptions, setUserOptions] = React.useState<{ value: string; label: string; description?: string | null }[]>([])\n\n // Devices admins may not hold auth.users.list; degrade gracefully (no options) instead of redirecting.\n const loadUserOptions = React.useCallback(async (query?: string) => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '20')\n if (query && query.trim().length > 0) params.set('search', query.trim())\n const call = await apiCall<{ items?: { id: string; name?: string | null; email?: string | null }[] }>(\n `/api/auth/users?${params.toString()}`,\n { headers: { 'x-om-forbidden-redirect': '0' } },\n { fallback: null },\n ).catch(() => null)\n if (!call || !call.ok) return []\n const next = (call.result?.items ?? []).flatMap((item) => {\n if (!item || typeof item.id !== 'string' || !item.id.trim()) return []\n const name = typeof item.name === 'string' && item.name.trim() ? item.name.trim() : null\n const email = typeof item.email === 'string' && item.email.trim() ? item.email.trim() : null\n const label = name ?? email ?? item.id\n return [{ value: item.id, label, description: email && email !== label ? email : null }]\n })\n setUserOptions((prev) => {\n const map = new Map(prev.map((opt) => [opt.value, opt]))\n for (const opt of next) map.set(opt.value, opt)\n return Array.from(map.values())\n })\n return next\n }, [])\n\n React.useEffect(() => { void loadUserOptions() }, [loadUserOptions, scopeVersion])\n\n // Reuse the picker cache to label the User column; rows whose owner isn't cached still link by id.\n const userLabelById = React.useMemo(\n () => new Map(userOptions.map((opt) => [opt.value, opt.label])),\n [userOptions],\n )\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'platform',\n label: t('devices.list.columns.platform'),\n type: 'select',\n options: [\n { value: 'ios', label: 'iOS' },\n { value: 'android', label: 'Android' },\n { value: 'web', label: 'Web' },\n ],\n },\n {\n id: 'userId',\n label: t('devices.list.columns.user'),\n type: 'combobox',\n options: userOptions,\n loadOptions: loadUserOptions,\n },\n ], [t, userOptions, loadUserOptions])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '50')\n const platform = typeof filterValues.platform === 'string' ? filterValues.platform.trim() : ''\n const userId = typeof filterValues.userId === 'string' ? filterValues.userId.trim() : ''\n if (platform) params.set('platform', platform)\n if (userId) params.set('userId', userId)\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(`/api/devices/admin/devices?${params.toString()}`, undefined, { fallback })\n if (!call.ok) {\n const errorPayload = call.result as { error?: string } | undefined\n const message = typeof errorPayload?.error === 'string' ? errorPayload.error : t('devices.list.error.loadFailed')\n flash(message, 'error')\n return\n }\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n }\n } catch (error) {\n if (!cancelled) {\n const message = error instanceof Error ? error.message : t('devices.list.error.loadFailed')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [page, reloadToken, scopeVersion, filterValues, t])\n\n const handleDeactivate = React.useCallback(async (row: Row) => {\n const confirmed = await confirm({\n title: t('devices.list.confirmDeactivate'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n // optimistic-lock-exempt: device deactivate is an idempotent soft-delete of a registry row, not a concurrent field edit\n const call = await apiCall<{ error?: string }>(\n `/api/devices/admin/devices/${encodeURIComponent(row.id)}`,\n { method: 'DELETE' },\n { fallback: null },\n )\n if (!call.ok) {\n const errorPayload = call.result as { error?: string } | undefined\n const message = typeof errorPayload?.error === 'string' ? errorPayload.error : t('devices.list.error.deactivateFailed')\n flash(message, 'error')\n return\n }\n flash(t('devices.list.success.deactivated'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n const message = error instanceof Error ? error.message : t('devices.list.error.deactivateFailed')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n const columns = React.useMemo<ColumnDef<Row>[]>(() => [\n {\n accessorKey: 'device_id',\n header: t('devices.list.columns.device'),\n cell: ({ row }) => <code className=\"text-xs\">{row.original.device_id}</code>,\n },\n { accessorKey: 'platform', header: t('devices.list.columns.platform') },\n {\n accessorKey: 'user_id',\n header: t('devices.list.columns.user'),\n cell: ({ row }) => {\n const userId = row.original.user_id\n const label = userLabelById.get(userId)\n return (\n // Stop the click bubbling to the row, whose default action navigates to the device edit page.\n <Link\n href={`/backend/users/${encodeURIComponent(userId)}/edit`}\n className=\"text-primary hover:underline\"\n onClick={(e) => e.stopPropagation()}\n >\n {label ?? <code className=\"text-xs\">{userId}</code>}\n </Link>\n )\n },\n },\n {\n accessorKey: 'client_app_version',\n header: t('devices.list.columns.appVersion'),\n cell: ({ row }) => row.original.client_app_version || t('devices.list.noValue'),\n },\n {\n accessorKey: 'os_version',\n header: t('devices.list.columns.osVersion'),\n cell: ({ row }) => row.original.os_version || t('devices.list.noValue'),\n },\n {\n accessorKey: 'push_provider',\n header: t('devices.list.columns.pushProvider'),\n cell: ({ row }) => row.original.push_provider || t('devices.list.noValue'),\n },\n {\n accessorKey: 'last_seen_at',\n header: t('devices.list.columns.lastSeen'),\n cell: ({ row }) => formatDate(row.original.last_seen_at, t),\n },\n ], [t, userLabelById])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('devices.list.title')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/devices/create\">{t('devices.list.actions.register')}</Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={(values) => { setFilterValues(values); setPage(1) }}\n onFiltersClear={() => { setFilterValues({}); setPage(1) }}\n perspective={{ tableId: 'devices.list' }}\n rowActions={(row) => (\n <RowActions items={[\n { id: 'edit', label: t('devices.list.actions.edit'), href: `/backend/devices/${row.id}` },\n { id: 'deactivate', label: t('devices.list.actions.deactivate'), destructive: true, onSelect: () => { void handleDeactivate(row) } },\n ]} />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
const deviceIcon = React.createElement(
|
|
3
|
+
"svg",
|
|
4
|
+
{ width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2 },
|
|
5
|
+
React.createElement("rect", { x: 5, y: 2, width: 14, height: 20, rx: 2, ry: 2 }),
|
|
6
|
+
React.createElement("path", { d: "M12 18h.01" })
|
|
7
|
+
);
|
|
8
|
+
const metadata = {
|
|
9
|
+
requireAuth: true,
|
|
10
|
+
requireFeatures: ["devices.admin"],
|
|
11
|
+
pageTitle: "Devices",
|
|
12
|
+
pageTitleKey: "devices.nav.devices",
|
|
13
|
+
pageGroup: "Auth",
|
|
14
|
+
pageGroupKey: "settings.sections.auth",
|
|
15
|
+
pageOrder: 4,
|
|
16
|
+
icon: deviceIcon,
|
|
17
|
+
pageContext: "settings",
|
|
18
|
+
breadcrumb: [{ label: "Devices", labelKey: "devices.nav.devices" }]
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
metadata
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/devices/backend/devices/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import React from 'react'\n\nconst deviceIcon = React.createElement(\n 'svg',\n { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },\n React.createElement('rect', { x: 5, y: 2, width: 14, height: 20, rx: 2, ry: 2 }),\n React.createElement('path', { d: 'M12 18h.01' }),\n)\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['devices.admin'],\n pageTitle: 'Devices',\n pageTitleKey: 'devices.nav.devices',\n pageGroup: 'Auth',\n pageGroupKey: 'settings.sections.auth',\n pageOrder: 4,\n icon: deviceIcon,\n pageContext: 'settings' as const,\n breadcrumb: [{ label: 'Devices', labelKey: 'devices.nav.devices' }],\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAElB,MAAM,aAAa,MAAM;AAAA,EACvB;AAAA,EACA,EAAE,OAAO,IAAI,QAAQ,IAAI,SAAS,aAAa,MAAM,QAAQ,QAAQ,gBAAgB,aAAa,EAAE;AAAA,EACpG,MAAM,cAAc,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,IAAI,QAAQ,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;AAAA,EAC/E,MAAM,cAAc,QAAQ,EAAE,GAAG,aAAa,CAAC;AACjD;AAEO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,eAAe;AAAA,EACjC,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,aAAa;AAAA,EACb,YAAY,CAAC,EAAE,OAAO,WAAW,UAAU,sBAAsB,CAAC;AACpE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
2
|
+
import { ensureOrganizationScope, ensureTenantScope } from "@open-mercato/shared/lib/commands/scope";
|
|
3
|
+
import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
|
|
4
|
+
import { emitCrudSideEffects, emitCrudUndoSideEffects } from "@open-mercato/shared/lib/commands/helpers";
|
|
5
|
+
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
6
|
+
import { assertFound } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
8
|
+
import { UserDevice } from "../data/entities.js";
|
|
9
|
+
import { deactivateDeviceCommandSchema } from "../data/validators.js";
|
|
10
|
+
import { emitDevicesEvent } from "../events.js";
|
|
11
|
+
import {
|
|
12
|
+
applySnapshot,
|
|
13
|
+
deviceEvents,
|
|
14
|
+
deviceIndexer,
|
|
15
|
+
serializeDevice
|
|
16
|
+
} from "./shared.js";
|
|
17
|
+
const deactivateDeviceCommand = {
|
|
18
|
+
id: "devices.user_devices.deactivate",
|
|
19
|
+
async prepare(rawInput, ctx) {
|
|
20
|
+
const parsed = deactivateDeviceCommandSchema.parse(rawInput);
|
|
21
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
22
|
+
const em = ctx.container.resolve("em").fork();
|
|
23
|
+
const device = await findOneWithDecryption(
|
|
24
|
+
em,
|
|
25
|
+
UserDevice,
|
|
26
|
+
{ id: parsed.id, tenantId: parsed.tenantId, deletedAt: null }
|
|
27
|
+
);
|
|
28
|
+
return device ? { before: serializeDevice(device) } : {};
|
|
29
|
+
},
|
|
30
|
+
async execute(rawInput, ctx) {
|
|
31
|
+
const parsed = deactivateDeviceCommandSchema.parse(rawInput);
|
|
32
|
+
const em = ctx.container.resolve("em").fork();
|
|
33
|
+
const device = assertFound(
|
|
34
|
+
await findOneWithDecryption(em, UserDevice, { id: parsed.id, tenantId: parsed.tenantId, deletedAt: null }),
|
|
35
|
+
"Device not found"
|
|
36
|
+
);
|
|
37
|
+
ensureTenantScope(ctx, device.tenantId);
|
|
38
|
+
if (device.organizationId) ensureOrganizationScope(ctx, device.organizationId);
|
|
39
|
+
await withAtomicFlush(em, [() => {
|
|
40
|
+
device.deletedAt = /* @__PURE__ */ new Date();
|
|
41
|
+
}], { transaction: true });
|
|
42
|
+
const de = ctx.container.resolve("dataEngine");
|
|
43
|
+
await emitCrudSideEffects({
|
|
44
|
+
dataEngine: de,
|
|
45
|
+
action: "deleted",
|
|
46
|
+
entity: device,
|
|
47
|
+
identifiers: {
|
|
48
|
+
id: device.id,
|
|
49
|
+
tenantId: device.tenantId,
|
|
50
|
+
organizationId: device.organizationId ?? null
|
|
51
|
+
},
|
|
52
|
+
indexer: deviceIndexer,
|
|
53
|
+
events: deviceEvents
|
|
54
|
+
});
|
|
55
|
+
await emitDevicesEvent(
|
|
56
|
+
"devices.user_device.deactivated",
|
|
57
|
+
{
|
|
58
|
+
id: device.id,
|
|
59
|
+
tenantId: device.tenantId,
|
|
60
|
+
organizationId: device.organizationId ?? null,
|
|
61
|
+
userId: device.userId,
|
|
62
|
+
deviceId: device.deviceId
|
|
63
|
+
},
|
|
64
|
+
{ persistent: true }
|
|
65
|
+
);
|
|
66
|
+
return { id: device.id };
|
|
67
|
+
},
|
|
68
|
+
buildLog: async ({ snapshots }) => {
|
|
69
|
+
const before = snapshots.before ?? null;
|
|
70
|
+
if (!before) return null;
|
|
71
|
+
return {
|
|
72
|
+
actionLabel: "Deactivate device",
|
|
73
|
+
resourceKind: "devices.user_device",
|
|
74
|
+
resourceId: before.id,
|
|
75
|
+
tenantId: before.tenantId,
|
|
76
|
+
organizationId: before.organizationId,
|
|
77
|
+
snapshotBefore: before,
|
|
78
|
+
payload: {
|
|
79
|
+
undo: { before }
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
undo: async ({ logEntry, ctx }) => {
|
|
84
|
+
const payload = extractUndoPayload(logEntry);
|
|
85
|
+
const before = payload?.before;
|
|
86
|
+
if (!before) return;
|
|
87
|
+
const em = ctx.container.resolve("em").fork();
|
|
88
|
+
const device = await findOneWithDecryption(em, UserDevice, { id: before.id });
|
|
89
|
+
if (!device) return;
|
|
90
|
+
await withAtomicFlush(em, [() => applySnapshot(device, before)], { transaction: true });
|
|
91
|
+
const de = ctx.container.resolve("dataEngine");
|
|
92
|
+
await emitCrudUndoSideEffects({
|
|
93
|
+
dataEngine: de,
|
|
94
|
+
action: "created",
|
|
95
|
+
entity: device,
|
|
96
|
+
identifiers: {
|
|
97
|
+
id: device.id,
|
|
98
|
+
tenantId: device.tenantId,
|
|
99
|
+
organizationId: device.organizationId ?? null
|
|
100
|
+
},
|
|
101
|
+
indexer: deviceIndexer,
|
|
102
|
+
events: deviceEvents
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
registerCommand(deactivateDeviceCommand);
|
|
107
|
+
//# sourceMappingURL=deactivate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/devices/commands/deactivate.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { ensureOrganizationScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { assertFound } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { UserDevice } from '../data/entities'\nimport { deactivateDeviceCommandSchema, type DeactivateDeviceCommandInput } from '../data/validators'\nimport { emitDevicesEvent } from '../events'\nimport {\n applySnapshot,\n deviceEvents,\n deviceIndexer,\n serializeDevice,\n type DeviceSnapshot,\n type DeviceUndoPayload,\n} from './shared'\n\nconst deactivateDeviceCommand: CommandHandler<DeactivateDeviceCommandInput, { id: string }> = {\n id: 'devices.user_devices.deactivate',\n async prepare(rawInput, ctx) {\n const parsed = deactivateDeviceCommandSchema.parse(rawInput)\n // Enforce tenant scope and constrain the snapshot lookup to the caller's tenant.\n ensureTenantScope(ctx, parsed.tenantId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n // No fallback decryption scope: `UserDevice` always carries its own organization_id, which\n // `decryptEntitiesWithFallbackScope` resolves first \u2014 matching loadExistingDevice in shared.ts.\n const device = await findOneWithDecryption(\n em,\n UserDevice,\n { id: parsed.id, tenantId: parsed.tenantId, deletedAt: null },\n )\n return device ? { before: serializeDevice(device) } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = deactivateDeviceCommandSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n // Scope the load to the caller's tenant (mirroring `prepare`) instead of loading by id alone \u2014 a\n // bare id lookup would let a cross-tenant id resolve a row before the post-load tenant check runs.\n const device = assertFound(\n await findOneWithDecryption(em, UserDevice, { id: parsed.id, tenantId: parsed.tenantId, deletedAt: null }),\n 'Device not found',\n )\n ensureTenantScope(ctx, device.tenantId)\n // Enforce the ORGANIZATION scope the caller handed us instead of leaving it unread. Without this the\n // org context threaded through the push \"unregister null-org\" path \u2014 and any org-scoped admin\n // dispatch \u2014 is silently ignored, so a caller scoped to org A could deactivate an org-B device in\n // the same tenant. Guard on a non-null org: tenant-scoped devices (org = null) have nothing to\n // constrain, and the system/worker path passes `selectedOrganizationId = device.organizationId`,\n // which `ensureOrganizationScope` accepts.\n if (device.organizationId) ensureOrganizationScope(ctx, device.organizationId)\n\n await withAtomicFlush(em, [() => { device.deletedAt = new Date() }], { transaction: true })\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: device,\n identifiers: {\n id: device.id,\n tenantId: device.tenantId,\n organizationId: device.organizationId ?? null,\n },\n indexer: deviceIndexer,\n events: deviceEvents,\n })\n await emitDevicesEvent(\n 'devices.user_device.deactivated',\n {\n id: device.id,\n tenantId: device.tenantId,\n organizationId: device.organizationId ?? null,\n userId: device.userId,\n deviceId: device.deviceId,\n },\n { persistent: true },\n )\n\n return { id: device.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = (snapshots.before as DeviceSnapshot | undefined) ?? null\n if (!before) return null\n return {\n actionLabel: 'Deactivate device',\n resourceKind: 'devices.user_device',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: { before } satisfies DeviceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DeviceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const device = await findOneWithDecryption(em, UserDevice, { id: before.id })\n if (!device) return\n\n await withAtomicFlush(em, [() => applySnapshot(device, before)], { transaction: true })\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: device,\n identifiers: {\n id: device.id,\n tenantId: device.tenantId,\n organizationId: device.organizationId ?? null,\n },\n indexer: deviceIndexer,\n events: deviceEvents,\n })\n },\n}\n\nregisterCommand(deactivateDeviceCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
2
|
+
import { ensureTenantScope } from "@open-mercato/shared/lib/commands/scope";
|
|
3
|
+
import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
|
|
4
|
+
import { emitCrudSideEffects, emitCrudUndoSideEffects } from "@open-mercato/shared/lib/commands/helpers";
|
|
5
|
+
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
6
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
8
|
+
import { UserDevice } from "../data/entities.js";
|
|
9
|
+
import { registerDeviceCommandSchema } from "../data/validators.js";
|
|
10
|
+
import { emitDevicesEvent } from "../events.js";
|
|
11
|
+
import {
|
|
12
|
+
applySnapshot,
|
|
13
|
+
deviceEvents,
|
|
14
|
+
deviceIndexer,
|
|
15
|
+
isDeviceUniqueViolation,
|
|
16
|
+
loadExistingDevice,
|
|
17
|
+
serializeDevice
|
|
18
|
+
} from "./shared.js";
|
|
19
|
+
const registerDeviceCommand = {
|
|
20
|
+
id: "devices.user_devices.register",
|
|
21
|
+
async prepare(rawInput, ctx) {
|
|
22
|
+
const parsed = registerDeviceCommandSchema.parse(rawInput);
|
|
23
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
24
|
+
const em = ctx.container.resolve("em").fork();
|
|
25
|
+
const existing = await loadExistingDevice(em, {
|
|
26
|
+
tenantId: parsed.tenantId,
|
|
27
|
+
organizationId: parsed.organizationId ?? null,
|
|
28
|
+
userId: parsed.userId,
|
|
29
|
+
deviceId: parsed.deviceId
|
|
30
|
+
});
|
|
31
|
+
return existing ? { before: serializeDevice(existing) } : {};
|
|
32
|
+
},
|
|
33
|
+
async execute(rawInput, ctx) {
|
|
34
|
+
const parsed = registerDeviceCommandSchema.parse(rawInput);
|
|
35
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
36
|
+
const em = ctx.container.resolve("em").fork();
|
|
37
|
+
const existing = await loadExistingDevice(em, {
|
|
38
|
+
tenantId: parsed.tenantId,
|
|
39
|
+
organizationId: parsed.organizationId ?? null,
|
|
40
|
+
userId: parsed.userId,
|
|
41
|
+
deviceId: parsed.deviceId
|
|
42
|
+
});
|
|
43
|
+
const now = /* @__PURE__ */ new Date();
|
|
44
|
+
const hasPushToken = parsed.pushToken !== void 0 && parsed.pushToken !== null;
|
|
45
|
+
const wasActive = existing ? existing.deletedAt == null : false;
|
|
46
|
+
let device;
|
|
47
|
+
try {
|
|
48
|
+
await withAtomicFlush(
|
|
49
|
+
em,
|
|
50
|
+
[
|
|
51
|
+
() => {
|
|
52
|
+
if (existing) {
|
|
53
|
+
device = existing;
|
|
54
|
+
device.platform = parsed.platform;
|
|
55
|
+
device.organizationId = parsed.organizationId ?? device.organizationId ?? null;
|
|
56
|
+
if (parsed.clientAppVersion !== void 0) device.clientAppVersion = parsed.clientAppVersion ?? null;
|
|
57
|
+
if (parsed.osVersion !== void 0) device.osVersion = parsed.osVersion ?? null;
|
|
58
|
+
if (parsed.locale !== void 0) device.locale = parsed.locale ?? null;
|
|
59
|
+
if (hasPushToken) {
|
|
60
|
+
device.pushToken = parsed.pushToken ?? null;
|
|
61
|
+
device.pushProvider = parsed.pushProvider ?? device.pushProvider ?? null;
|
|
62
|
+
device.pushTokenUpdatedAt = now;
|
|
63
|
+
}
|
|
64
|
+
device.lastSeenAt = now;
|
|
65
|
+
device.deletedAt = null;
|
|
66
|
+
} else {
|
|
67
|
+
device = em.create(UserDevice, {
|
|
68
|
+
tenantId: parsed.tenantId,
|
|
69
|
+
organizationId: parsed.organizationId ?? null,
|
|
70
|
+
userId: parsed.userId,
|
|
71
|
+
deviceId: parsed.deviceId,
|
|
72
|
+
platform: parsed.platform,
|
|
73
|
+
clientAppVersion: parsed.clientAppVersion ?? null,
|
|
74
|
+
osVersion: parsed.osVersion ?? null,
|
|
75
|
+
locale: parsed.locale ?? null,
|
|
76
|
+
pushToken: hasPushToken ? parsed.pushToken ?? null : null,
|
|
77
|
+
pushProvider: hasPushToken ? parsed.pushProvider ?? null : null,
|
|
78
|
+
pushTokenUpdatedAt: hasPushToken ? now : null,
|
|
79
|
+
lastSeenAt: now
|
|
80
|
+
});
|
|
81
|
+
em.persist(device);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
{ transaction: true }
|
|
86
|
+
);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (isDeviceUniqueViolation(err)) {
|
|
89
|
+
throw new CrudHttpError(409, {
|
|
90
|
+
code: "device_already_registered",
|
|
91
|
+
error: "This device is already registered"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
const revived = Boolean(existing) && !wasActive;
|
|
97
|
+
const de = ctx.container.resolve("dataEngine");
|
|
98
|
+
await emitCrudSideEffects({
|
|
99
|
+
dataEngine: de,
|
|
100
|
+
action: existing ? "updated" : "created",
|
|
101
|
+
entity: device,
|
|
102
|
+
identifiers: {
|
|
103
|
+
id: device.id,
|
|
104
|
+
tenantId: device.tenantId,
|
|
105
|
+
organizationId: device.organizationId ?? null
|
|
106
|
+
},
|
|
107
|
+
indexer: deviceIndexer,
|
|
108
|
+
events: deviceEvents
|
|
109
|
+
});
|
|
110
|
+
await emitDevicesEvent(
|
|
111
|
+
"devices.user_device.registered",
|
|
112
|
+
{
|
|
113
|
+
id: device.id,
|
|
114
|
+
tenantId: device.tenantId,
|
|
115
|
+
organizationId: device.organizationId ?? null,
|
|
116
|
+
userId: device.userId,
|
|
117
|
+
deviceId: device.deviceId,
|
|
118
|
+
platform: device.platform,
|
|
119
|
+
revived
|
|
120
|
+
},
|
|
121
|
+
{ persistent: true }
|
|
122
|
+
);
|
|
123
|
+
return { id: device.id, deviceId: device.deviceId, revived };
|
|
124
|
+
},
|
|
125
|
+
captureAfter: async (_input, result, ctx) => {
|
|
126
|
+
const em = ctx.container.resolve("em").fork();
|
|
127
|
+
const device = await findOneWithDecryption(em, UserDevice, { id: result.id });
|
|
128
|
+
return device ? serializeDevice(device) : null;
|
|
129
|
+
},
|
|
130
|
+
buildLog: async ({ result, snapshots }) => {
|
|
131
|
+
const before = snapshots.before ?? null;
|
|
132
|
+
const after = snapshots.after ?? null;
|
|
133
|
+
return {
|
|
134
|
+
actionLabel: before ? "Update device" : "Register device",
|
|
135
|
+
resourceKind: "devices.user_device",
|
|
136
|
+
resourceId: result.id,
|
|
137
|
+
tenantId: after?.tenantId ?? before?.tenantId ?? null,
|
|
138
|
+
organizationId: after?.organizationId ?? before?.organizationId ?? null,
|
|
139
|
+
snapshotBefore: before,
|
|
140
|
+
snapshotAfter: after,
|
|
141
|
+
payload: {
|
|
142
|
+
undo: { before, after }
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
undo: async ({ logEntry, ctx }) => {
|
|
147
|
+
const payload = extractUndoPayload(logEntry);
|
|
148
|
+
const after = payload?.after;
|
|
149
|
+
if (!after) return;
|
|
150
|
+
const before = payload?.before ?? null;
|
|
151
|
+
const em = ctx.container.resolve("em").fork();
|
|
152
|
+
const device = await findOneWithDecryption(em, UserDevice, { id: after.id });
|
|
153
|
+
if (!device) return;
|
|
154
|
+
await withAtomicFlush(
|
|
155
|
+
em,
|
|
156
|
+
[
|
|
157
|
+
() => {
|
|
158
|
+
if (before) {
|
|
159
|
+
applySnapshot(device, before);
|
|
160
|
+
} else {
|
|
161
|
+
device.deletedAt = /* @__PURE__ */ new Date();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
{ transaction: true }
|
|
166
|
+
);
|
|
167
|
+
const de = ctx.container.resolve("dataEngine");
|
|
168
|
+
await emitCrudUndoSideEffects({
|
|
169
|
+
dataEngine: de,
|
|
170
|
+
action: before ? "updated" : "deleted",
|
|
171
|
+
entity: device,
|
|
172
|
+
identifiers: {
|
|
173
|
+
id: device.id,
|
|
174
|
+
tenantId: device.tenantId,
|
|
175
|
+
organizationId: device.organizationId ?? null
|
|
176
|
+
},
|
|
177
|
+
indexer: deviceIndexer,
|
|
178
|
+
events: deviceEvents
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
registerCommand(registerDeviceCommand);
|
|
183
|
+
//# sourceMappingURL=register.js.map
|