@open-mercato/core 0.5.1-develop.2691.d8a0934b37 → 0.5.1-develop.2699.f8b50c8046
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/dist/modules/api_keys/data/entities.js +1 -1
- package/dist/modules/api_keys/data/entities.js.map +1 -1
- package/dist/modules/api_keys/services/apiKeyService.js +5 -5
- package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
- package/dist/modules/attachments/api/library/[id]/route.js +1 -1
- package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/library/route.js +7 -9
- package/dist/modules/attachments/api/library/route.js.map +2 -2
- package/dist/modules/attachments/api/partitions/route.js +3 -3
- package/dist/modules/attachments/api/partitions/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +6 -5
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/api/transfer/route.js +1 -1
- package/dist/modules/attachments/api/transfer/route.js.map +2 -2
- package/dist/modules/attachments/data/entities.js +2 -1
- package/dist/modules/attachments/data/entities.js.map +2 -2
- package/dist/modules/attachments/lib/ocrQueue.js +1 -1
- package/dist/modules/attachments/lib/ocrQueue.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/route.js.map +2 -2
- package/dist/modules/audit_logs/data/entities.js +1 -1
- package/dist/modules/audit_logs/data/entities.js.map +1 -1
- package/dist/modules/audit_logs/services/actionLogService.js +77 -70
- package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +1 -1
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +2 -2
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/resend-invite/route.js +1 -1
- package/dist/modules/auth/api/users/resend-invite/route.js.map +2 -2
- package/dist/modules/auth/cli.js +12 -6
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/users.js +1 -1
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/entities.js +1 -1
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +3 -3
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/services/authService.js +2 -2
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/business_rules/api/rules/route.js +3 -3
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/business_rules/api/sets/[id]/members/route.js +7 -4
- package/dist/modules/business_rules/api/sets/[id]/members/route.js.map +2 -2
- package/dist/modules/business_rules/api/sets/route.js +3 -3
- package/dist/modules/business_rules/api/sets/route.js.map +2 -2
- package/dist/modules/business_rules/cli.js +1 -1
- package/dist/modules/business_rules/cli.js.map +2 -2
- package/dist/modules/business_rules/data/entities.js +2 -9
- package/dist/modules/business_rules/data/entities.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +1 -1
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/catalog/api/option-schemas/route.js +0 -1
- package/dist/modules/catalog/api/option-schemas/route.js.map +2 -2
- package/dist/modules/catalog/data/entities.js +2 -11
- package/dist/modules/catalog/data/entities.js.map +2 -2
- package/dist/modules/configs/data/entities.js +2 -1
- package/dist/modules/configs/data/entities.js.map +2 -2
- package/dist/modules/currencies/commands/fetch-configs.js +3 -3
- package/dist/modules/currencies/commands/fetch-configs.js.map +2 -2
- package/dist/modules/currencies/data/entities.js +1 -1
- package/dist/modules/currencies/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/api/signup.js +1 -1
- package/dist/modules/customer_accounts/api/signup.js.map +2 -2
- package/dist/modules/customer_accounts/data/entities.js +1 -1
- package/dist/modules/customer_accounts/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/services/customerInvitationService.js +1 -1
- package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
- package/dist/modules/customer_accounts/services/customerSessionService.js +1 -1
- package/dist/modules/customer_accounts/services/customerSessionService.js.map +2 -2
- package/dist/modules/customer_accounts/services/customerTokenService.js +12 -7
- package/dist/modules/customer_accounts/services/customerTokenService.js.map +2 -2
- package/dist/modules/customers/api/interactions/conflicts/route.js +19 -17
- package/dist/modules/customers/api/interactions/conflicts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/counts/route.js +7 -6
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +28 -42
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/utils.js +29 -24
- package/dist/modules/customers/api/utils.js.map +2 -2
- package/dist/modules/customers/cli.js +45 -40
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/dictionaries.js +1 -1
- package/dist/modules/customers/commands/dictionaries.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +1 -1
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/customers/data/entities.js +2 -12
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/lib/interactionProjection.js +18 -15
- package/dist/modules/customers/lib/interactionProjection.js.map +2 -2
- package/dist/modules/customers/lib/personCompanyLinkTable.js +6 -8
- package/dist/modules/customers/lib/personCompanyLinkTable.js.map +2 -2
- package/dist/modules/dashboards/api/roles/widgets/route.js +1 -1
- package/dist/modules/dashboards/api/roles/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/api/users/widgets/route.js +1 -1
- package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/data/entities.js +1 -1
- package/dist/modules/dashboards/data/entities.js.map +1 -1
- package/dist/modules/data_sync/api/mappings/route.js +1 -1
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/data/entities.js +2 -1
- package/dist/modules/data_sync/data/entities.js.map +2 -2
- package/dist/modules/data_sync/lib/id-mapping.js +1 -1
- package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +1 -1
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/dictionaries/commands/factory.js +1 -1
- package/dist/modules/dictionaries/commands/factory.js.map +2 -2
- package/dist/modules/dictionaries/data/entities.js +2 -9
- package/dist/modules/dictionaries/data/entities.js.map +2 -2
- package/dist/modules/directory/commands/organizations.js +4 -4
- package/dist/modules/directory/commands/organizations.js.map +2 -2
- package/dist/modules/directory/data/entities.js +2 -1
- package/dist/modules/directory/data/entities.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +2 -2
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/encryption.js +2 -2
- package/dist/modules/entities/api/encryption.js.map +2 -2
- package/dist/modules/entities/api/relations/options.js +2 -2
- package/dist/modules/entities/api/relations/options.js.map +2 -2
- package/dist/modules/entities/cli.js +4 -4
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/entities/data/entities.js +1 -1
- package/dist/modules/entities/data/entities.js.map +2 -2
- package/dist/modules/entities/lib/field-definitions.js +2 -2
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/entities/lib/register.js +1 -1
- package/dist/modules/entities/lib/register.js.map +2 -2
- package/dist/modules/feature_toggles/data/entities.js +2 -9
- package/dist/modules/feature_toggles/data/entities.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/counts/route.js +3 -6
- package/dist/modules/inbox_ops/api/proposals/counts/route.js.map +2 -2
- package/dist/modules/inbox_ops/data/entities.js +2 -8
- package/dist/modules/inbox_ops/data/entities.js.map +2 -2
- package/dist/modules/inbox_ops/lib/messagesIntegration.js +6 -6
- package/dist/modules/inbox_ops/lib/messagesIntegration.js.map +2 -2
- package/dist/modules/integrations/data/entities.js +2 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +1 -1
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/dist/modules/integrations/lib/log-service.js +1 -1
- package/dist/modules/integrations/lib/log-service.js.map +2 -2
- package/dist/modules/integrations/lib/state-service.js +1 -1
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/messages/api/route.js +90 -93
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/api/unread-count/route.js +8 -7
- package/dist/modules/messages/api/unread-count/route.js.map +2 -2
- package/dist/modules/messages/commands/confirmations.js +1 -1
- package/dist/modules/messages/commands/confirmations.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +3 -3
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/data/entities.js +2 -1
- package/dist/modules/messages/data/entities.js.map +2 -2
- package/dist/modules/messages/lib/email-sender.js +1 -1
- package/dist/modules/messages/lib/email-sender.js.map +2 -2
- package/dist/modules/messages/lib/searchLookup.js +8 -8
- package/dist/modules/messages/lib/searchLookup.js.map +2 -2
- package/dist/modules/messages/lib/tokenConsumption.js +9 -4
- package/dist/modules/messages/lib/tokenConsumption.js.map +2 -2
- package/dist/modules/notifications/data/entities.js +2 -1
- package/dist/modules/notifications/data/entities.js.map +2 -2
- package/dist/modules/notifications/lib/notificationRecipients.js +15 -5
- package/dist/modules/notifications/lib/notificationRecipients.js.map +2 -2
- package/dist/modules/notifications/lib/notificationService.js +39 -34
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/notifications/workers/create-notification.worker.js +14 -13
- package/dist/modules/notifications/workers/create-notification.worker.js.map +2 -2
- package/dist/modules/payment_gateways/api/transactions/route.js +2 -2
- package/dist/modules/payment_gateways/api/transactions/route.js.map +2 -2
- package/dist/modules/payment_gateways/data/entities.js +2 -1
- package/dist/modules/payment_gateways/data/entities.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +1 -1
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/payment_gateways/lib/webhook-utils.js +2 -2
- package/dist/modules/payment_gateways/lib/webhook-utils.js.map +2 -2
- package/dist/modules/perspectives/data/entities.js +1 -1
- package/dist/modules/perspectives/data/entities.js.map +2 -2
- package/dist/modules/planner/data/entities.js +1 -1
- package/dist/modules/planner/data/entities.js.map +2 -2
- package/dist/modules/progress/data/entities.js +2 -1
- package/dist/modules/progress/data/entities.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +1 -1
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/api/status.js +66 -57
- package/dist/modules/query_index/api/status.js.map +2 -2
- package/dist/modules/query_index/cli.js +39 -24
- package/dist/modules/query_index/cli.js.map +2 -2
- package/dist/modules/query_index/data/entities.js +1 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/di.js +25 -13
- package/dist/modules/query_index/di.js.map +2 -2
- package/dist/modules/query_index/lib/batch.js +31 -33
- package/dist/modules/query_index/lib/batch.js.map +2 -2
- package/dist/modules/query_index/lib/coverage.js +63 -50
- package/dist/modules/query_index/lib/coverage.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +592 -588
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +74 -47
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/query_index/lib/jobs.js +37 -24
- package/dist/modules/query_index/lib/jobs.js.map +2 -2
- package/dist/modules/query_index/lib/purge.js +19 -11
- package/dist/modules/query_index/lib/purge.js.map +2 -2
- package/dist/modules/query_index/lib/reindexer.js +47 -44
- package/dist/modules/query_index/lib/reindexer.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +47 -25
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/query_index/lib/stale.js +14 -12
- package/dist/modules/query_index/lib/stale.js.map +2 -2
- package/dist/modules/query_index/lib/subscriber-scope.js +2 -2
- package/dist/modules/query_index/lib/subscriber-scope.js.map +2 -2
- package/dist/modules/query_index/subscribers/delete_one.js +3 -2
- package/dist/modules/query_index/subscribers/delete_one.js.map +2 -2
- package/dist/modules/resources/commands/tag-assignments.js +1 -1
- package/dist/modules/resources/commands/tag-assignments.js.map +2 -2
- package/dist/modules/resources/commands/tags.js +1 -1
- package/dist/modules/resources/commands/tags.js.map +2 -2
- package/dist/modules/resources/data/entities.js +2 -1
- package/dist/modules/resources/data/entities.js.map +2 -2
- package/dist/modules/sales/commands/documentAddresses.js +2 -2
- package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
- package/dist/modules/sales/commands/notes.js.map +2 -2
- package/dist/modules/sales/commands/tags.js +1 -1
- package/dist/modules/sales/commands/tags.js.map +2 -2
- package/dist/modules/sales/data/enrichers.js +9 -8
- package/dist/modules/sales/data/enrichers.js.map +2 -2
- package/dist/modules/sales/data/entities.js +2 -11
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/shipping_carriers/data/entities.js +2 -1
- package/dist/modules/shipping_carriers/data/entities.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +1 -1
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/webhook-utils.js +2 -2
- package/dist/modules/shipping_carriers/lib/webhook-utils.js.map +2 -2
- package/dist/modules/staff/data/entities.js +1 -1
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +3 -5
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/context.js +2 -2
- package/dist/modules/translations/api/context.js.map +2 -2
- package/dist/modules/translations/commands/translations.js +46 -39
- package/dist/modules/translations/commands/translations.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +19 -10
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/translations/data/entities.js +1 -1
- package/dist/modules/translations/data/entities.js.map +2 -2
- package/dist/modules/translations/lib/apply.js +4 -4
- package/dist/modules/translations/lib/apply.js.map +2 -2
- package/dist/modules/translations/lib/batch.js +3 -2
- package/dist/modules/translations/lib/batch.js.map +2 -2
- package/dist/modules/translations/subscribers/cleanup.js +3 -5
- package/dist/modules/translations/subscribers/cleanup.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +1 -1
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/cli.js +5 -5
- package/dist/modules/workflows/cli.js.map +2 -2
- package/dist/modules/workflows/data/entities.js +2 -1
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/lib/event-logger.js +2 -2
- package/dist/modules/workflows/lib/event-logger.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +16 -1
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +3 -3
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-handler.js +1 -1
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +1 -1
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/jest.config.cjs +4 -2
- package/package.json +3 -3
- package/src/modules/api_keys/data/entities.ts +1 -1
- package/src/modules/api_keys/services/apiKeyService.ts +5 -5
- package/src/modules/attachments/api/library/[id]/route.ts +1 -1
- package/src/modules/attachments/api/library/route.ts +10 -12
- package/src/modules/attachments/api/partitions/route.ts +3 -3
- package/src/modules/attachments/api/route.ts +10 -8
- package/src/modules/attachments/api/transfer/route.ts +1 -1
- package/src/modules/attachments/data/entities.ts +2 -1
- package/src/modules/attachments/lib/ocrQueue.ts +1 -1
- package/src/modules/audit_logs/api/audit-logs/actions/export/route.ts +4 -4
- package/src/modules/audit_logs/api/audit-logs/actions/route.ts +4 -4
- package/src/modules/audit_logs/data/entities.ts +1 -1
- package/src/modules/audit_logs/services/actionLogService.ts +96 -87
- package/src/modules/auth/api/roles/acl/route.ts +1 -1
- package/src/modules/auth/api/users/acl/route.ts +2 -2
- package/src/modules/auth/api/users/resend-invite/route.ts +1 -1
- package/src/modules/auth/cli.ts +46 -40
- package/src/modules/auth/commands/users.ts +1 -1
- package/src/modules/auth/data/entities.ts +1 -1
- package/src/modules/auth/lib/setup-app.ts +3 -3
- package/src/modules/auth/services/authService.ts +2 -2
- package/src/modules/business_rules/api/rules/route.ts +3 -3
- package/src/modules/business_rules/api/sets/[id]/members/route.ts +7 -4
- package/src/modules/business_rules/api/sets/route.ts +3 -3
- package/src/modules/business_rules/cli.ts +1 -1
- package/src/modules/business_rules/data/entities.ts +2 -9
- package/src/modules/business_rules/lib/rule-engine.ts +1 -1
- package/src/modules/catalog/api/option-schemas/route.ts +0 -1
- package/src/modules/catalog/data/entities.ts +2 -11
- package/src/modules/configs/data/entities.ts +2 -1
- package/src/modules/currencies/commands/fetch-configs.ts +3 -3
- package/src/modules/currencies/data/entities.ts +1 -1
- package/src/modules/customer_accounts/api/signup.ts +1 -1
- package/src/modules/customer_accounts/data/entities.ts +1 -1
- package/src/modules/customer_accounts/services/customerInvitationService.ts +1 -1
- package/src/modules/customer_accounts/services/customerSessionService.ts +1 -1
- package/src/modules/customer_accounts/services/customerTokenService.ts +26 -15
- package/src/modules/customers/api/interactions/conflicts/route.ts +26 -23
- package/src/modules/customers/api/interactions/counts/route.ts +13 -11
- package/src/modules/customers/api/interactions/route.ts +32 -44
- package/src/modules/customers/api/utils.ts +45 -37
- package/src/modules/customers/cli.ts +88 -67
- package/src/modules/customers/commands/dictionaries.ts +1 -1
- package/src/modules/customers/commands/tags.ts +1 -1
- package/src/modules/customers/data/entities.ts +2 -12
- package/src/modules/customers/lib/interactionProjection.ts +36 -25
- package/src/modules/customers/lib/personCompanyLinkTable.ts +13 -18
- package/src/modules/dashboards/api/roles/widgets/route.ts +1 -1
- package/src/modules/dashboards/api/users/widgets/route.ts +1 -1
- package/src/modules/dashboards/data/entities.ts +1 -1
- package/src/modules/data_sync/api/mappings/route.ts +1 -1
- package/src/modules/data_sync/data/entities.ts +2 -1
- package/src/modules/data_sync/lib/id-mapping.ts +1 -1
- package/src/modules/data_sync/lib/sync-run-service.ts +1 -1
- package/src/modules/dictionaries/commands/factory.ts +1 -1
- package/src/modules/dictionaries/data/entities.ts +2 -9
- package/src/modules/directory/commands/organizations.ts +4 -4
- package/src/modules/directory/data/entities.ts +2 -1
- package/src/modules/entities/api/definitions.ts +2 -2
- package/src/modules/entities/api/encryption.ts +2 -2
- package/src/modules/entities/api/relations/options.ts +8 -3
- package/src/modules/entities/cli.ts +4 -4
- package/src/modules/entities/data/entities.ts +1 -1
- package/src/modules/entities/lib/field-definitions.ts +2 -2
- package/src/modules/entities/lib/register.ts +1 -1
- package/src/modules/feature_toggles/data/entities.ts +2 -9
- package/src/modules/inbox_ops/api/proposals/counts/route.ts +10 -10
- package/src/modules/inbox_ops/data/entities.ts +2 -8
- package/src/modules/inbox_ops/lib/messagesIntegration.ts +12 -11
- package/src/modules/integrations/data/entities.ts +2 -1
- package/src/modules/integrations/lib/credentials-service.ts +1 -1
- package/src/modules/integrations/lib/log-service.ts +1 -1
- package/src/modules/integrations/lib/state-service.ts +1 -1
- package/src/modules/messages/api/route.ts +134 -123
- package/src/modules/messages/api/unread-count/route.ts +19 -16
- package/src/modules/messages/commands/confirmations.ts +1 -1
- package/src/modules/messages/commands/messages.ts +3 -3
- package/src/modules/messages/data/entities.ts +2 -1
- package/src/modules/messages/lib/email-sender.ts +1 -1
- package/src/modules/messages/lib/searchLookup.ts +16 -13
- package/src/modules/messages/lib/tokenConsumption.ts +16 -8
- package/src/modules/notifications/data/entities.ts +2 -1
- package/src/modules/notifications/lib/notificationRecipients.ts +42 -26
- package/src/modules/notifications/lib/notificationService.ts +53 -42
- package/src/modules/notifications/workers/create-notification.worker.ts +20 -17
- package/src/modules/payment_gateways/api/transactions/route.ts +2 -2
- package/src/modules/payment_gateways/data/entities.ts +2 -1
- package/src/modules/payment_gateways/lib/gateway-service.ts +1 -1
- package/src/modules/payment_gateways/lib/webhook-utils.ts +2 -2
- package/src/modules/perspectives/data/entities.ts +1 -1
- package/src/modules/planner/data/entities.ts +1 -1
- package/src/modules/progress/data/entities.ts +2 -1
- package/src/modules/progress/lib/progressServiceImpl.ts +1 -1
- package/src/modules/query_index/api/status.ts +85 -71
- package/src/modules/query_index/cli.ts +51 -31
- package/src/modules/query_index/data/entities.ts +1 -1
- package/src/modules/query_index/di.ts +41 -16
- package/src/modules/query_index/lib/batch.ts +68 -55
- package/src/modules/query_index/lib/coverage.ts +115 -88
- package/src/modules/query_index/lib/engine.ts +1036 -1096
- package/src/modules/query_index/lib/indexer.ts +115 -79
- package/src/modules/query_index/lib/jobs.ts +51 -31
- package/src/modules/query_index/lib/purge.ts +25 -19
- package/src/modules/query_index/lib/reindexer.ts +97 -84
- package/src/modules/query_index/lib/search-tokens.ts +67 -36
- package/src/modules/query_index/lib/stale.ts +14 -17
- package/src/modules/query_index/lib/subscriber-scope.ts +6 -5
- package/src/modules/query_index/subscribers/delete_one.ts +9 -6
- package/src/modules/resources/commands/tag-assignments.ts +1 -1
- package/src/modules/resources/commands/tags.ts +1 -1
- package/src/modules/resources/data/entities.ts +2 -1
- package/src/modules/sales/commands/documentAddresses.ts +2 -2
- package/src/modules/sales/commands/notes.ts +1 -1
- package/src/modules/sales/commands/tags.ts +1 -1
- package/src/modules/sales/data/enrichers.ts +17 -13
- package/src/modules/sales/data/entities.ts +2 -11
- package/src/modules/shipping_carriers/data/entities.ts +2 -1
- package/src/modules/shipping_carriers/lib/shipping-service.ts +1 -1
- package/src/modules/shipping_carriers/lib/webhook-utils.ts +2 -2
- package/src/modules/staff/data/entities.ts +1 -1
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +14 -11
- package/src/modules/translations/api/context.ts +4 -4
- package/src/modules/translations/commands/translations.ts +116 -81
- package/src/modules/translations/components/TranslationManager.tsx +23 -14
- package/src/modules/translations/data/entities.ts +1 -1
- package/src/modules/translations/i18n/de.json +1 -0
- package/src/modules/translations/i18n/en.json +1 -0
- package/src/modules/translations/i18n/es.json +1 -0
- package/src/modules/translations/i18n/pl.json +1 -0
- package/src/modules/translations/lib/apply.ts +6 -6
- package/src/modules/translations/lib/batch.ts +9 -7
- package/src/modules/translations/subscribers/cleanup.ts +10 -11
- package/src/modules/workflows/api/definitions/route.ts +1 -1
- package/src/modules/workflows/cli.ts +5 -5
- package/src/modules/workflows/data/entities.ts +2 -1
- package/src/modules/workflows/lib/event-logger.ts +2 -2
- package/src/modules/workflows/lib/seeds.ts +16 -1
- package/src/modules/workflows/lib/step-handler.ts +3 -3
- package/src/modules/workflows/lib/task-handler.ts +1 -1
- package/src/modules/workflows/lib/transition-handler.ts +1 -1
- package/src/modules/workflows/lib/workflow-executor.ts +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { sql } from "kysely";
|
|
2
3
|
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
4
|
import { hashForLookup } from "@open-mercato/shared/lib/encryption/aes";
|
|
4
5
|
import { User } from "../../auth/data/entities.js";
|
|
@@ -17,8 +18,8 @@ import {
|
|
|
17
18
|
messageListItemSchema
|
|
18
19
|
} from "./openapi.js";
|
|
19
20
|
const NO_MATCH_ID = "00000000-0000-0000-0000-000000000000";
|
|
20
|
-
function
|
|
21
|
-
return em.
|
|
21
|
+
function getDb(em) {
|
|
22
|
+
return em.getKysely();
|
|
22
23
|
}
|
|
23
24
|
const metadata = {
|
|
24
25
|
GET: { requireAuth: true, requireFeatures: ["messages.view"] },
|
|
@@ -30,104 +31,100 @@ async function GET(req) {
|
|
|
30
31
|
const url = new URL(req.url);
|
|
31
32
|
const params = Object.fromEntries(url.searchParams);
|
|
32
33
|
const input = listMessagesSchema.parse(params);
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
query
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
45
|
-
switch (input.folder) {
|
|
46
|
-
case "inbox":
|
|
47
|
-
joinRecipient();
|
|
48
|
-
query = query.whereNotNull("r.message_id").whereNull("r.deleted_at").whereNull("r.archived_at").where("m.is_draft", false);
|
|
49
|
-
break;
|
|
50
|
-
case "archived":
|
|
51
|
-
joinRecipient();
|
|
52
|
-
query = query.whereNotNull("r.message_id").whereNull("r.deleted_at").whereNotNull("r.archived_at");
|
|
53
|
-
break;
|
|
54
|
-
case "sent":
|
|
55
|
-
query = query.where("m.sender_user_id", scope.userId).where("m.is_draft", false);
|
|
56
|
-
joinRecipient();
|
|
57
|
-
break;
|
|
58
|
-
case "drafts":
|
|
59
|
-
query = query.where("m.sender_user_id", scope.userId).where("m.is_draft", true);
|
|
60
|
-
joinRecipient();
|
|
61
|
-
break;
|
|
62
|
-
case "all":
|
|
63
|
-
joinRecipient();
|
|
64
|
-
query = query.where(function() {
|
|
65
|
-
this.where("m.sender_user_id", scope.userId).orWhereNotNull("r.message_id");
|
|
66
|
-
});
|
|
67
|
-
break;
|
|
68
|
-
default: {
|
|
69
|
-
const unsupportedFolder = input.folder;
|
|
70
|
-
throw new Error(`Unsupported folder: ${String(unsupportedFolder)}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (input.status) {
|
|
74
|
-
query = query.where("r.status", input.status);
|
|
75
|
-
}
|
|
76
|
-
if (input.type) {
|
|
77
|
-
query = query.where("m.type", input.type);
|
|
78
|
-
}
|
|
79
|
-
if (input.visibility) {
|
|
80
|
-
query = query.where("m.visibility", input.visibility);
|
|
81
|
-
}
|
|
82
|
-
if (input.sourceEntityType) {
|
|
83
|
-
query = query.where("m.source_entity_type", input.sourceEntityType);
|
|
84
|
-
}
|
|
85
|
-
if (input.sourceEntityId) {
|
|
86
|
-
query = query.where("m.source_entity_id", input.sourceEntityId);
|
|
87
|
-
}
|
|
88
|
-
if (input.externalEmail) {
|
|
89
|
-
query = query.where("m.external_email_hash", hashForLookup(input.externalEmail));
|
|
90
|
-
}
|
|
91
|
-
if (input.senderId) {
|
|
92
|
-
query = query.where("m.sender_user_id", input.senderId);
|
|
93
|
-
}
|
|
94
|
-
if (input.search) {
|
|
95
|
-
const matchedIds = await findMessageIdsBySearchTokens({
|
|
96
|
-
em,
|
|
97
|
-
query: input.search,
|
|
98
|
-
tenantId: scope.tenantId ?? null,
|
|
99
|
-
organizationId: scope.organizationId
|
|
100
|
-
});
|
|
101
|
-
if (matchedIds === null || matchedIds.length === 0) {
|
|
102
|
-
query = query.where("m.id", NO_MATCH_ID);
|
|
34
|
+
const db = getDb(em);
|
|
35
|
+
const searchIds = input.search ? await findMessageIdsBySearchTokens({
|
|
36
|
+
em,
|
|
37
|
+
query: input.search,
|
|
38
|
+
tenantId: scope.tenantId ?? null,
|
|
39
|
+
organizationId: scope.organizationId
|
|
40
|
+
}) : void 0;
|
|
41
|
+
const buildBaseQuery = () => {
|
|
42
|
+
let q = db.selectFrom("messages as m").where("m.tenant_id", "=", scope.tenantId).where("m.deleted_at", "is", null);
|
|
43
|
+
if (scope.organizationId) {
|
|
44
|
+
q = q.where("m.organization_id", "=", scope.organizationId);
|
|
103
45
|
} else {
|
|
104
|
-
|
|
46
|
+
q = q.where("m.organization_id", "is", null);
|
|
105
47
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
48
|
+
const joinRecipient = () => {
|
|
49
|
+
q = q.leftJoin("message_recipients as r", (jb) => jb.onRef("m.id", "=", "r.message_id").on("r.recipient_user_id", "=", scope.userId));
|
|
50
|
+
};
|
|
51
|
+
switch (input.folder) {
|
|
52
|
+
case "inbox":
|
|
53
|
+
joinRecipient();
|
|
54
|
+
q = q.where("r.message_id", "is not", null).where("r.deleted_at", "is", null).where("r.archived_at", "is", null).where("m.is_draft", "=", false);
|
|
55
|
+
break;
|
|
56
|
+
case "archived":
|
|
57
|
+
joinRecipient();
|
|
58
|
+
q = q.where("r.message_id", "is not", null).where("r.deleted_at", "is", null).where("r.archived_at", "is not", null);
|
|
59
|
+
break;
|
|
60
|
+
case "sent":
|
|
61
|
+
q = q.where("m.sender_user_id", "=", scope.userId).where("m.is_draft", "=", false);
|
|
62
|
+
joinRecipient();
|
|
63
|
+
break;
|
|
64
|
+
case "drafts":
|
|
65
|
+
q = q.where("m.sender_user_id", "=", scope.userId).where("m.is_draft", "=", true);
|
|
66
|
+
joinRecipient();
|
|
67
|
+
break;
|
|
68
|
+
case "all":
|
|
69
|
+
joinRecipient();
|
|
70
|
+
q = q.where((eb) => eb.or([
|
|
71
|
+
eb("m.sender_user_id", "=", scope.userId),
|
|
72
|
+
eb("r.message_id", "is not", null)
|
|
73
|
+
]));
|
|
74
|
+
break;
|
|
75
|
+
default: {
|
|
76
|
+
const unsupportedFolder = input.folder;
|
|
77
|
+
throw new Error(`Unsupported folder: ${String(unsupportedFolder)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (input.status) q = q.where("r.status", "=", input.status);
|
|
81
|
+
if (input.type) q = q.where("m.type", "=", input.type);
|
|
82
|
+
if (input.visibility) q = q.where("m.visibility", "=", input.visibility);
|
|
83
|
+
if (input.sourceEntityType) q = q.where("m.source_entity_type", "=", input.sourceEntityType);
|
|
84
|
+
if (input.sourceEntityId) q = q.where("m.source_entity_id", "=", input.sourceEntityId);
|
|
85
|
+
if (input.externalEmail) q = q.where("m.external_email_hash", "=", hashForLookup(input.externalEmail));
|
|
86
|
+
if (input.senderId) q = q.where("m.sender_user_id", "=", input.senderId);
|
|
87
|
+
if (input.search) {
|
|
88
|
+
if (!searchIds || searchIds.length === 0) {
|
|
89
|
+
q = q.where("m.id", "=", NO_MATCH_ID);
|
|
90
|
+
} else {
|
|
91
|
+
q = q.where("m.id", "in", searchIds);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (input.since) q = q.where("m.sent_at", ">", new Date(input.since));
|
|
95
|
+
if (input.hasObjects !== void 0) {
|
|
96
|
+
const existsFn = (eb) => eb.exists(
|
|
97
|
+
eb.selectFrom("message_objects").select(sql`1`.as("one")).whereRef("message_objects.message_id", "=", "m.id")
|
|
98
|
+
);
|
|
99
|
+
const notExistsFn = (eb) => eb.not(eb.exists(
|
|
100
|
+
eb.selectFrom("message_objects").select(sql`1`.as("one")).whereRef("message_objects.message_id", "=", "m.id")
|
|
101
|
+
));
|
|
102
|
+
q = input.hasObjects ? q.where(existsFn) : q.where(notExistsFn);
|
|
103
|
+
}
|
|
104
|
+
if (input.hasAttachments !== void 0) {
|
|
105
|
+
const existsFn = (eb) => eb.exists(
|
|
106
|
+
eb.selectFrom("attachments").select(sql`1`.as("one")).where("attachments.entity_id", "=", MESSAGE_ATTACHMENT_ENTITY_ID).whereRef("attachments.record_id", "=", "m.id")
|
|
107
|
+
);
|
|
108
|
+
const notExistsFn = (eb) => eb.not(eb.exists(
|
|
109
|
+
eb.selectFrom("attachments").select(sql`1`.as("one")).where("attachments.entity_id", "=", MESSAGE_ATTACHMENT_ENTITY_ID).whereRef("attachments.record_id", "=", "m.id")
|
|
110
|
+
));
|
|
111
|
+
q = input.hasAttachments ? q.where(existsFn) : q.where(notExistsFn);
|
|
112
|
+
}
|
|
113
|
+
if (input.hasActions !== void 0) {
|
|
114
|
+
q = input.hasActions ? q.where("m.action_data", "is not", null) : q.where("m.action_data", "is", null);
|
|
115
|
+
}
|
|
116
|
+
return q;
|
|
117
|
+
};
|
|
118
|
+
const countResult = await buildBaseQuery().select(sql`count(*)`.as("count")).executeTakeFirst();
|
|
122
119
|
const total = Number(countResult?.count ?? 0);
|
|
123
120
|
const offset = (input.page - 1) * input.pageSize;
|
|
124
|
-
const scopeRows = await
|
|
121
|
+
const scopeRows = await buildBaseQuery().select([
|
|
125
122
|
"m.id",
|
|
126
123
|
"m.sender_user_id",
|
|
127
124
|
"m.is_draft",
|
|
128
125
|
"r.status as recipient_status",
|
|
129
126
|
"r.read_at"
|
|
130
|
-
).orderBy("m.sent_at", "desc").offset(offset).limit(input.pageSize);
|
|
127
|
+
]).orderBy("m.sent_at", "desc").offset(offset).limit(input.pageSize).execute();
|
|
131
128
|
const typedRows = scopeRows;
|
|
132
129
|
const messageIds = typedRows.map((row) => row.id);
|
|
133
130
|
const messageEntities = messageIds.length > 0 ? await findWithDecryption(
|
|
@@ -147,12 +144,12 @@ async function GET(req) {
|
|
|
147
144
|
acc[obj.messageId].push(obj);
|
|
148
145
|
return acc;
|
|
149
146
|
}, {});
|
|
150
|
-
const attachmentCounts = messageIds.length > 0 ? await
|
|
147
|
+
const attachmentCounts = messageIds.length > 0 ? await getDb(em).selectFrom("attachments").select(["record_id", sql`count(*)`.as("count")]).where("entity_id", "=", MESSAGE_ATTACHMENT_ENTITY_ID).where("record_id", "in", messageIds).groupBy("record_id").execute() : [];
|
|
151
148
|
const attachmentCountByMessage = attachmentCounts.reduce((acc, row) => {
|
|
152
149
|
acc[row.record_id] = Number(row.count);
|
|
153
150
|
return acc;
|
|
154
151
|
}, {});
|
|
155
|
-
const recipientCounts = messageIds.length > 0 ? await
|
|
152
|
+
const recipientCounts = messageIds.length > 0 ? await getDb(em).selectFrom("message_recipients").select(["message_id", sql`count(*)`.as("count")]).where("message_id", "in", messageIds).where("deleted_at", "is", null).groupBy("message_id").execute() : [];
|
|
156
153
|
const recipientCountByMessage = recipientCounts.reduce((acc, row) => {
|
|
157
154
|
acc[row.message_id] = Number(row.count);
|
|
158
155
|
return acc;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/messages/api/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport { User } from '../../auth/data/entities'\nimport { Message, MessageObject } from '../data/entities'\nimport { composeMessageSchema, listMessagesSchema } from '../data/validators'\nimport { MESSAGE_ATTACHMENT_ENTITY_ID } from '../lib/constants'\nimport { getMessageType } from '../lib/message-types-registry'\nimport { validateMessageObjectsForType } from '../lib/object-validation'\nimport { attachOperationMetadataHeader } from '../lib/operationMetadata'\nimport { canUseMessageEmailFeature, resolveMessageContext } from '../lib/routeHelpers'\nimport { findMessageIdsBySearchTokens } from '../lib/searchLookup'\nimport { MessageCommandExecuteResult } from '../commands/shared'\nimport {\n composeMessageSchema as composeSchema,\n composeResponseSchema,\n listMessagesSchema as listSchema,\n messageListItemSchema,\n} from './openapi'\n\ntype MessageCommandExecuteResultWithThreadId = MessageCommandExecuteResult & {\n threadId: string\n}\n\nconst NO_MATCH_ID = '00000000-0000-0000-0000-000000000000'\n\nfunction getKnex(em: EntityManager): Knex {\n return (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()\n}\n\ntype MessageListScopeRow = {\n id: string\n sender_user_id: string\n is_draft: boolean\n recipient_status: string | null\n read_at: string | null\n}\n\ntype AttachmentCountRow = {\n record_id: string\n count: string | number\n}\n\ntype RecipientCountRow = {\n message_id: string\n count: string | number\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['messages.view'] },\n POST: { requireAuth: true, requireFeatures: ['messages.compose'] },\n}\n\nexport async function GET(req: Request) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = ctx.container.resolve('em') as EntityManager\n const url = new URL(req.url)\n const params = Object.fromEntries(url.searchParams)\n const input = listMessagesSchema.parse(params)\n const knex = getKnex(em)\n\n let query = knex('messages as m')\n .where('m.tenant_id', scope.tenantId)\n .whereNull('m.deleted_at')\n\n if (scope.organizationId) {\n query = query.where('m.organization_id', scope.organizationId)\n } else {\n query = query.whereNull('m.organization_id')\n }\n\n const joinRecipient = () => {\n query = query.leftJoin('message_recipients as r', function () {\n this.on('m.id', '=', 'r.message_id').andOn('r.recipient_user_id', '=', knex.raw('?', [scope.userId]))\n })\n }\n\n switch (input.folder) {\n case 'inbox':\n joinRecipient()\n query = query\n .whereNotNull('r.message_id')\n .whereNull('r.deleted_at')\n .whereNull('r.archived_at')\n .where('m.is_draft', false)\n break\n case 'archived':\n joinRecipient()\n query = query\n .whereNotNull('r.message_id')\n .whereNull('r.deleted_at')\n .whereNotNull('r.archived_at')\n break\n case 'sent':\n query = query\n .where('m.sender_user_id', scope.userId)\n .where('m.is_draft', false)\n joinRecipient()\n break\n case 'drafts':\n query = query\n .where('m.sender_user_id', scope.userId)\n .where('m.is_draft', true)\n joinRecipient()\n break\n case 'all':\n joinRecipient()\n query = query.where(function () {\n this.where('m.sender_user_id', scope.userId).orWhereNotNull('r.message_id')\n })\n break\n default: {\n const unsupportedFolder: never = input.folder\n throw new Error(`Unsupported folder: ${String(unsupportedFolder)}`)\n }\n }\n\n if (input.status) {\n query = query.where('r.status', input.status)\n }\n\n if (input.type) {\n query = query.where('m.type', input.type)\n }\n\n if (input.visibility) {\n query = query.where('m.visibility', input.visibility)\n }\n\n if (input.sourceEntityType) {\n query = query.where('m.source_entity_type', input.sourceEntityType)\n }\n\n if (input.sourceEntityId) {\n query = query.where('m.source_entity_id', input.sourceEntityId)\n }\n\n if (input.externalEmail) {\n query = query.where('m.external_email_hash', hashForLookup(input.externalEmail))\n }\n\n if (input.senderId) {\n query = query.where('m.sender_user_id', input.senderId)\n }\n\n if (input.search) {\n const matchedIds = await findMessageIdsBySearchTokens({\n em,\n query: input.search,\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n })\n if (matchedIds === null || matchedIds.length === 0) {\n query = query.where('m.id', NO_MATCH_ID)\n } else {\n query = query.whereIn('m.id', matchedIds)\n }\n }\n\n if (input.since) {\n query = query.where('m.sent_at', '>', new Date(input.since))\n }\n\n if (input.hasObjects !== undefined) {\n const subquery = knex('message_objects').select(1).whereRaw('message_objects.message_id = m.id')\n query = input.hasObjects ? query.whereExists(subquery) : query.whereNotExists(subquery)\n }\n\n if (input.hasAttachments !== undefined) {\n const subquery = knex('attachments')\n .select(1)\n .where('attachments.entity_id', MESSAGE_ATTACHMENT_ENTITY_ID)\n .whereRaw('attachments.record_id = m.id')\n query = input.hasAttachments ? query.whereExists(subquery) : query.whereNotExists(subquery)\n }\n\n if (input.hasActions !== undefined) {\n query = input.hasActions ? query.whereNotNull('m.action_data') : query.whereNull('m.action_data')\n }\n\n const countResult = await query.clone().count('* as count').first()\n const total = Number(countResult?.count ?? 0)\n\n const offset = (input.page - 1) * input.pageSize\n const scopeRows = await query\n .select(\n 'm.id',\n 'm.sender_user_id',\n 'm.is_draft',\n 'r.status as recipient_status',\n 'r.read_at',\n )\n .orderBy('m.sent_at', 'desc')\n .offset(offset)\n .limit(input.pageSize)\n\n const typedRows = scopeRows as MessageListScopeRow[]\n const messageIds = typedRows.map((row) => row.id)\n\n const messageEntities = messageIds.length > 0\n ? await findWithDecryption(\n em,\n Message,\n { id: { $in: messageIds } },\n undefined,\n { tenantId: scope.tenantId, organizationId: scope.organizationId }\n )\n : []\n\n const messagesById = new Map<string, Message>()\n for (const message of messageEntities) {\n messagesById.set(message.id, message)\n }\n\n const objects = messageIds.length > 0\n ? await em.find(MessageObject, { messageId: { $in: messageIds } })\n : []\n\n const objectsByMessage = objects.reduce((acc, obj) => {\n if (!acc[obj.messageId]) acc[obj.messageId] = []\n acc[obj.messageId].push(obj)\n return acc\n }, {} as Record<string, MessageObject[]>)\n\n const attachmentCounts: AttachmentCountRow[] = messageIds.length > 0\n ? await getKnex(em)('attachments')\n .select('record_id')\n .count('* as count')\n .where('entity_id', MESSAGE_ATTACHMENT_ENTITY_ID)\n .whereIn('record_id', messageIds)\n .groupBy('record_id')\n : []\n\n const attachmentCountByMessage = attachmentCounts.reduce((acc: Record<string, number>, row) => {\n acc[row.record_id] = Number(row.count)\n return acc\n }, {})\n\n const recipientCounts: RecipientCountRow[] = messageIds.length > 0\n ? await getKnex(em)('message_recipients')\n .select('message_id')\n .count('* as count')\n .whereIn('message_id', messageIds)\n .whereNull('deleted_at')\n .groupBy('message_id')\n : []\n\n const recipientCountByMessage = recipientCounts.reduce((acc: Record<string, number>, row) => {\n acc[row.message_id] = Number(row.count)\n return acc\n }, {})\n\n const senderUserIds = Array.from(new Set(typedRows.map((row) => row.sender_user_id).filter(Boolean)))\n const senderUsers = senderUserIds.length > 0\n ? await findWithDecryption(\n em,\n User,\n { id: { $in: senderUserIds } },\n undefined,\n { tenantId: scope.tenantId, organizationId: scope.organizationId }\n )\n : []\n\n const senderMetaById = new Map<string, { name: string | null; email: string | null }>()\n senderUsers.forEach((user) => {\n const name = typeof user.name === 'string' && user.name.trim().length ? user.name.trim() : null\n senderMetaById.set(user.id, { name, email: user.email ?? null })\n })\n\n return Response.json({\n items: typedRows\n .map((row) => {\n const message = messagesById.get(row.id)\n if (!message) return null\n const body = typeof message.body === 'string' ? message.body : ''\n const bodyPreview = body.substring(0, 150) + (body.length > 150 ? '...' : '')\n const actionData = message.actionData ?? null\n return {\n ...(senderMetaById.get(row.sender_user_id)\n ? {\n senderName: senderMetaById.get(row.sender_user_id)?.name ?? null,\n senderEmail: senderMetaById.get(row.sender_user_id)?.email ?? null,\n }\n : { senderName: null, senderEmail: null }),\n id: message.id,\n type: message.type,\n visibility: message.visibility ?? null,\n sourceEntityType: message.sourceEntityType ?? null,\n sourceEntityId: message.sourceEntityId ?? null,\n externalEmail: message.externalEmail ?? null,\n externalName: message.externalName ?? null,\n subject: message.subject,\n bodyPreview,\n senderUserId: message.senderUserId,\n priority: message.priority,\n status: row.recipient_status ?? (row.is_draft ? 'draft' : 'sent'),\n hasObjects: (objectsByMessage[message.id] || []).length > 0,\n objectCount: (objectsByMessage[message.id] || []).length,\n hasAttachments: (attachmentCountByMessage[message.id] || 0) > 0,\n attachmentCount: attachmentCountByMessage[message.id] || 0,\n recipientCount: recipientCountByMessage[message.id] || 0,\n hasActions:\n Boolean(actionData?.actions?.length)\n || Boolean(getMessageType(message.type)?.defaultActions?.length)\n || (objectsByMessage[message.id] || []).some((item) => item.actionRequired && Boolean(item.actionType)),\n actionTaken: message.actionTaken ?? null,\n sentAt: message.sentAt ? message.sentAt.toISOString() : null,\n readAt: row.read_at,\n threadId: message.threadId ?? null,\n }\n })\n .filter((item): item is NonNullable<typeof item> => item !== null),\n page: input.page,\n pageSize: input.pageSize,\n total,\n totalPages: Math.ceil(total / input.pageSize),\n })\n}\n\nexport async function POST(req: Request) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const body = await req.json().catch(() => ({}))\n const input = composeMessageSchema.parse(body)\n\n const isPublicVisibility = input.visibility === 'public'\n const sendViaEmail = isPublicVisibility ? true : input.sendViaEmail\n if (sendViaEmail && !(await canUseMessageEmailFeature(ctx, scope))) {\n return Response.json({ error: 'Missing feature: messages.email' }, { status: 403 })\n }\n\n if (input.objects?.length) {\n const objectValidationError = validateMessageObjectsForType(input.type, input.objects)\n if (objectValidationError) {\n return Response.json({ error: objectValidationError }, { status: 400 })\n }\n }\n\n const { result, logEntry } = await commandBus.execute('messages.messages.compose', {\n input: {\n ...input,\n sendViaEmail,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n const { id: messageId, threadId: responseThreadId } = result as unknown as MessageCommandExecuteResultWithThreadId\n\n const response = Response.json({ id: messageId, threadId: responseThreadId }, { status: 201 })\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.message',\n resourceId: messageId,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n GET: {\n summary: 'List messages',\n query: listSchema,\n responses: [\n {\n status: 200,\n description: 'Message list',\n schema: z.object({\n items: z.array(messageListItemSchema),\n page: z.number(),\n pageSize: z.number(),\n total: z.number(),\n totalPages: z.number(),\n }),\n },\n ],\n },\n POST: {\n summary: 'Compose a message',\n requestBody: {\n schema: composeSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Message created',\n schema: composeResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport { User } from '../../auth/data/entities'\nimport { Message, MessageObject } from '../data/entities'\nimport { composeMessageSchema, listMessagesSchema } from '../data/validators'\nimport { MESSAGE_ATTACHMENT_ENTITY_ID } from '../lib/constants'\nimport { getMessageType } from '../lib/message-types-registry'\nimport { validateMessageObjectsForType } from '../lib/object-validation'\nimport { attachOperationMetadataHeader } from '../lib/operationMetadata'\nimport { canUseMessageEmailFeature, resolveMessageContext } from '../lib/routeHelpers'\nimport { findMessageIdsBySearchTokens } from '../lib/searchLookup'\nimport { MessageCommandExecuteResult } from '../commands/shared'\nimport {\n composeMessageSchema as composeSchema,\n composeResponseSchema,\n listMessagesSchema as listSchema,\n messageListItemSchema,\n} from './openapi'\n\ntype MessageCommandExecuteResultWithThreadId = MessageCommandExecuteResult & {\n threadId: string\n}\n\nconst NO_MATCH_ID = '00000000-0000-0000-0000-000000000000'\n\nfunction getDb(em: EntityManager): Kysely<any> {\n return em.getKysely<any>()\n}\n\ntype MessageListScopeRow = {\n id: string\n sender_user_id: string\n is_draft: boolean\n recipient_status: string | null\n read_at: string | null\n}\n\ntype AttachmentCountRow = {\n record_id: string\n count: string | number\n}\n\ntype RecipientCountRow = {\n message_id: string\n count: string | number\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['messages.view'] },\n POST: { requireAuth: true, requireFeatures: ['messages.compose'] },\n}\n\nexport async function GET(req: Request) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = ctx.container.resolve('em') as EntityManager\n const url = new URL(req.url)\n const params = Object.fromEntries(url.searchParams)\n const input = listMessagesSchema.parse(params)\n const db = getDb(em) as any\n\n const searchIds = input.search\n ? await findMessageIdsBySearchTokens({\n em,\n query: input.search,\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n })\n : undefined\n\n const buildBaseQuery = () => {\n let q: any = db\n .selectFrom('messages as m')\n .where('m.tenant_id', '=', scope.tenantId)\n .where('m.deleted_at', 'is', null)\n\n if (scope.organizationId) {\n q = q.where('m.organization_id', '=', scope.organizationId)\n } else {\n q = q.where('m.organization_id', 'is', null)\n }\n\n const joinRecipient = () => {\n q = q.leftJoin('message_recipients as r', (jb: any) => jb\n .onRef('m.id', '=', 'r.message_id')\n .on('r.recipient_user_id', '=', scope.userId))\n }\n\n switch (input.folder) {\n case 'inbox':\n joinRecipient()\n q = q\n .where('r.message_id', 'is not', null)\n .where('r.deleted_at', 'is', null)\n .where('r.archived_at', 'is', null)\n .where('m.is_draft', '=', false)\n break\n case 'archived':\n joinRecipient()\n q = q\n .where('r.message_id', 'is not', null)\n .where('r.deleted_at', 'is', null)\n .where('r.archived_at', 'is not', null)\n break\n case 'sent':\n q = q\n .where('m.sender_user_id', '=', scope.userId)\n .where('m.is_draft', '=', false)\n joinRecipient()\n break\n case 'drafts':\n q = q\n .where('m.sender_user_id', '=', scope.userId)\n .where('m.is_draft', '=', true)\n joinRecipient()\n break\n case 'all':\n joinRecipient()\n q = q.where((eb: any) => eb.or([\n eb('m.sender_user_id', '=', scope.userId),\n eb('r.message_id', 'is not', null),\n ]))\n break\n default: {\n const unsupportedFolder: never = input.folder\n throw new Error(`Unsupported folder: ${String(unsupportedFolder)}`)\n }\n }\n\n if (input.status) q = q.where('r.status', '=', input.status)\n if (input.type) q = q.where('m.type', '=', input.type)\n if (input.visibility) q = q.where('m.visibility', '=', input.visibility)\n if (input.sourceEntityType) q = q.where('m.source_entity_type', '=', input.sourceEntityType)\n if (input.sourceEntityId) q = q.where('m.source_entity_id', '=', input.sourceEntityId)\n if (input.externalEmail) q = q.where('m.external_email_hash', '=', hashForLookup(input.externalEmail))\n if (input.senderId) q = q.where('m.sender_user_id', '=', input.senderId)\n\n if (input.search) {\n if (!searchIds || searchIds.length === 0) {\n q = q.where('m.id', '=', NO_MATCH_ID)\n } else {\n q = q.where('m.id', 'in', searchIds)\n }\n }\n\n if (input.since) q = q.where('m.sent_at', '>', new Date(input.since))\n\n if (input.hasObjects !== undefined) {\n const existsFn = (eb: any) => eb.exists(\n eb.selectFrom('message_objects')\n .select(sql<number>`1`.as('one'))\n .whereRef('message_objects.message_id', '=', 'm.id')\n )\n const notExistsFn = (eb: any) => eb.not(eb.exists(\n eb.selectFrom('message_objects')\n .select(sql<number>`1`.as('one'))\n .whereRef('message_objects.message_id', '=', 'm.id')\n ))\n q = input.hasObjects ? q.where(existsFn) : q.where(notExistsFn)\n }\n\n if (input.hasAttachments !== undefined) {\n const existsFn = (eb: any) => eb.exists(\n eb.selectFrom('attachments')\n .select(sql<number>`1`.as('one'))\n .where('attachments.entity_id', '=', MESSAGE_ATTACHMENT_ENTITY_ID)\n .whereRef('attachments.record_id', '=', 'm.id')\n )\n const notExistsFn = (eb: any) => eb.not(eb.exists(\n eb.selectFrom('attachments')\n .select(sql<number>`1`.as('one'))\n .where('attachments.entity_id', '=', MESSAGE_ATTACHMENT_ENTITY_ID)\n .whereRef('attachments.record_id', '=', 'm.id')\n ))\n q = input.hasAttachments ? q.where(existsFn) : q.where(notExistsFn)\n }\n\n if (input.hasActions !== undefined) {\n q = input.hasActions\n ? q.where('m.action_data', 'is not', null)\n : q.where('m.action_data', 'is', null)\n }\n\n return q\n }\n\n const countResult = await buildBaseQuery()\n .select(sql<number>`count(*)`.as('count'))\n .executeTakeFirst() as { count: string | number } | undefined\n const total = Number(countResult?.count ?? 0)\n\n const offset = (input.page - 1) * input.pageSize\n const scopeRows = await buildBaseQuery()\n .select([\n 'm.id',\n 'm.sender_user_id',\n 'm.is_draft',\n 'r.status as recipient_status',\n 'r.read_at',\n ])\n .orderBy('m.sent_at', 'desc')\n .offset(offset)\n .limit(input.pageSize)\n .execute()\n\n const typedRows = scopeRows as MessageListScopeRow[]\n const messageIds = typedRows.map((row) => row.id)\n\n const messageEntities = messageIds.length > 0\n ? await findWithDecryption(\n em,\n Message,\n { id: { $in: messageIds } },\n undefined,\n { tenantId: scope.tenantId, organizationId: scope.organizationId }\n )\n : []\n\n const messagesById = new Map<string, Message>()\n for (const message of messageEntities) {\n messagesById.set(message.id, message)\n }\n\n const objects = messageIds.length > 0\n ? await em.find(MessageObject, { messageId: { $in: messageIds } })\n : []\n\n const objectsByMessage = objects.reduce((acc, obj) => {\n if (!acc[obj.messageId]) acc[obj.messageId] = []\n acc[obj.messageId].push(obj)\n return acc\n }, {} as Record<string, MessageObject[]>)\n\n const attachmentCounts: AttachmentCountRow[] = messageIds.length > 0\n ? await (getDb(em) as any)\n .selectFrom('attachments')\n .select(['record_id', sql<string>`count(*)`.as('count')])\n .where('entity_id', '=', MESSAGE_ATTACHMENT_ENTITY_ID)\n .where('record_id', 'in', messageIds)\n .groupBy('record_id')\n .execute()\n : []\n\n const attachmentCountByMessage = attachmentCounts.reduce((acc: Record<string, number>, row) => {\n acc[row.record_id] = Number(row.count)\n return acc\n }, {})\n\n const recipientCounts: RecipientCountRow[] = messageIds.length > 0\n ? await (getDb(em) as any)\n .selectFrom('message_recipients')\n .select(['message_id', sql<string>`count(*)`.as('count')])\n .where('message_id', 'in', messageIds)\n .where('deleted_at', 'is', null)\n .groupBy('message_id')\n .execute()\n : []\n\n const recipientCountByMessage = recipientCounts.reduce((acc: Record<string, number>, row) => {\n acc[row.message_id] = Number(row.count)\n return acc\n }, {})\n\n const senderUserIds = Array.from(new Set(typedRows.map((row) => row.sender_user_id).filter(Boolean)))\n const senderUsers = senderUserIds.length > 0\n ? await findWithDecryption(\n em,\n User,\n { id: { $in: senderUserIds } },\n undefined,\n { tenantId: scope.tenantId, organizationId: scope.organizationId }\n )\n : []\n\n const senderMetaById = new Map<string, { name: string | null; email: string | null }>()\n senderUsers.forEach((user) => {\n const name = typeof user.name === 'string' && user.name.trim().length ? user.name.trim() : null\n senderMetaById.set(user.id, { name, email: user.email ?? null })\n })\n\n return Response.json({\n items: typedRows\n .map((row) => {\n const message = messagesById.get(row.id)\n if (!message) return null\n const body = typeof message.body === 'string' ? message.body : ''\n const bodyPreview = body.substring(0, 150) + (body.length > 150 ? '...' : '')\n const actionData = message.actionData ?? null\n return {\n ...(senderMetaById.get(row.sender_user_id)\n ? {\n senderName: senderMetaById.get(row.sender_user_id)?.name ?? null,\n senderEmail: senderMetaById.get(row.sender_user_id)?.email ?? null,\n }\n : { senderName: null, senderEmail: null }),\n id: message.id,\n type: message.type,\n visibility: message.visibility ?? null,\n sourceEntityType: message.sourceEntityType ?? null,\n sourceEntityId: message.sourceEntityId ?? null,\n externalEmail: message.externalEmail ?? null,\n externalName: message.externalName ?? null,\n subject: message.subject,\n bodyPreview,\n senderUserId: message.senderUserId,\n priority: message.priority,\n status: row.recipient_status ?? (row.is_draft ? 'draft' : 'sent'),\n hasObjects: (objectsByMessage[message.id] || []).length > 0,\n objectCount: (objectsByMessage[message.id] || []).length,\n hasAttachments: (attachmentCountByMessage[message.id] || 0) > 0,\n attachmentCount: attachmentCountByMessage[message.id] || 0,\n recipientCount: recipientCountByMessage[message.id] || 0,\n hasActions:\n Boolean(actionData?.actions?.length)\n || Boolean(getMessageType(message.type)?.defaultActions?.length)\n || (objectsByMessage[message.id] || []).some((item) => item.actionRequired && Boolean(item.actionType)),\n actionTaken: message.actionTaken ?? null,\n sentAt: message.sentAt ? message.sentAt.toISOString() : null,\n readAt: row.read_at,\n threadId: message.threadId ?? null,\n }\n })\n .filter((item): item is NonNullable<typeof item> => item !== null),\n page: input.page,\n pageSize: input.pageSize,\n total,\n totalPages: Math.ceil(total / input.pageSize),\n })\n}\n\nexport async function POST(req: Request) {\n const { ctx, scope } = await resolveMessageContext(req)\n const commandBus = ctx.container.resolve('commandBus') as CommandBus\n const body = await req.json().catch(() => ({}))\n const input = composeMessageSchema.parse(body)\n\n const isPublicVisibility = input.visibility === 'public'\n const sendViaEmail = isPublicVisibility ? true : input.sendViaEmail\n if (sendViaEmail && !(await canUseMessageEmailFeature(ctx, scope))) {\n return Response.json({ error: 'Missing feature: messages.email' }, { status: 403 })\n }\n\n if (input.objects?.length) {\n const objectValidationError = validateMessageObjectsForType(input.type, input.objects)\n if (objectValidationError) {\n return Response.json({ error: objectValidationError }, { status: 400 })\n }\n }\n\n const { result, logEntry } = await commandBus.execute('messages.messages.compose', {\n input: {\n ...input,\n sendViaEmail,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n userId: scope.userId,\n },\n ctx: {\n container: ctx.container,\n auth: ctx.auth ?? null,\n organizationScope: null,\n selectedOrganizationId: scope.organizationId,\n organizationIds: scope.organizationId ? [scope.organizationId] : null,\n request: req,\n },\n })\n const { id: messageId, threadId: responseThreadId } = result as unknown as MessageCommandExecuteResultWithThreadId\n\n const response = Response.json({ id: messageId, threadId: responseThreadId }, { status: 201 })\n attachOperationMetadataHeader(response, logEntry, {\n resourceKind: 'messages.message',\n resourceId: messageId,\n })\n return response\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n GET: {\n summary: 'List messages',\n query: listSchema,\n responses: [\n {\n status: 200,\n description: 'Message list',\n schema: z.object({\n items: z.array(messageListItemSchema),\n page: z.number(),\n pageSize: z.number(),\n total: z.number(),\n totalPages: z.number(),\n }),\n },\n ],\n },\n POST: {\n summary: 'Compose a message',\n requestBody: {\n schema: composeSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Message created',\n schema: composeResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
1
2
|
import { resolveMessageContext } from "../../lib/routeHelpers.js";
|
|
2
3
|
import { unreadCountResponseSchema } from "../openapi.js";
|
|
3
4
|
const metadata = {
|
|
4
5
|
GET: { requireAuth: true, requireFeatures: ["messages.view"] }
|
|
5
6
|
};
|
|
6
|
-
function
|
|
7
|
-
return em.
|
|
7
|
+
function getDb(em) {
|
|
8
|
+
return em.getKysely();
|
|
8
9
|
}
|
|
9
10
|
async function GET(req) {
|
|
10
11
|
const { ctx, scope } = await resolveMessageContext(req);
|
|
11
12
|
const em = ctx.container.resolve("em");
|
|
12
|
-
const
|
|
13
|
-
let query =
|
|
13
|
+
const db = getDb(em);
|
|
14
|
+
let query = db.selectFrom("message_recipients as r").innerJoin("messages as m", "m.id", "r.message_id").where("r.recipient_user_id", "=", scope.userId).where("r.status", "=", "unread").where("r.deleted_at", "is", null).where("r.archived_at", "is", null).where("m.tenant_id", "=", scope.tenantId).where("m.deleted_at", "is", null);
|
|
14
15
|
if (scope.organizationId) {
|
|
15
|
-
query = query.where("m.organization_id", scope.organizationId);
|
|
16
|
+
query = query.where("m.organization_id", "=", scope.organizationId);
|
|
16
17
|
} else {
|
|
17
|
-
query = query.
|
|
18
|
+
query = query.where("m.organization_id", "is", null);
|
|
18
19
|
}
|
|
19
|
-
const row = await query.count(
|
|
20
|
+
const row = await query.select(sql`count(*)`.as("count")).executeTakeFirst();
|
|
20
21
|
const count = Number(row?.count ?? 0);
|
|
21
22
|
return Response.json({ unreadCount: count });
|
|
22
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/messages/api/unread-count/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi/types'\nimport { resolveMessageContext } from '../../lib/routeHelpers'\nimport { unreadCountResponseSchema } from '../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['messages.view'] },\n}\n\nfunction getDb(em: EntityManager): Kysely<any> {\n return em.getKysely<any>()\n}\n\nexport async function GET(req: Request) {\n const { ctx, scope } = await resolveMessageContext(req)\n const em = ctx.container.resolve('em') as EntityManager\n const db = getDb(em) as any\n\n let query = db\n .selectFrom('message_recipients as r')\n .innerJoin('messages as m', 'm.id', 'r.message_id')\n .where('r.recipient_user_id', '=', scope.userId)\n .where('r.status', '=', 'unread')\n .where('r.deleted_at', 'is', null)\n .where('r.archived_at', 'is', null)\n .where('m.tenant_id', '=', scope.tenantId)\n .where('m.deleted_at', 'is', null)\n\n if (scope.organizationId) {\n query = query.where('m.organization_id', '=', scope.organizationId)\n } else {\n query = query.where('m.organization_id', 'is', null)\n }\n\n const row = await query\n .select(sql<number>`count(*)`.as('count'))\n .executeTakeFirst() as { count: string | number } | undefined\n const count = Number(row?.count ?? 0)\n\n return Response.json({ unreadCount: count })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Messages',\n methods: {\n GET: {\n summary: 'Get unread message count',\n responses: [\n {\n status: 200,\n description: 'Unread count',\n schema: unreadCountResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAsB,WAAW;AAEjC,SAAS,6BAA6B;AACtC,SAAS,iCAAiC;AAEnC,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAC/D;AAEA,SAAS,MAAM,IAAgC;AAC7C,SAAO,GAAG,UAAe;AAC3B;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,EAAE,KAAK,MAAM,IAAI,MAAM,sBAAsB,GAAG;AACtD,QAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,QAAM,KAAK,MAAM,EAAE;AAEnB,MAAI,QAAQ,GACT,WAAW,yBAAyB,EACpC,UAAU,iBAAiB,QAAQ,cAAc,EACjD,MAAM,uBAAuB,KAAK,MAAM,MAAM,EAC9C,MAAM,YAAY,KAAK,QAAQ,EAC/B,MAAM,gBAAgB,MAAM,IAAI,EAChC,MAAM,iBAAiB,MAAM,IAAI,EACjC,MAAM,eAAe,KAAK,MAAM,QAAQ,EACxC,MAAM,gBAAgB,MAAM,IAAI;AAEnC,MAAI,MAAM,gBAAgB;AACxB,YAAQ,MAAM,MAAM,qBAAqB,KAAK,MAAM,cAAc;AAAA,EACpE,OAAO;AACL,YAAQ,MAAM,MAAM,qBAAqB,MAAM,IAAI;AAAA,EACrD;AAEA,QAAM,MAAM,MAAM,MACf,OAAO,cAAsB,GAAG,OAAO,CAAC,EACxC,iBAAiB;AACpB,QAAM,QAAQ,OAAO,KAAK,SAAS,CAAC;AAEpC,SAAO,SAAS,KAAK,EAAE,aAAa,MAAM,CAAC;AAC7C;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -71,7 +71,7 @@ const confirmMessageCommand = {
|
|
|
71
71
|
confirmation.confirmed = input.confirmed;
|
|
72
72
|
confirmation.confirmedByUserId = actorUserId;
|
|
73
73
|
confirmation.confirmedAt = input.confirmed ? /* @__PURE__ */ new Date() : null;
|
|
74
|
-
await em.
|
|
74
|
+
await em.persist(confirmation).flush();
|
|
75
75
|
return {
|
|
76
76
|
messageId: confirmation.messageId,
|
|
77
77
|
confirmed: confirmation.confirmed,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/messages/commands/confirmations.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { Message, MessageConfirmation, MessageRecipient } from '../data/entities'\nimport { confirmMessageSchema } from '../data/validators'\n\ntype ConfirmMessageResult = {\n messageId: string\n confirmed: boolean\n confirmedAt: string | null\n confirmedByUserId: string | null\n}\n\ntype ConfirmationSnapshot = {\n id: string | null\n messageId: string\n confirmed: boolean\n confirmedAt: string | null\n confirmedByUserId: string | null\n tenantId: string\n organizationId: string | null\n}\n\nfunction toIso(value: Date | null | undefined): string | null {\n return value ? value.toISOString() : null\n}\n\nfunction toDate(value: string | null | undefined): Date | null {\n if (!value) return null\n return new Date(value)\n}\n\nconst confirmMessageCommand: CommandHandler<unknown, ConfirmMessageResult> = {\n id: 'messages.confirmations.confirm',\n async prepare(rawInput, ctx) {\n const input = confirmMessageSchema.parse(rawInput)\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n if (!tenantId) throw new Error('Tenant scope is required')\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(MessageConfirmation, { messageId: input.messageId })\n return {\n before: {\n id: existing?.id ?? null,\n messageId: input.messageId,\n confirmed: existing?.confirmed ?? false,\n confirmedAt: toIso(existing?.confirmedAt),\n confirmedByUserId: existing?.confirmedByUserId ?? null,\n tenantId,\n organizationId,\n } satisfies ConfirmationSnapshot,\n }\n },\n async execute(rawInput, ctx) {\n const input = confirmMessageSchema.parse(rawInput)\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n\n if (!tenantId) {\n throw new Error('Tenant scope is required')\n }\n\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const message = await em.findOne(Message, {\n id: input.messageId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n if (!message) {\n throw new Error('Message not found')\n }\n\n const actorUserId = ctx.auth?.sub ?? null\n if (!actorUserId) {\n throw new Error('Authentication required')\n }\n\n const recipient = await em.findOne(MessageRecipient, {\n messageId: message.id,\n recipientUserId: actorUserId,\n deletedAt: null,\n })\n const isSender = message.senderUserId === actorUserId\n if (!isSender && !recipient) {\n throw new Error('Access denied')\n }\n\n let confirmation = await em.findOne(MessageConfirmation, { messageId: message.id })\n if (!confirmation) {\n confirmation = em.create(MessageConfirmation, {\n messageId: message.id,\n tenantId,\n organizationId,\n })\n }\n\n confirmation.confirmed = input.confirmed\n confirmation.confirmedByUserId = actorUserId\n confirmation.confirmedAt = input.confirmed ? new Date() : null\n\n await em.
|
|
5
|
-
"mappings": "AACA,SAAS,uBAAuB;AAEhC,SAAS,0BAA4C;AACrD,SAAS,SAAS,qBAAqB,wBAAwB;AAC/D,SAAS,4BAA4B;AAmBrC,SAAS,MAAM,OAA+C;AAC5D,SAAO,QAAQ,MAAM,YAAY,IAAI;AACvC;AAEA,SAAS,OAAO,OAA+C;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,IAAI,KAAK,KAAK;AACvB;AAEA,MAAM,wBAAuE;AAAA,EAC3E,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,qBAAqB,MAAM,QAAQ;AACjD,UAAM,WAAW,MAAM,YAAY,IAAI,MAAM,YAAY;AACzD,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,0BAA0B;AACzD,UAAM,iBAAiB,MAAM,kBAAkB,IAAI,0BAA0B;AAC7E,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,GAAG,QAAQ,qBAAqB,EAAE,WAAW,MAAM,UAAU,CAAC;AACrF,WAAO;AAAA,MACL,QAAQ;AAAA,QACN,IAAI,UAAU,MAAM;AAAA,QACpB,WAAW,MAAM;AAAA,QACjB,WAAW,UAAU,aAAa;AAAA,QAClC,aAAa,MAAM,UAAU,WAAW;AAAA,QACxC,mBAAmB,UAAU,qBAAqB;AAAA,QAClD;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,qBAAqB,MAAM,QAAQ;AACjD,UAAM,WAAW,MAAM,YAAY,IAAI,MAAM,YAAY;AAEzD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,iBAAiB,MAAM,kBAAkB,IAAI,0BAA0B;AAC7E,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAE/D,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS;AAAA,MACxC,IAAI,MAAM;AAAA,MACV;AAAA,MACA;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAED,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAEA,UAAM,cAAc,IAAI,MAAM,OAAO;AACrC,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,UAAM,YAAY,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACnD,WAAW,QAAQ;AAAA,MACnB,iBAAiB;AAAA,MACjB,WAAW;AAAA,IACb,CAAC;AACD,UAAM,WAAW,QAAQ,iBAAiB;AAC1C,QAAI,CAAC,YAAY,CAAC,WAAW;AAC3B,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAEA,QAAI,eAAe,MAAM,GAAG,QAAQ,qBAAqB,EAAE,WAAW,QAAQ,GAAG,CAAC;AAClF,QAAI,CAAC,cAAc;AACjB,qBAAe,GAAG,OAAO,qBAAqB;AAAA,QAC5C,WAAW,QAAQ;AAAA,QACnB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,iBAAa,YAAY,MAAM;AAC/B,iBAAa,oBAAoB;AACjC,iBAAa,cAAc,MAAM,YAAY,oBAAI,KAAK,IAAI;AAE1D,UAAM,GAAG,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { Message, MessageConfirmation, MessageRecipient } from '../data/entities'\nimport { confirmMessageSchema } from '../data/validators'\n\ntype ConfirmMessageResult = {\n messageId: string\n confirmed: boolean\n confirmedAt: string | null\n confirmedByUserId: string | null\n}\n\ntype ConfirmationSnapshot = {\n id: string | null\n messageId: string\n confirmed: boolean\n confirmedAt: string | null\n confirmedByUserId: string | null\n tenantId: string\n organizationId: string | null\n}\n\nfunction toIso(value: Date | null | undefined): string | null {\n return value ? value.toISOString() : null\n}\n\nfunction toDate(value: string | null | undefined): Date | null {\n if (!value) return null\n return new Date(value)\n}\n\nconst confirmMessageCommand: CommandHandler<unknown, ConfirmMessageResult> = {\n id: 'messages.confirmations.confirm',\n async prepare(rawInput, ctx) {\n const input = confirmMessageSchema.parse(rawInput)\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n if (!tenantId) throw new Error('Tenant scope is required')\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(MessageConfirmation, { messageId: input.messageId })\n return {\n before: {\n id: existing?.id ?? null,\n messageId: input.messageId,\n confirmed: existing?.confirmed ?? false,\n confirmedAt: toIso(existing?.confirmedAt),\n confirmedByUserId: existing?.confirmedByUserId ?? null,\n tenantId,\n organizationId,\n } satisfies ConfirmationSnapshot,\n }\n },\n async execute(rawInput, ctx) {\n const input = confirmMessageSchema.parse(rawInput)\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n\n if (!tenantId) {\n throw new Error('Tenant scope is required')\n }\n\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const message = await em.findOne(Message, {\n id: input.messageId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n if (!message) {\n throw new Error('Message not found')\n }\n\n const actorUserId = ctx.auth?.sub ?? null\n if (!actorUserId) {\n throw new Error('Authentication required')\n }\n\n const recipient = await em.findOne(MessageRecipient, {\n messageId: message.id,\n recipientUserId: actorUserId,\n deletedAt: null,\n })\n const isSender = message.senderUserId === actorUserId\n if (!isSender && !recipient) {\n throw new Error('Access denied')\n }\n\n let confirmation = await em.findOne(MessageConfirmation, { messageId: message.id })\n if (!confirmation) {\n confirmation = em.create(MessageConfirmation, {\n messageId: message.id,\n tenantId,\n organizationId,\n })\n }\n\n confirmation.confirmed = input.confirmed\n confirmation.confirmedByUserId = actorUserId\n confirmation.confirmedAt = input.confirmed ? new Date() : null\n\n await em.persist(confirmation).flush()\n\n return {\n messageId: confirmation.messageId,\n confirmed: confirmation.confirmed,\n confirmedAt: confirmation.confirmedAt ? confirmation.confirmedAt.toISOString() : null,\n confirmedByUserId: confirmation.confirmedByUserId ?? null,\n }\n },\n async captureAfter(rawInput, _result, ctx) {\n const input = confirmMessageSchema.parse(rawInput)\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n if (!tenantId) return null\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const confirmation = await em.findOne(MessageConfirmation, { messageId: input.messageId })\n return {\n id: confirmation?.id ?? null,\n messageId: input.messageId,\n confirmed: confirmation?.confirmed ?? false,\n confirmedAt: toIso(confirmation?.confirmedAt),\n confirmedByUserId: confirmation?.confirmedByUserId ?? null,\n tenantId,\n organizationId,\n } satisfies ConfirmationSnapshot\n },\n async buildLog({ input, snapshots }) {\n const parsed = confirmMessageSchema.parse(input)\n return {\n actionLabel: parsed.confirmed ? 'Confirm message' : 'Unconfirm message',\n resourceKind: 'messages.message',\n resourceId: parsed.messageId,\n tenantId: parsed.tenantId ?? null,\n organizationId: parsed.organizationId ?? null,\n payload: {\n undo: {\n before: (snapshots.before as ConfirmationSnapshot | undefined) ?? null,\n after: (snapshots.after as ConfirmationSnapshot | undefined) ?? null,\n } satisfies UndoPayload<ConfirmationSnapshot>,\n },\n snapshotBefore: snapshots.before ?? null,\n snapshotAfter: snapshots.after ?? null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<UndoPayload<ConfirmationSnapshot>>(logEntry)\n const before = undo?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(MessageConfirmation, { messageId: before.messageId })\n if (!before.id) {\n if (existing) {\n em.remove(existing)\n await em.flush()\n }\n return\n }\n if (!existing) {\n em.persist(em.create(MessageConfirmation, {\n id: before.id,\n messageId: before.messageId,\n confirmed: before.confirmed,\n confirmedAt: toDate(before.confirmedAt),\n confirmedByUserId: before.confirmedByUserId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n }))\n } else {\n existing.confirmed = before.confirmed\n existing.confirmedAt = toDate(before.confirmedAt)\n existing.confirmedByUserId = before.confirmedByUserId\n existing.tenantId = before.tenantId\n existing.organizationId = before.organizationId\n }\n await em.flush()\n },\n}\n\nregisterCommand(confirmMessageCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -161,7 +161,7 @@ const composeMessageCommand = {
|
|
|
161
161
|
tenantId: input.tenantId,
|
|
162
162
|
organizationId: input.organizationId
|
|
163
163
|
});
|
|
164
|
-
await trx.
|
|
164
|
+
await trx.persist(message).flush();
|
|
165
165
|
if (!threadId && !input.isDraft && !message.threadId) {
|
|
166
166
|
message.threadId = message.id;
|
|
167
167
|
await trx.flush();
|
|
@@ -468,7 +468,7 @@ const replyMessageCommand = {
|
|
|
468
468
|
tenantId: input.tenantId,
|
|
469
469
|
organizationId: input.organizationId
|
|
470
470
|
});
|
|
471
|
-
await trx.
|
|
471
|
+
await trx.persist(message).flush();
|
|
472
472
|
for (const recipientUserId of recipientIds) {
|
|
473
473
|
trx.persist(trx.create(MessageRecipient, {
|
|
474
474
|
messageId: message.id,
|
|
@@ -603,7 +603,7 @@ const forwardMessageCommand = {
|
|
|
603
603
|
tenantId: input.tenantId,
|
|
604
604
|
organizationId: input.organizationId
|
|
605
605
|
});
|
|
606
|
-
await trx.
|
|
606
|
+
await trx.persist(newMessage).flush();
|
|
607
607
|
for (const recipient of input.recipients) {
|
|
608
608
|
trx.persist(trx.create(MessageRecipient, {
|
|
609
609
|
messageId: newMessage.id,
|