@open-mercato/core 0.6.8-develop.6984.1.f0cf23f0f6 → 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,219 @@
|
|
|
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 { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
|
+
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
8
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
9
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
10
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
11
|
+
import { UserDevice } from "../../../../data/entities.js";
|
|
12
|
+
import { updateDeviceSchema } from "../../../../data/validators.js";
|
|
13
|
+
import { isOrganizationReadAccessAllowed } from "@open-mercato/core/modules/directory/utils/organizationScopeGuard";
|
|
14
|
+
import { resolveDeviceActorUserId } from "../../../auth.js";
|
|
15
|
+
import { executeUpdate, executeDeactivate } from "../../../deviceOps.js";
|
|
16
|
+
const logger = createLogger("devices");
|
|
17
|
+
const metadata = {
|
|
18
|
+
GET: { requireAuth: true, requireFeatures: ["devices.admin"] },
|
|
19
|
+
PUT: { requireAuth: true, requireFeatures: ["devices.admin"] },
|
|
20
|
+
DELETE: { requireAuth: true, requireFeatures: ["devices.admin"] }
|
|
21
|
+
};
|
|
22
|
+
const paramsSchema = z.object({ id: z.string().uuid() });
|
|
23
|
+
async function loadDevice(container, id, tenantId) {
|
|
24
|
+
const em = container.resolve("em");
|
|
25
|
+
return findOneWithDecryption(em, UserDevice, { id, tenantId, deletedAt: null }, void 0, { tenantId });
|
|
26
|
+
}
|
|
27
|
+
function serializeDevice(device) {
|
|
28
|
+
return {
|
|
29
|
+
id: device.id,
|
|
30
|
+
user_id: device.userId,
|
|
31
|
+
device_id: device.deviceId,
|
|
32
|
+
platform: device.platform,
|
|
33
|
+
client_app_version: device.clientAppVersion ?? null,
|
|
34
|
+
os_version: device.osVersion ?? null,
|
|
35
|
+
push_provider: device.pushProvider ?? null,
|
|
36
|
+
push_token_updated_at: device.pushTokenUpdatedAt ? device.pushTokenUpdatedAt.toISOString() : null,
|
|
37
|
+
last_seen_at: device.lastSeenAt ? device.lastSeenAt.toISOString() : null,
|
|
38
|
+
created_at: device.createdAt ? device.createdAt.toISOString() : null,
|
|
39
|
+
updated_at: device.updatedAt ? device.updatedAt.toISOString() : null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function GET(req, { params }) {
|
|
43
|
+
const { translate } = await resolveTranslations();
|
|
44
|
+
try {
|
|
45
|
+
const container = await createRequestContainer();
|
|
46
|
+
const auth = await getAuthFromRequest(req);
|
|
47
|
+
const actorUserId = resolveDeviceActorUserId(auth);
|
|
48
|
+
if (!auth || !auth.tenantId || !actorUserId) {
|
|
49
|
+
return NextResponse.json({ error: translate("devices.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
50
|
+
}
|
|
51
|
+
const parsedParams = paramsSchema.safeParse({ id: params.id });
|
|
52
|
+
if (!parsedParams.success) {
|
|
53
|
+
return NextResponse.json({ error: translate("devices.errors.invalid_id", "Invalid device id") }, { status: 400 });
|
|
54
|
+
}
|
|
55
|
+
const device = await loadDevice(container, parsedParams.data.id, auth.tenantId);
|
|
56
|
+
if (!device) {
|
|
57
|
+
return NextResponse.json({ error: translate("devices.errors.not_found", "Device not found") }, { status: 404 });
|
|
58
|
+
}
|
|
59
|
+
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
60
|
+
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: device.organizationId ?? null })) {
|
|
61
|
+
return NextResponse.json({ error: translate("devices.errors.forbidden", "Access denied") }, { status: 403 });
|
|
62
|
+
}
|
|
63
|
+
return NextResponse.json({ item: serializeDevice(device) });
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (isCrudHttpError(err)) {
|
|
66
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
67
|
+
}
|
|
68
|
+
logger.error("devices.admin.GET failed", { err });
|
|
69
|
+
return NextResponse.json({ error: translate("devices.errors.load_failed", "Failed to load device") }, { status: 500 });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function PUT(req, { params }) {
|
|
73
|
+
const { translate } = await resolveTranslations();
|
|
74
|
+
try {
|
|
75
|
+
const container = await createRequestContainer();
|
|
76
|
+
const auth = await getAuthFromRequest(req);
|
|
77
|
+
const actorUserId = resolveDeviceActorUserId(auth);
|
|
78
|
+
if (!auth || !auth.tenantId || !actorUserId) {
|
|
79
|
+
return NextResponse.json({ error: translate("devices.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
80
|
+
}
|
|
81
|
+
const parsedParams = paramsSchema.safeParse({ id: params.id });
|
|
82
|
+
if (!parsedParams.success) {
|
|
83
|
+
return NextResponse.json({ error: translate("devices.errors.invalid_id", "Invalid device id") }, { status: 400 });
|
|
84
|
+
}
|
|
85
|
+
const body = updateDeviceSchema.parse(await readJsonSafe(req, {}));
|
|
86
|
+
const device = await loadDevice(container, parsedParams.data.id, auth.tenantId);
|
|
87
|
+
if (!device) {
|
|
88
|
+
return NextResponse.json({ error: translate("devices.errors.not_found", "Device not found") }, { status: 404 });
|
|
89
|
+
}
|
|
90
|
+
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
91
|
+
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: device.organizationId ?? null })) {
|
|
92
|
+
return NextResponse.json({ error: translate("devices.errors.forbidden", "Access denied") }, { status: 403 });
|
|
93
|
+
}
|
|
94
|
+
const mctx = {
|
|
95
|
+
container,
|
|
96
|
+
auth,
|
|
97
|
+
scope,
|
|
98
|
+
organizationId: device.organizationId ?? null,
|
|
99
|
+
actorUserId,
|
|
100
|
+
request: req
|
|
101
|
+
};
|
|
102
|
+
return await executeUpdate(mctx, device, body);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof z.ZodError) {
|
|
105
|
+
return NextResponse.json(
|
|
106
|
+
{ error: translate("devices.errors.invalid_payload", "Invalid device payload"), details: err.flatten() },
|
|
107
|
+
{ status: 400 }
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
if (isCrudHttpError(err)) {
|
|
111
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
112
|
+
}
|
|
113
|
+
logger.error("devices.admin.PUT failed", { err });
|
|
114
|
+
return NextResponse.json({ error: translate("devices.errors.update_failed", "Failed to update device") }, { status: 500 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function DELETE(req, { params }) {
|
|
118
|
+
const { translate } = await resolveTranslations();
|
|
119
|
+
try {
|
|
120
|
+
const container = await createRequestContainer();
|
|
121
|
+
const auth = await getAuthFromRequest(req);
|
|
122
|
+
const actorUserId = resolveDeviceActorUserId(auth);
|
|
123
|
+
if (!auth || !auth.tenantId || !actorUserId) {
|
|
124
|
+
return NextResponse.json({ error: translate("devices.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
125
|
+
}
|
|
126
|
+
const parsedParams = paramsSchema.safeParse({ id: params.id });
|
|
127
|
+
if (!parsedParams.success) {
|
|
128
|
+
return NextResponse.json({ error: translate("devices.errors.invalid_id", "Invalid device id") }, { status: 400 });
|
|
129
|
+
}
|
|
130
|
+
const device = await loadDevice(container, parsedParams.data.id, auth.tenantId);
|
|
131
|
+
if (!device) {
|
|
132
|
+
return NextResponse.json({ error: translate("devices.errors.not_found", "Device not found") }, { status: 404 });
|
|
133
|
+
}
|
|
134
|
+
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
135
|
+
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: device.organizationId ?? null })) {
|
|
136
|
+
return NextResponse.json({ error: translate("devices.errors.forbidden", "Access denied") }, { status: 403 });
|
|
137
|
+
}
|
|
138
|
+
const mctx = {
|
|
139
|
+
container,
|
|
140
|
+
auth,
|
|
141
|
+
scope,
|
|
142
|
+
organizationId: device.organizationId ?? null,
|
|
143
|
+
actorUserId,
|
|
144
|
+
request: req
|
|
145
|
+
};
|
|
146
|
+
return await executeDeactivate(mctx, device);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (isCrudHttpError(err)) {
|
|
149
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
150
|
+
}
|
|
151
|
+
logger.error("devices.admin.DELETE failed", { err });
|
|
152
|
+
return NextResponse.json({ error: translate("devices.errors.delete_failed", "Failed to delete device") }, { status: 500 });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const okResponseSchema = z.object({ ok: z.literal(true), id: z.string().uuid().optional() });
|
|
156
|
+
const errorResponseSchema = z.object({ error: z.string() });
|
|
157
|
+
const detailResponseSchema = z.object({
|
|
158
|
+
item: z.object({
|
|
159
|
+
id: z.string().uuid(),
|
|
160
|
+
user_id: z.string().uuid(),
|
|
161
|
+
device_id: z.string(),
|
|
162
|
+
platform: z.enum(["ios", "android", "web"]),
|
|
163
|
+
client_app_version: z.string().nullable(),
|
|
164
|
+
os_version: z.string().nullable(),
|
|
165
|
+
push_provider: z.string().nullable(),
|
|
166
|
+
push_token_updated_at: z.string().nullable(),
|
|
167
|
+
last_seen_at: z.string().nullable(),
|
|
168
|
+
created_at: z.string().nullable(),
|
|
169
|
+
updated_at: z.string().nullable()
|
|
170
|
+
})
|
|
171
|
+
});
|
|
172
|
+
const openApi = {
|
|
173
|
+
tag: "Devices (admin)",
|
|
174
|
+
summary: "Admin: read, update or deactivate any device in the tenant",
|
|
175
|
+
methods: {
|
|
176
|
+
GET: {
|
|
177
|
+
summary: "Get any device",
|
|
178
|
+
description: "Admin: fetch a single device by id (push_token is never returned).",
|
|
179
|
+
responses: [{ status: 200, description: "Device", schema: detailResponseSchema }],
|
|
180
|
+
errors: [
|
|
181
|
+
{ status: 400, description: "Invalid id", schema: errorResponseSchema },
|
|
182
|
+
{ status: 401, description: "Unauthorized", schema: errorResponseSchema },
|
|
183
|
+
{ status: 403, description: "Missing devices.admin", schema: errorResponseSchema },
|
|
184
|
+
{ status: 404, description: "Device not found", schema: errorResponseSchema }
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
PUT: {
|
|
188
|
+
summary: "Update any device",
|
|
189
|
+
description: "Admin: update last-seen, app/OS metadata, and push token for any device. Set pushToken to null to clear it.",
|
|
190
|
+
requestBody: { schema: updateDeviceSchema },
|
|
191
|
+
responses: [{ status: 200, description: "Device updated", schema: okResponseSchema }],
|
|
192
|
+
errors: [
|
|
193
|
+
{ status: 400, description: "Invalid payload or id", schema: errorResponseSchema },
|
|
194
|
+
{ status: 401, description: "Unauthorized", schema: errorResponseSchema },
|
|
195
|
+
{ status: 403, description: "Missing devices.admin", schema: errorResponseSchema },
|
|
196
|
+
{ status: 404, description: "Device not found", schema: errorResponseSchema }
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
DELETE: {
|
|
200
|
+
summary: "Deactivate any device",
|
|
201
|
+
description: "Admin: soft-delete any device in the tenant.",
|
|
202
|
+
responses: [{ status: 200, description: "Device deactivated", schema: okResponseSchema }],
|
|
203
|
+
errors: [
|
|
204
|
+
{ status: 400, description: "Invalid id", schema: errorResponseSchema },
|
|
205
|
+
{ status: 401, description: "Unauthorized", schema: errorResponseSchema },
|
|
206
|
+
{ status: 403, description: "Missing devices.admin", schema: errorResponseSchema },
|
|
207
|
+
{ status: 404, description: "Device not found", schema: errorResponseSchema }
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
export {
|
|
213
|
+
DELETE,
|
|
214
|
+
GET,
|
|
215
|
+
PUT,
|
|
216
|
+
metadata,
|
|
217
|
+
openApi
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/devices/api/admin/devices/%5Bid%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 { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { UserDevice } from '../../../../data/entities'\nimport { updateDeviceSchema } from '../../../../data/validators'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { resolveDeviceActorUserId } from '../../../auth'\nimport { executeUpdate, executeDeactivate, type DeviceMutationContext } from '../../../deviceOps'\n\nconst logger = createLogger('devices')\n\n// Admin: read/update/deactivate ANY device in the tenant. Gated by devices.admin (no owner check).\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['devices.admin'] },\n PUT: { requireAuth: true, requireFeatures: ['devices.admin'] },\n DELETE: { requireAuth: true, requireFeatures: ['devices.admin'] },\n}\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nasync function loadDevice(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n id: string,\n tenantId: string,\n): Promise<UserDevice | null> {\n const em = container.resolve('em') as EntityManager\n // push_token is encrypted at rest; the org is not known until the row loads, so pass tenant only \u2014\n // the helper prefers each record's own tenant/org and treats this scope as a fallback.\n return findOneWithDecryption(em, UserDevice, { id, tenantId, deletedAt: null }, undefined, { tenantId })\n}\n\n// push_token is a secret and is never returned.\nfunction serializeDevice(device: UserDevice) {\n return {\n id: device.id,\n user_id: device.userId,\n device_id: device.deviceId,\n platform: device.platform,\n client_app_version: device.clientAppVersion ?? null,\n os_version: device.osVersion ?? null,\n push_provider: device.pushProvider ?? null,\n push_token_updated_at: device.pushTokenUpdatedAt ? device.pushTokenUpdatedAt.toISOString() : null,\n last_seen_at: device.lastSeenAt ? device.lastSeenAt.toISOString() : null,\n created_at: device.createdAt ? device.createdAt.toISOString() : null,\n updated_at: device.updatedAt ? device.updatedAt.toISOString() : null,\n }\n}\n\nexport async function GET(req: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveDeviceActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('devices.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n const parsedParams = paramsSchema.safeParse({ id: params.id })\n if (!parsedParams.success) {\n return NextResponse.json({ error: translate('devices.errors.invalid_id', 'Invalid device id') }, { status: 400 })\n }\n const device = await loadDevice(container, parsedParams.data.id, auth.tenantId)\n if (!device) {\n return NextResponse.json({ error: translate('devices.errors.not_found', 'Device not found') }, { status: 404 })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: device.organizationId ?? null })) {\n return NextResponse.json({ error: translate('devices.errors.forbidden', 'Access denied') }, { status: 403 })\n }\n return NextResponse.json({ item: serializeDevice(device) })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('devices.admin.GET failed', { err })\n return NextResponse.json({ error: translate('devices.errors.load_failed', 'Failed to load device') }, { status: 500 })\n }\n}\n\nexport async function PUT(req: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveDeviceActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('devices.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n const parsedParams = paramsSchema.safeParse({ id: params.id })\n if (!parsedParams.success) {\n return NextResponse.json({ error: translate('devices.errors.invalid_id', 'Invalid device id') }, { status: 400 })\n }\n\n const body = updateDeviceSchema.parse(await readJsonSafe(req, {}))\n const device = await loadDevice(container, parsedParams.data.id, auth.tenantId)\n if (!device) {\n return NextResponse.json({ error: translate('devices.errors.not_found', 'Device not found') }, { status: 404 })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: device.organizationId ?? null })) {\n return NextResponse.json({ error: translate('devices.errors.forbidden', 'Access denied') }, { status: 403 })\n }\n const mctx: DeviceMutationContext = {\n container,\n auth,\n scope,\n organizationId: device.organizationId ?? null,\n actorUserId,\n request: req,\n }\n return await executeUpdate(mctx, device, body)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('devices.errors.invalid_payload', 'Invalid device payload'), details: err.flatten() },\n { status: 400 },\n )\n }\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('devices.admin.PUT failed', { err })\n return NextResponse.json({ error: translate('devices.errors.update_failed', 'Failed to update device') }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveDeviceActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('devices.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n const parsedParams = paramsSchema.safeParse({ id: params.id })\n if (!parsedParams.success) {\n return NextResponse.json({ error: translate('devices.errors.invalid_id', 'Invalid device id') }, { status: 400 })\n }\n\n const device = await loadDevice(container, parsedParams.data.id, auth.tenantId)\n if (!device) {\n return NextResponse.json({ error: translate('devices.errors.not_found', 'Device not found') }, { status: 404 })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: device.organizationId ?? null })) {\n return NextResponse.json({ error: translate('devices.errors.forbidden', 'Access denied') }, { status: 403 })\n }\n const mctx: DeviceMutationContext = {\n container,\n auth,\n scope,\n organizationId: device.organizationId ?? null,\n actorUserId,\n request: req,\n }\n return await executeDeactivate(mctx, device)\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('devices.admin.DELETE failed', { err })\n return NextResponse.json({ error: translate('devices.errors.delete_failed', 'Failed to delete device') }, { status: 500 })\n }\n}\n\nconst okResponseSchema = z.object({ ok: z.literal(true), id: z.string().uuid().optional() })\nconst errorResponseSchema = z.object({ error: z.string() })\nconst detailResponseSchema = z.object({\n item: z.object({\n id: z.string().uuid(),\n user_id: z.string().uuid(),\n device_id: z.string(),\n platform: z.enum(['ios', 'android', 'web']),\n client_app_version: z.string().nullable(),\n os_version: z.string().nullable(),\n push_provider: z.string().nullable(),\n push_token_updated_at: z.string().nullable(),\n last_seen_at: z.string().nullable(),\n created_at: z.string().nullable(),\n updated_at: z.string().nullable(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Devices (admin)',\n summary: 'Admin: read, update or deactivate any device in the tenant',\n methods: {\n GET: {\n summary: 'Get any device',\n description: 'Admin: fetch a single device by id (push_token is never returned).',\n responses: [{ status: 200, description: 'Device', schema: detailResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid id', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Missing devices.admin', schema: errorResponseSchema },\n { status: 404, description: 'Device not found', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update any device',\n description: 'Admin: update last-seen, app/OS metadata, and push token for any device. Set pushToken to null to clear it.',\n requestBody: { schema: updateDeviceSchema },\n responses: [{ status: 200, description: 'Device updated', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid payload or id', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Missing devices.admin', schema: errorResponseSchema },\n { status: 404, description: 'Device not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Deactivate any device',\n description: 'Admin: soft-delete any device in the tenant.',\n responses: [{ status: 200, description: 'Device deactivated', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid id', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Missing devices.admin', schema: errorResponseSchema },\n { status: 404, description: 'Device not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
6
|
+
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
7
|
+
import { isOrganizationReadAccessAllowed } from "@open-mercato/core/modules/directory/utils/organizationScopeGuard";
|
|
8
|
+
import { User } from "@open-mercato/core/modules/auth/data/entities";
|
|
9
|
+
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
10
|
+
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
11
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
12
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
13
|
+
import { E } from "../../../../../generated/entities.ids.generated.js";
|
|
14
|
+
import { UserDevice } from "../../../data/entities.js";
|
|
15
|
+
import {
|
|
16
|
+
registerDeviceAdminSchema,
|
|
17
|
+
registerDeviceCommandSchema
|
|
18
|
+
} from "../../../data/validators.js";
|
|
19
|
+
import { resolveDeviceActorUserId } from "../../auth.js";
|
|
20
|
+
import { createDevicesCrudOpenApi, createPagedListResponseSchema } from "../../openapi.js";
|
|
21
|
+
import { deviceListSchema, deviceListFields, deviceListSortFieldMap, deviceListItemSchema } from "../../deviceList.js";
|
|
22
|
+
import { executeRegister } from "../../deviceOps.js";
|
|
23
|
+
const logger = createLogger("devices");
|
|
24
|
+
const routeMetadata = {
|
|
25
|
+
GET: { requireAuth: true, requireFeatures: ["devices.admin"] },
|
|
26
|
+
POST: { requireAuth: true, requireFeatures: ["devices.admin"] }
|
|
27
|
+
};
|
|
28
|
+
const metadata = routeMetadata;
|
|
29
|
+
const crud = makeCrudRoute({
|
|
30
|
+
metadata: routeMetadata,
|
|
31
|
+
orm: {
|
|
32
|
+
entity: UserDevice,
|
|
33
|
+
idField: "id",
|
|
34
|
+
// organization_id stays nullable, but scoping follows the standard org-scoped pattern: the factory
|
|
35
|
+
// auto-filters by the caller's organization scope (null-aware in the query engine). Unrestricted
|
|
36
|
+
// admins see the whole tenant including null-org rows; org-restricted admins see only their orgs.
|
|
37
|
+
orgField: "organizationId",
|
|
38
|
+
tenantField: "tenantId",
|
|
39
|
+
softDeleteField: "deletedAt"
|
|
40
|
+
},
|
|
41
|
+
indexer: { entityType: E.devices.user_device },
|
|
42
|
+
// Same cache resource tag as the self route + commands, so any device write busts both list caches.
|
|
43
|
+
events: { module: "devices", entity: "user_device" },
|
|
44
|
+
list: {
|
|
45
|
+
schema: deviceListSchema,
|
|
46
|
+
entityId: E.devices.user_device,
|
|
47
|
+
fields: deviceListFields,
|
|
48
|
+
sortFieldMap: deviceListSortFieldMap,
|
|
49
|
+
// Tenant + org scope is enforced by the factory (orm.tenantField + orm.orgField); only the optional
|
|
50
|
+
// userId/platform narrowing is left to do here.
|
|
51
|
+
buildFilters: async (query) => {
|
|
52
|
+
const filters = {};
|
|
53
|
+
if (query.userId) filters.user_id = { $eq: query.userId };
|
|
54
|
+
if (query.platform) filters.platform = { $eq: query.platform };
|
|
55
|
+
return filters;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const GET = crud.GET;
|
|
60
|
+
async function POST(req) {
|
|
61
|
+
const { translate } = await resolveTranslations();
|
|
62
|
+
try {
|
|
63
|
+
const container = await createRequestContainer();
|
|
64
|
+
const auth = await getAuthFromRequest(req);
|
|
65
|
+
const actorUserId = resolveDeviceActorUserId(auth);
|
|
66
|
+
if (!auth || !auth.tenantId || !actorUserId) {
|
|
67
|
+
return NextResponse.json({ error: translate("devices.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
68
|
+
}
|
|
69
|
+
const body = registerDeviceAdminSchema.parse(await readJsonSafe(req, {}));
|
|
70
|
+
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
71
|
+
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
72
|
+
const em = container.resolve("em");
|
|
73
|
+
const targetUser = await em.findOne(
|
|
74
|
+
User,
|
|
75
|
+
{ id: body.userId, tenantId: auth.tenantId, deletedAt: null },
|
|
76
|
+
{ fields: ["id", "organizationId"] }
|
|
77
|
+
);
|
|
78
|
+
if (!targetUser) {
|
|
79
|
+
return NextResponse.json(
|
|
80
|
+
{ error: translate("devices.errors.user_not_found", "Target user not found in this tenant") },
|
|
81
|
+
{ status: 400 }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: targetUser.organizationId ?? null })) {
|
|
85
|
+
return NextResponse.json({ error: translate("devices.errors.forbidden", "Access denied") }, { status: 403 });
|
|
86
|
+
}
|
|
87
|
+
const commandInput = registerDeviceCommandSchema.parse({
|
|
88
|
+
...body,
|
|
89
|
+
tenantId: auth.tenantId,
|
|
90
|
+
organizationId,
|
|
91
|
+
userId: body.userId
|
|
92
|
+
});
|
|
93
|
+
const mctx = { container, auth, scope, organizationId, actorUserId, request: req };
|
|
94
|
+
return await executeRegister(mctx, commandInput);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err instanceof z.ZodError) {
|
|
97
|
+
return NextResponse.json(
|
|
98
|
+
{ error: translate("devices.errors.invalid_payload", "Invalid device payload"), details: err.flatten() },
|
|
99
|
+
{ status: 400 }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (isCrudHttpError(err) && err.body?.code === "device_already_registered") {
|
|
103
|
+
return NextResponse.json(
|
|
104
|
+
{ error: translate("devices.errors.already_registered", "This device is already registered") },
|
|
105
|
+
{ status: 409 }
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (isCrudHttpError(err)) {
|
|
109
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
110
|
+
}
|
|
111
|
+
logger.error("devices.admin.POST failed", { err });
|
|
112
|
+
return NextResponse.json(
|
|
113
|
+
{ error: translate("devices.errors.register_failed", "Failed to register device") },
|
|
114
|
+
{ status: 500 }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const registerResponseSchema = z.object({
|
|
119
|
+
id: z.string().uuid(),
|
|
120
|
+
deviceId: z.string(),
|
|
121
|
+
revived: z.boolean()
|
|
122
|
+
});
|
|
123
|
+
const openApi = createDevicesCrudOpenApi({
|
|
124
|
+
resourceName: "Device (admin)",
|
|
125
|
+
pluralName: "Devices (admin)",
|
|
126
|
+
querySchema: deviceListSchema,
|
|
127
|
+
listResponseSchema: createPagedListResponseSchema(deviceListItemSchema),
|
|
128
|
+
create: {
|
|
129
|
+
schema: registerDeviceAdminSchema,
|
|
130
|
+
responseSchema: registerResponseSchema,
|
|
131
|
+
description: "Admin: register (idempotently upsert) a device on behalf of any user in the tenant."
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
export {
|
|
135
|
+
GET,
|
|
136
|
+
POST,
|
|
137
|
+
metadata,
|
|
138
|
+
openApi
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/devices/api/admin/devices/route.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { E } from '#generated/entities.ids.generated'\nimport { UserDevice } from '../../../data/entities'\nimport {\n registerDeviceAdminSchema,\n registerDeviceCommandSchema,\n type RegisterDeviceCommandInput,\n} from '../../../data/validators'\nimport { resolveDeviceActorUserId } from '../../auth'\nimport { createDevicesCrudOpenApi, createPagedListResponseSchema } from '../../openapi'\nimport { deviceListSchema, deviceListFields, deviceListSortFieldMap, deviceListItemSchema } from '../../deviceList'\nimport { executeRegister, type DeviceMutationContext } from '../../deviceOps'\n\nconst logger = createLogger('devices')\n\n// Admin device administration: cross-user listing + register-on-behalf-of-user. Gated by devices.admin.\n// Self-serve registration/listing stays on /api/devices (scoped to the acting user).\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['devices.admin'] },\n POST: { requireAuth: true, requireFeatures: ['devices.admin'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: UserDevice,\n idField: 'id',\n // organization_id stays nullable, but scoping follows the standard org-scoped pattern: the factory\n // auto-filters by the caller's organization scope (null-aware in the query engine). Unrestricted\n // admins see the whole tenant including null-org rows; org-restricted admins see only their orgs.\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.devices.user_device },\n // Same cache resource tag as the self route + commands, so any device write busts both list caches.\n events: { module: 'devices', entity: 'user_device' },\n list: {\n schema: deviceListSchema,\n entityId: E.devices.user_device,\n fields: deviceListFields,\n sortFieldMap: deviceListSortFieldMap,\n // Tenant + org scope is enforced by the factory (orm.tenantField + orm.orgField); only the optional\n // userId/platform narrowing is left to do here.\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n if (query.userId) filters.user_id = { $eq: query.userId }\n if (query.platform) filters.platform = { $eq: query.platform }\n return filters\n },\n },\n})\n\nexport const GET = crud.GET\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveDeviceActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('devices.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const body = registerDeviceAdminSchema.parse(await readJsonSafe(req, {}))\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n\n // Validate the on-behalf-of target before registering. `body.userId` is caller-supplied, and the\n // fan-out delivers by `(tenant, org, user)` \u2014 so without this an admin could register their OWN push\n // token under an arbitrary `userId` (even a non-existent or cross-tenant one) and start receiving that\n // user's notifications. Require a real, active user in the admin's tenant who is also within the\n // admin's organization scope (org-restricted admins cannot reach users in orgs they can't access).\n // Only non-encrypted columns are selected, so no decryption is needed.\n const em = container.resolve('em') as EntityManager\n const targetUser = await em.findOne(\n User,\n { id: body.userId, tenantId: auth.tenantId, deletedAt: null },\n { fields: ['id', 'organizationId'] },\n )\n if (!targetUser) {\n return NextResponse.json(\n { error: translate('devices.errors.user_not_found', 'Target user not found in this tenant') },\n { status: 400 },\n )\n }\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: targetUser.organizationId ?? null })) {\n return NextResponse.json({ error: translate('devices.errors.forbidden', 'Access denied') }, { status: 403 })\n }\n\n // Register is an idempotent upsert keyed per (tenant, org, user, device). organizationId is the\n // admin's own resolved scope, so the upsert can only ever touch a row inside an org the admin can\n // access \u2014 no cross-org guard is needed here.\n const commandInput = registerDeviceCommandSchema.parse({\n ...body,\n tenantId: auth.tenantId,\n organizationId,\n userId: body.userId,\n } satisfies RegisterDeviceCommandInput)\n\n // actorUserId is the admin (for the mutation guard/audit); the device is owned by body.userId.\n const mctx: DeviceMutationContext = { container, auth, scope, organizationId, actorUserId, request: req }\n return await executeRegister(mctx, commandInput)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('devices.errors.invalid_payload', 'Invalid device payload'), details: err.flatten() },\n { status: 400 },\n )\n }\n if (isCrudHttpError(err) && (err.body as { code?: string } | undefined)?.code === 'device_already_registered') {\n return NextResponse.json(\n { error: translate('devices.errors.already_registered', 'This device is already registered') },\n { status: 409 },\n )\n }\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('devices.admin.POST failed', { err })\n return NextResponse.json(\n { error: translate('devices.errors.register_failed', 'Failed to register device') },\n { status: 500 },\n )\n }\n}\n\nconst registerResponseSchema = z.object({\n id: z.string().uuid(),\n deviceId: z.string(),\n revived: z.boolean(),\n})\n\nexport const openApi: OpenApiRouteDoc = createDevicesCrudOpenApi({\n resourceName: 'Device (admin)',\n pluralName: 'Devices (admin)',\n querySchema: deviceListSchema,\n listResponseSchema: createPagedListResponseSchema(deviceListItemSchema),\n create: {\n schema: registerDeviceAdminSchema,\n responseSchema: registerResponseSchema,\n description: 'Admin: register (idempotently upsert) a device on behalf of any user in the tenant.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function resolveDeviceActorUserId(auth) {
|
|
2
|
+
if (!auth || auth.isApiKey) return null;
|
|
3
|
+
const subjectId = typeof auth.sub === "string" ? auth.sub.trim() : "";
|
|
4
|
+
return subjectId || null;
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
resolveDeviceActorUserId
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/devices/api/auth.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AuthContext } from '@open-mercato/shared/lib/auth/server'\n\n/**\n * Resolves the acting user id for device routes. Device ownership is per-user, so API-key\n * principals (which have no human user) are not valid device actors and yield null. Mirrors the\n * `auth.isApiKey ? null : auth.sub` convention used across the codebase; the target user id is\n * validated as a UUID by the command schemas downstream.\n */\nexport function resolveDeviceActorUserId(auth: AuthContext | null): string | null {\n if (!auth || auth.isApiKey) return null\n const subjectId = typeof auth.sub === 'string' ? auth.sub.trim() : ''\n return subjectId || null\n}\n"],
|
|
5
|
+
"mappings": "AAQO,SAAS,yBAAyB,MAAyC;AAChF,MAAI,CAAC,QAAQ,KAAK,SAAU,QAAO;AACnC,QAAM,YAAY,OAAO,KAAK,QAAQ,WAAW,KAAK,IAAI,KAAK,IAAI;AACnE,SAAO,aAAa;AACtB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const deviceListSchema = z.object({
|
|
3
|
+
page: z.coerce.number().min(1).default(1),
|
|
4
|
+
pageSize: z.coerce.number().min(1).max(100).default(50),
|
|
5
|
+
platform: z.enum(["ios", "android", "web"]).optional(),
|
|
6
|
+
userId: z.string().uuid().optional(),
|
|
7
|
+
sortField: z.string().optional(),
|
|
8
|
+
sortDir: z.enum(["asc", "desc"]).optional()
|
|
9
|
+
}).passthrough();
|
|
10
|
+
const deviceListFields = [
|
|
11
|
+
"id",
|
|
12
|
+
"tenant_id",
|
|
13
|
+
"organization_id",
|
|
14
|
+
"user_id",
|
|
15
|
+
"device_id",
|
|
16
|
+
"platform",
|
|
17
|
+
"client_app_version",
|
|
18
|
+
"os_version",
|
|
19
|
+
"locale",
|
|
20
|
+
"push_provider",
|
|
21
|
+
"push_token_updated_at",
|
|
22
|
+
"last_seen_at",
|
|
23
|
+
"created_at",
|
|
24
|
+
"updated_at"
|
|
25
|
+
];
|
|
26
|
+
const deviceListSortFieldMap = {
|
|
27
|
+
lastSeenAt: "last_seen_at",
|
|
28
|
+
createdAt: "created_at",
|
|
29
|
+
updatedAt: "updated_at"
|
|
30
|
+
};
|
|
31
|
+
const deviceListItemSchema = z.object({
|
|
32
|
+
id: z.string().uuid(),
|
|
33
|
+
tenant_id: z.string().uuid(),
|
|
34
|
+
organization_id: z.string().uuid().nullable().optional(),
|
|
35
|
+
user_id: z.string().uuid(),
|
|
36
|
+
device_id: z.string(),
|
|
37
|
+
platform: z.enum(["ios", "android", "web"]),
|
|
38
|
+
client_app_version: z.string().nullable().optional(),
|
|
39
|
+
os_version: z.string().nullable().optional(),
|
|
40
|
+
locale: z.string().nullable().optional(),
|
|
41
|
+
push_provider: z.string().nullable().optional(),
|
|
42
|
+
push_token_updated_at: z.string().nullable().optional(),
|
|
43
|
+
last_seen_at: z.string().nullable().optional(),
|
|
44
|
+
created_at: z.string().nullable().optional(),
|
|
45
|
+
updated_at: z.string().nullable().optional()
|
|
46
|
+
});
|
|
47
|
+
export {
|
|
48
|
+
deviceListFields,
|
|
49
|
+
deviceListItemSchema,
|
|
50
|
+
deviceListSchema,
|
|
51
|
+
deviceListSortFieldMap
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=deviceList.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/devices/api/deviceList.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\n\n// Shared list contract for the self-serve (`/api/devices`) and admin (`/api/devices/admin/devices`)\n// list routes. push_token is a secret and is never part of the exposed field set.\nexport const deviceListSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n platform: z.enum(['ios', 'android', 'web']).optional(),\n userId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nexport const deviceListFields: string[] = [\n 'id',\n 'tenant_id',\n 'organization_id',\n 'user_id',\n 'device_id',\n 'platform',\n 'client_app_version',\n 'os_version',\n 'locale',\n 'push_provider',\n 'push_token_updated_at',\n 'last_seen_at',\n 'created_at',\n 'updated_at',\n]\n\nexport const deviceListSortFieldMap: Record<string, string> = {\n lastSeenAt: 'last_seen_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n}\n\nexport const deviceListItemSchema = z.object({\n id: z.string().uuid(),\n tenant_id: z.string().uuid(),\n organization_id: z.string().uuid().nullable().optional(),\n user_id: z.string().uuid(),\n device_id: z.string(),\n platform: z.enum(['ios', 'android', 'web']),\n client_app_version: z.string().nullable().optional(),\n os_version: z.string().nullable().optional(),\n locale: z.string().nullable().optional(),\n push_provider: z.string().nullable().optional(),\n push_token_updated_at: z.string().nullable().optional(),\n last_seen_at: z.string().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n})\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAIX,MAAM,mBAAmB,EAC7B,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,UAAU,EAAE,KAAK,CAAC,OAAO,WAAW,KAAK,CAAC,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACnC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAC5C,CAAC,EACA,YAAY;AAER,MAAM,mBAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,yBAAiD;AAAA,EAC5D,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,WAAW;AACb;AAEO,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,SAAS,EAAE,OAAO,EAAE,KAAK;AAAA,EACzB,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,KAAK,CAAC,OAAO,WAAW,KAAK,CAAC;AAAA,EAC1C,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACnD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC7C,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import {
|
|
3
|
+
runCrudMutationGuardAfterSuccess,
|
|
4
|
+
validateCrudMutationGuard
|
|
5
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
6
|
+
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
7
|
+
import { attachOperationMetadataHeader } from "../lib/operationMetadata.js";
|
|
8
|
+
const RESOURCE_KIND = "devices.user_device";
|
|
9
|
+
function redactMutationPayload(payload) {
|
|
10
|
+
if (!payload || !("pushToken" in payload)) return payload;
|
|
11
|
+
const { pushToken: _pushToken, ...rest } = payload;
|
|
12
|
+
return rest;
|
|
13
|
+
}
|
|
14
|
+
function commandCtx(mctx) {
|
|
15
|
+
return {
|
|
16
|
+
container: mctx.container,
|
|
17
|
+
auth: mctx.auth,
|
|
18
|
+
organizationScope: mctx.scope ?? null,
|
|
19
|
+
selectedOrganizationId: mctx.organizationId,
|
|
20
|
+
organizationIds: mctx.scope?.filterIds ?? (mctx.auth.orgId ? [mctx.auth.orgId] : null),
|
|
21
|
+
request: mctx.request
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function runGuards(mctx, operation, resourceId, mutationPayload) {
|
|
25
|
+
return validateCrudMutationGuard(mctx.container, {
|
|
26
|
+
tenantId: mctx.auth.tenantId,
|
|
27
|
+
organizationId: mctx.organizationId,
|
|
28
|
+
userId: mctx.actorUserId,
|
|
29
|
+
resourceKind: RESOURCE_KIND,
|
|
30
|
+
resourceId,
|
|
31
|
+
operation,
|
|
32
|
+
requestMethod: mctx.request.method,
|
|
33
|
+
requestHeaders: mctx.request.headers,
|
|
34
|
+
mutationPayload
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async function runAfter(mctx, guardResult, operation, resourceId) {
|
|
38
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
39
|
+
await runCrudMutationGuardAfterSuccess(mctx.container, {
|
|
40
|
+
tenantId: mctx.auth.tenantId,
|
|
41
|
+
organizationId: mctx.organizationId,
|
|
42
|
+
userId: mctx.actorUserId,
|
|
43
|
+
resourceKind: RESOURCE_KIND,
|
|
44
|
+
resourceId,
|
|
45
|
+
operation,
|
|
46
|
+
requestMethod: mctx.request.method,
|
|
47
|
+
requestHeaders: mctx.request.headers,
|
|
48
|
+
metadata: guardResult.metadata ?? null
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function executeRegister(mctx, commandInput) {
|
|
53
|
+
const guardResourceId = `${commandInput.userId}:${commandInput.deviceId}`;
|
|
54
|
+
const guardResult = await runGuards(mctx, "create", guardResourceId, redactMutationPayload(commandInput));
|
|
55
|
+
if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
56
|
+
const commandBus = mctx.container.resolve("commandBus");
|
|
57
|
+
const { result, logEntry } = await commandBus.execute("devices.user_devices.register", { input: commandInput, ctx: commandCtx(mctx) });
|
|
58
|
+
await runAfter(mctx, guardResult, "create", guardResourceId);
|
|
59
|
+
const response = NextResponse.json(
|
|
60
|
+
{ id: result.id, deviceId: result.deviceId, revived: result.revived },
|
|
61
|
+
{ status: 201 }
|
|
62
|
+
);
|
|
63
|
+
attachOperationMetadataHeader(response, logEntry, { resourceKind: RESOURCE_KIND, resourceId: result.id });
|
|
64
|
+
return response;
|
|
65
|
+
}
|
|
66
|
+
async function executeUpdate(mctx, device, body) {
|
|
67
|
+
enforceCommandOptimisticLock({
|
|
68
|
+
resourceKind: RESOURCE_KIND,
|
|
69
|
+
resourceId: device.id,
|
|
70
|
+
current: device.updatedAt,
|
|
71
|
+
request: mctx.request
|
|
72
|
+
});
|
|
73
|
+
const guardResult = await runGuards(mctx, "update", device.id, redactMutationPayload(body));
|
|
74
|
+
if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
75
|
+
const commandBus = mctx.container.resolve("commandBus");
|
|
76
|
+
const { logEntry } = await commandBus.execute("devices.user_devices.update", {
|
|
77
|
+
input: {
|
|
78
|
+
...body,
|
|
79
|
+
id: device.id,
|
|
80
|
+
tenantId: device.tenantId,
|
|
81
|
+
userId: device.userId,
|
|
82
|
+
organizationId: device.organizationId ?? null
|
|
83
|
+
},
|
|
84
|
+
ctx: commandCtx(mctx)
|
|
85
|
+
});
|
|
86
|
+
await runAfter(mctx, guardResult, "update", device.id);
|
|
87
|
+
const response = NextResponse.json({ ok: true, id: device.id });
|
|
88
|
+
attachOperationMetadataHeader(response, logEntry, { resourceKind: RESOURCE_KIND, resourceId: device.id });
|
|
89
|
+
return response;
|
|
90
|
+
}
|
|
91
|
+
async function executeDeactivate(mctx, device) {
|
|
92
|
+
const guardResult = await runGuards(mctx, "delete", device.id);
|
|
93
|
+
if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
94
|
+
const commandBus = mctx.container.resolve("commandBus");
|
|
95
|
+
const { logEntry } = await commandBus.execute(
|
|
96
|
+
"devices.user_devices.deactivate",
|
|
97
|
+
{
|
|
98
|
+
input: {
|
|
99
|
+
id: device.id,
|
|
100
|
+
tenantId: device.tenantId,
|
|
101
|
+
userId: device.userId,
|
|
102
|
+
organizationId: device.organizationId ?? null
|
|
103
|
+
},
|
|
104
|
+
ctx: commandCtx(mctx)
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
await runAfter(mctx, guardResult, "delete", device.id);
|
|
108
|
+
const response = NextResponse.json({ ok: true });
|
|
109
|
+
attachOperationMetadataHeader(response, logEntry, { resourceKind: RESOURCE_KIND, resourceId: device.id });
|
|
110
|
+
return response;
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
executeDeactivate,
|
|
114
|
+
executeRegister,
|
|
115
|
+
executeUpdate,
|
|
116
|
+
redactMutationPayload
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=deviceOps.js.map
|