@open-mercato/core 0.7.0 → 0.7.1-develop.7102.1.b41f7e3e51
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 +1 -1
- package/dist/helpers/integration/api.js +13 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +19 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/api_keys/api/keys/route.js +1 -1
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +3 -1
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/api_keys/di.js +9 -0
- package/dist/modules/api_keys/di.js.map +7 -0
- package/dist/modules/api_keys/services/principalService.js +31 -0
- package/dist/modules/api_keys/services/principalService.js.map +7 -0
- package/dist/modules/attachments/api/route.js +2 -2
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +2 -0
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/di.js +11 -1
- package/dist/modules/attachments/di.js.map +2 -2
- package/dist/modules/attachments/index.js.map +1 -1
- package/dist/modules/attachments/lib/attachment-service.js +313 -0
- package/dist/modules/attachments/lib/attachment-service.js.map +7 -0
- package/dist/modules/attachments/lib/scoped-upload-service.js +15 -2
- package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
- package/dist/modules/attachments/lib/upload-limits.js +6 -2
- package/dist/modules/attachments/lib/upload-limits.js.map +2 -2
- package/dist/modules/audit_logs/backend/audit-logs/page.js +6 -0
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +19 -11
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +57 -12
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/acl.js +30 -5
- package/dist/modules/auth/acl.js.map +2 -2
- package/dist/modules/auth/api/feature-check.js +8 -2
- package/dist/modules/auth/api/feature-check.js.map +2 -2
- package/dist/modules/auth/api/login.js +4 -1
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/reset/validate.js +55 -0
- package/dist/modules/auth/api/reset/validate.js.map +7 -0
- package/dist/modules/auth/api/roles/acl/route.js +28 -33
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/session/refresh.js +25 -1
- package/dist/modules/auth/api/session/refresh.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +96 -51
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +13 -5
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +5 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +3 -1
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +5 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +2 -1
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/commands/acl.js +254 -0
- package/dist/modules/auth/commands/acl.js.map +7 -0
- package/dist/modules/auth/components/AclEditor.js +39 -30
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/data/validators.js +5 -1
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/di.js +35 -9
- package/dist/modules/auth/di.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +46 -0
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/auth/lib/userIdFilter.js +16 -0
- package/dist/modules/auth/lib/userIdFilter.js.map +7 -0
- package/dist/modules/auth/services/authService.js +16 -2
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/principalService.js +200 -0
- package/dist/modules/auth/services/principalService.js.map +7 -0
- package/dist/modules/auth/services/rbacService.js +67 -24
- package/dist/modules/auth/services/rbacService.js.map +3 -3
- package/dist/modules/auth/services/roleOrganizationScope.js +41 -0
- package/dist/modules/auth/services/roleOrganizationScope.js.map +7 -0
- package/dist/modules/business_rules/backend/logs/page.js +3 -1
- package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +3 -1
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +3 -1
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +9 -9
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-evaluator.js +10 -9
- package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
- package/dist/modules/catalog/ai-agents.js +1 -1
- package/dist/modules/catalog/ai-agents.js.map +1 -1
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +5 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -0
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +7 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +9 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +95 -10
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +3 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +14 -1
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/di.js +7 -1
- package/dist/modules/communication_channels/di.js.map +2 -2
- package/dist/modules/communication_channels/lib/email-capabilities.js +4 -1
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +2 -2
- package/dist/modules/communication_channels/lib/outbound-recipient.js +33 -0
- package/dist/modules/communication_channels/lib/outbound-recipient.js.map +7 -0
- package/dist/modules/communication_channels/lib/resolve-channel-type.js +51 -0
- package/dist/modules/communication_channels/lib/resolve-channel-type.js.map +7 -0
- package/dist/modules/communication_channels/lib/test-seed.js +41 -2
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +9 -0
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +3 -1
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +3 -1
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/services/rateFetchingService.js +6 -1
- package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +9 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
- package/dist/modules/customer_accounts/services/domainMappingService.js +10 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
- package/dist/modules/customers/ai-tools/companies-pack.js +4 -1
- package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +13 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deals-pack.js +52 -6
- package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/people-pack.js +4 -1
- package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +48 -6
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +20 -64
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +17 -94
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +8 -5
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +8 -25
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/stats/route.js +3 -22
- package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
- package/dist/modules/customers/api/deals/aggregate/route.js +11 -4
- package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
- package/dist/modules/customers/api/deals/map/route.js +2 -1
- package/dist/modules/customers/api/deals/map/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +38 -1
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +4 -4
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +3 -1
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +27 -4
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +11 -10
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/check-phone/route.js +51 -9
- package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +30 -5
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +4 -2
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +6 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +5 -2
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +4 -13
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +85 -14
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js +18 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js.map +7 -0
- package/dist/modules/customers/backend/customers/people/page.js +4 -2
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js +9 -3
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +21 -34
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js.map +1 -1
- package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +7 -6
- package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
- package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js +3 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/types.js +1 -1
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +5 -4
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +19 -7
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +6 -5
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +35 -11
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +19 -13
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/detail/utils.js +5 -5
- package/dist/modules/customers/components/detail/utils.js.map +2 -2
- package/dist/modules/customers/data/entities.js.map +1 -1
- package/dist/modules/customers/data/validators.js +32 -3
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +5 -0
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
- package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
- package/dist/modules/customers/lib/closureStage.js +45 -0
- package/dist/modules/customers/lib/closureStage.js.map +7 -0
- package/dist/modules/customers/lib/dealStatus.js +30 -0
- package/dist/modules/customers/lib/dealStatus.js.map +2 -2
- package/dist/modules/customers/lib/detailReadAccess.js +12 -0
- package/dist/modules/customers/lib/detailReadAccess.js.map +7 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js +1 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +100 -2
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +12 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +6 -1
- package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +3 -1
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/abandoned-run.js +19 -0
- package/dist/modules/data_sync/lib/abandoned-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +72 -6
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-export.js +5 -1
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +5 -1
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/devices/acl.js +9 -1
- package/dist/modules/devices/acl.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/[id]/route.js +5 -32
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/route.js +14 -1
- package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
- package/dist/modules/devices/api/deviceList.js +99 -12
- package/dist/modules/devices/api/deviceList.js.map +2 -2
- package/dist/modules/devices/api/deviceSerialization.js +46 -0
- package/dist/modules/devices/api/deviceSerialization.js.map +7 -0
- package/dist/modules/devices/api/openapi.js +3 -1
- package/dist/modules/devices/api/openapi.js.map +2 -2
- package/dist/modules/devices/api/route.js +9 -1
- package/dist/modules/devices/api/route.js.map +2 -2
- package/dist/modules/devices/backend/devices/[id]/page.js +10 -26
- package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/create/page.js +17 -1
- package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/page.js +28 -34
- package/dist/modules/devices/backend/devices/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +34 -0
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +7 -0
- package/dist/modules/devices/backend/devices/userOptions.js +52 -0
- package/dist/modules/devices/backend/devices/userOptions.js.map +7 -0
- package/dist/modules/devices/setup.js +6 -2
- package/dist/modules/devices/setup.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/directory/di.js +9 -0
- package/dist/modules/directory/di.js.map +2 -2
- package/dist/modules/directory/services/organizationHierarchyService.js +29 -0
- package/dist/modules/directory/services/organizationHierarchyService.js.map +7 -0
- package/dist/modules/directory/services/organizationScopeService.js +56 -0
- package/dist/modules/directory/services/organizationScopeService.js.map +7 -0
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +4 -2
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +3 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/eudr/api/plots/route.js +5 -1
- package/dist/modules/eudr/api/plots/route.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
- package/dist/modules/eudr/commands/statements.js +0 -3
- package/dist/modules/eudr/commands/statements.js.map +2 -2
- package/dist/modules/eudr/data/validators.js +4 -1
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +1 -0
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +1 -0
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/feature_toggles/defaults.json +0 -8
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +3 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js +3 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js +1 -12
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +6 -1
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +38 -29
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +36 -9
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js +55 -0
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js.map +7 -0
- package/dist/modules/integrations/data/validators.js +11 -3
- package/dist/modules/integrations/data/validators.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-masking.js +6 -2
- package/dist/modules/integrations/lib/credentials-masking.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +29 -1
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/openapi.js +17 -2
- package/dist/modules/messages/api/openapi.js.map +2 -2
- package/dist/modules/messages/api/route.js +17 -2
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +7 -14
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/components/inboxFilters.js +15 -0
- package/dist/modules/messages/components/inboxFilters.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +11 -1
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +3 -1
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js +2 -1
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
- package/dist/modules/messages/components/messageListLabels.js +14 -2
- package/dist/modules/messages/components/messageListLabels.js.map +2 -2
- package/dist/modules/messages/data/validators.js +18 -4
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/messages/lib/channel-sender-identity.js +26 -0
- package/dist/modules/messages/lib/channel-sender-identity.js.map +7 -0
- package/dist/modules/messages/lib/composeSourceChannelType.js +41 -0
- package/dist/modules/messages/lib/composeSourceChannelType.js.map +7 -0
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +4 -6
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -1
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/payment_gateways/lib/webhook-processor.js +7 -0
- package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -0
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/page.js +3 -1
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +1 -1
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +150 -51
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/search-entity-policy.js +14 -0
- package/dist/modules/query_index/lib/search-entity-policy.js.map +7 -0
- package/dist/modules/query_index/lib/search-tokens.js +79 -3
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +3 -1
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +3 -1
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +3 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +3 -0
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +10 -8
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +79 -1
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/PriceWithCurrency.js +7 -5
- package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +3 -0
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +51 -22
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +83 -18
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +23 -9
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +8 -6
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +68 -14
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +6 -5
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +5 -4
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js +54 -0
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js.map +7 -0
- package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/sales/data/entities.js +8 -2
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/extension-points.js +1 -0
- package/dist/modules/sales/extension-points.js.map +2 -2
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js +33 -0
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js.map +7 -0
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js +32 -0
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js.map +7 -0
- package/dist/modules/sales/setup.js +2 -0
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +10 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +2 -2
- package/dist/modules/sales/widgets/injection-table.js +4 -7
- package/dist/modules/sales/widgets/injection-table.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -0
- package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -0
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +3 -0
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +3 -0
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -0
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +3 -0
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.js +29 -18
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +3 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/warranty_claims/api/assignees/route.js +3 -3
- package/dist/modules/warranty_claims/api/assignees/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +12 -9
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +3 -0
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -0
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/wms/api/warehouseSearch.js +21 -0
- package/dist/modules/wms/api/warehouseSearch.js.map +7 -0
- package/dist/modules/wms/api/warehouses/route.js +13 -9
- package/dist/modules/wms/api/warehouses/route.js.map +2 -2
- package/dist/modules/wms/api/zones/route.js +15 -2
- package/dist/modules/wms/api/zones/route.js.map +2 -2
- package/dist/modules/wms/commands/configuration.js +121 -9
- package/dist/modules/wms/commands/configuration.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +3 -0
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/dist/modules/wms/commands/shared.js +9 -1
- package/dist/modules/wms/commands/shared.js.map +2 -2
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js +158 -0
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js +76 -111
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +3 -3
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +1 -0
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotsListPage.js +1 -0
- package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +27 -20
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
- package/dist/modules/wms/components/backend/warehouseFormOptions.js +66 -0
- package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/page.js +3 -1
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/page.js +3 -1
- package/dist/modules/workflows/backend/events/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +3 -1
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +3 -1
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +4 -0
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/package.json +9 -10
- package/src/helpers/integration/api.ts +33 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +69 -1
- package/src/modules/api_keys/api/keys/route.ts +3 -1
- package/src/modules/api_keys/backend/api-keys/page.tsx +4 -1
- package/src/modules/api_keys/di.ts +7 -0
- package/src/modules/api_keys/services/principalService.ts +33 -0
- package/src/modules/attachments/AGENTS.md +15 -0
- package/src/modules/attachments/api/route.ts +2 -2
- package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -0
- package/src/modules/attachments/di.ts +16 -0
- package/src/modules/attachments/i18n/de.json +5 -0
- package/src/modules/attachments/i18n/en.json +5 -0
- package/src/modules/attachments/i18n/es.json +5 -0
- package/src/modules/attachments/i18n/ko.json +5 -0
- package/src/modules/attachments/i18n/pl.json +5 -0
- package/src/modules/attachments/index.ts +10 -0
- package/src/modules/attachments/lib/attachment-service.ts +468 -0
- package/src/modules/attachments/lib/scoped-upload-service.ts +46 -0
- package/src/modules/attachments/lib/upload-limits.ts +6 -2
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +8 -0
- package/src/modules/audit_logs/lib/display-helpers.tsx +42 -32
- package/src/modules/audit_logs/services/actionLogService.ts +74 -11
- package/src/modules/auth/acl.ts +30 -6
- package/src/modules/auth/api/feature-check.ts +10 -3
- package/src/modules/auth/api/login.ts +9 -1
- package/src/modules/auth/api/reset/validate.ts +57 -0
- package/src/modules/auth/api/roles/acl/route.ts +42 -39
- package/src/modules/auth/api/session/refresh.ts +32 -1
- package/src/modules/auth/api/users/acl/route.ts +177 -53
- package/src/modules/auth/api/users/route.ts +18 -5
- package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +5 -2
- package/src/modules/auth/backend/roles/page.tsx +4 -2
- package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +5 -2
- package/src/modules/auth/backend/users/page.tsx +3 -2
- package/src/modules/auth/commands/acl.ts +495 -0
- package/src/modules/auth/components/AclEditor.tsx +63 -33
- package/src/modules/auth/data/entities.ts +4 -1
- package/src/modules/auth/data/validators.ts +5 -0
- package/src/modules/auth/di.ts +46 -16
- package/src/modules/auth/frontend/reset/[token]/page.tsx +74 -0
- package/src/modules/auth/i18n/de.json +389 -0
- package/src/modules/auth/i18n/en.json +389 -0
- package/src/modules/auth/i18n/es.json +389 -0
- package/src/modules/auth/i18n/ko.json +389 -0
- package/src/modules/auth/i18n/pl.json +391 -2
- package/src/modules/auth/lib/grantChecks.ts +6 -0
- package/src/modules/auth/lib/userIdFilter.ts +31 -0
- package/src/modules/auth/services/authService.ts +18 -2
- package/src/modules/auth/services/principalService.ts +266 -0
- package/src/modules/auth/services/rbacService.ts +110 -24
- package/src/modules/auth/services/roleOrganizationScope.ts +70 -0
- package/src/modules/business_rules/backend/logs/page.tsx +4 -1
- package/src/modules/business_rules/backend/rules/page.tsx +4 -1
- package/src/modules/business_rules/backend/sets/page.tsx +4 -1
- package/src/modules/business_rules/lib/expression-evaluator.ts +9 -9
- package/src/modules/business_rules/lib/rule-evaluator.ts +10 -9
- package/src/modules/catalog/ai-agents.ts +1 -1
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +12 -5
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +3 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +7 -0
- package/src/modules/catalog/i18n/de.json +1 -0
- package/src/modules/catalog/i18n/en.json +1 -0
- package/src/modules/catalog/i18n/es.json +1 -0
- package/src/modules/catalog/i18n/ko.json +1 -0
- package/src/modules/catalog/i18n/pl.json +1 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -0
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +17 -1
- package/src/modules/communication_channels/api/post/test-seed/route.ts +155 -16
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +4 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +38 -1
- package/src/modules/communication_channels/di.ts +7 -0
- package/src/modules/communication_channels/lib/adapter.ts +14 -0
- package/src/modules/communication_channels/lib/email-capabilities.ts +4 -0
- package/src/modules/communication_channels/lib/outbound-recipient.ts +71 -0
- package/src/modules/communication_channels/lib/resolve-channel-type.ts +99 -0
- package/src/modules/communication_channels/lib/test-seed.ts +64 -4
- package/src/modules/configs/i18n/de.json +2 -0
- package/src/modules/configs/i18n/en.json +2 -0
- package/src/modules/configs/i18n/es.json +2 -0
- package/src/modules/configs/i18n/ko.json +2 -0
- package/src/modules/configs/i18n/pl.json +2 -0
- package/src/modules/configs/lib/system-status.ts +9 -0
- package/src/modules/currencies/AGENTS.md +3 -0
- package/src/modules/currencies/backend/currencies/page.tsx +4 -1
- package/src/modules/currencies/backend/exchange-rates/page.tsx +4 -1
- package/src/modules/currencies/services/README.md +45 -0
- package/src/modules/currencies/services/rateFetchingService.ts +6 -1
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +7 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +4 -1
- package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +4 -1
- package/src/modules/customer_accounts/services/domainMappingService.ts +10 -0
- package/src/modules/customers/ai-tools/companies-pack.ts +4 -1
- package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +17 -2
- package/src/modules/customers/ai-tools/deals-pack.ts +66 -1
- package/src/modules/customers/ai-tools/people-pack.ts +4 -1
- package/src/modules/customers/api/activities/route.ts +45 -0
- package/src/modules/customers/api/companies/[id]/people/route.ts +24 -73
- package/src/modules/customers/api/companies/[id]/route.ts +24 -107
- package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +16 -8
- package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
- package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
- package/src/modules/customers/api/deals/[id]/route.ts +17 -28
- package/src/modules/customers/api/deals/[id]/stats/route.ts +10 -25
- package/src/modules/customers/api/deals/aggregate/route.ts +14 -4
- package/src/modules/customers/api/deals/map/route.ts +1 -0
- package/src/modules/customers/api/deals/route.ts +50 -1
- package/src/modules/customers/api/entity-roles-factory.ts +8 -4
- package/src/modules/customers/api/interactions/route.ts +8 -2
- package/src/modules/customers/api/interactions/tasks/route.ts +24 -1
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
- package/src/modules/customers/api/people/[id]/companies/context.ts +4 -1
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
- package/src/modules/customers/api/people/[id]/route.ts +16 -11
- package/src/modules/customers/api/people/check-phone/route.ts +75 -10
- package/src/modules/customers/api/todos/route.ts +27 -2
- package/src/modules/customers/backend/customers/companies/page.tsx +5 -2
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +7 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +8 -2
- package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +5 -14
- package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +109 -27
- package/src/modules/customers/backend/customers/deals/pipeline/components/toneClasses.ts +21 -0
- package/src/modules/customers/backend/customers/people/page.tsx +5 -2
- package/src/modules/customers/backend/hooks/useEmailDuplicateCheck.ts +12 -4
- package/src/modules/customers/commands/deals.ts +34 -59
- package/src/modules/customers/commands/interactions.ts +1 -1
- package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
- package/src/modules/customers/components/CustomerTodosTable.tsx +2 -0
- package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
- package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +1 -1
- package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
- package/src/modules/customers/components/calendar/EventPeekPopover.tsx +4 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
- package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
- package/src/modules/customers/components/calendar/types.ts +2 -2
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +5 -4
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
- package/src/modules/customers/components/detail/DealForm.tsx +22 -2
- package/src/modules/customers/components/detail/DealsSection.tsx +11 -5
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +58 -12
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
- package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +23 -14
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
- package/src/modules/customers/components/detail/types.ts +1 -1
- package/src/modules/customers/components/detail/utils.ts +15 -5
- package/src/modules/customers/data/entities.ts +1 -1
- package/src/modules/customers/data/validators.ts +50 -9
- package/src/modules/customers/events.ts +5 -0
- package/src/modules/customers/i18n/de.json +9 -1
- package/src/modules/customers/i18n/en.json +9 -1
- package/src/modules/customers/i18n/es.json +9 -1
- package/src/modules/customers/i18n/ko.json +9 -1
- package/src/modules/customers/i18n/pl.json +9 -1
- package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
- package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
- package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
- package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
- package/src/modules/customers/lib/closureStage.ts +76 -0
- package/src/modules/customers/lib/dealStatus.ts +42 -0
- package/src/modules/customers/lib/detailReadAccess.ts +35 -0
- package/src/modules/customers/lib/findPeopleByAddresses.ts +8 -6
- package/src/modules/customers/lib/personCompanies.ts +136 -1
- package/src/modules/customers/lib/todoCompatibility.ts +19 -1
- package/src/modules/dashboards/i18n/es.json +4 -4
- package/src/modules/dashboards/i18n/pl.json +7 -7
- package/src/modules/dashboards/lib/widgetScope.ts +6 -0
- package/src/modules/data_sync/AGENTS.md +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -1
- package/src/modules/data_sync/lib/abandoned-run.ts +56 -0
- package/src/modules/data_sync/lib/adapter.ts +36 -0
- package/src/modules/data_sync/lib/sync-engine.ts +113 -5
- package/src/modules/data_sync/workers/sync-export.ts +4 -0
- package/src/modules/data_sync/workers/sync-import.ts +4 -0
- package/src/modules/devices/AGENTS.md +20 -1
- package/src/modules/devices/acl.ts +9 -1
- package/src/modules/devices/api/admin/devices/[id]/route.ts +5 -34
- package/src/modules/devices/api/admin/devices/route.ts +14 -1
- package/src/modules/devices/api/deviceList.ts +135 -11
- package/src/modules/devices/api/deviceSerialization.ts +50 -0
- package/src/modules/devices/api/openapi.ts +9 -1
- package/src/modules/devices/api/route.ts +9 -1
- package/src/modules/devices/backend/devices/[id]/page.tsx +18 -32
- package/src/modules/devices/backend/devices/create/page.tsx +17 -1
- package/src/modules/devices/backend/devices/page.tsx +44 -45
- package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +45 -0
- package/src/modules/devices/backend/devices/userOptions.ts +99 -0
- package/src/modules/devices/i18n/de.json +3 -2
- package/src/modules/devices/i18n/en.json +3 -2
- package/src/modules/devices/i18n/es.json +3 -2
- package/src/modules/devices/i18n/ko.json +3 -2
- package/src/modules/devices/i18n/pl.json +3 -2
- package/src/modules/devices/setup.ts +6 -2
- package/src/modules/directory/backend/directory/organizations/page.tsx +3 -1
- package/src/modules/directory/backend/directory/tenants/page.tsx +3 -1
- package/src/modules/directory/di.ts +14 -2
- package/src/modules/directory/services/organizationHierarchyService.ts +34 -0
- package/src/modules/directory/services/organizationScopeService.ts +85 -0
- package/src/modules/directory/utils/organizationScope.ts +4 -15
- package/src/modules/entities/api/records.ts +2 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +4 -1
- package/src/modules/eudr/api/plots/route.ts +4 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/plots/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/statements/page.tsx +4 -0
- package/src/modules/eudr/commands/statements.ts +0 -3
- package/src/modules/eudr/data/validators.ts +16 -1
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
- package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
- package/src/modules/feature_toggles/defaults.json +0 -8
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +4 -0
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +4 -0
- package/src/modules/inbox_ops/lib/htmlToPlainText.ts +1 -17
- package/src/modules/integrations/api/[id]/credentials/route.ts +6 -4
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +61 -31
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +44 -9
- package/src/modules/integrations/backend/integrations/credential-secret-fields.ts +84 -0
- package/src/modules/integrations/data/validators.ts +12 -2
- package/src/modules/integrations/i18n/de.json +1 -0
- package/src/modules/integrations/i18n/en.json +1 -0
- package/src/modules/integrations/i18n/es.json +1 -0
- package/src/modules/integrations/i18n/ko.json +1 -0
- package/src/modules/integrations/i18n/pl.json +1 -0
- package/src/modules/integrations/lib/credentials-masking.ts +10 -6
- package/src/modules/messages/api/[id]/route.ts +55 -1
- package/src/modules/messages/api/openapi.ts +20 -0
- package/src/modules/messages/api/route.ts +31 -2
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +9 -16
- package/src/modules/messages/components/inboxFilters.ts +36 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +14 -1
- package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +3 -1
- package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +2 -1
- package/src/modules/messages/components/message-detail/types.ts +3 -0
- package/src/modules/messages/components/messageListLabels.ts +52 -3
- package/src/modules/messages/data/validators.ts +43 -4
- package/src/modules/messages/lib/channel-sender-identity.ts +55 -0
- package/src/modules/messages/lib/composeSourceChannelType.ts +95 -0
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -6
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +4 -1
- package/src/modules/payment_gateways/lib/webhook-processor.ts +11 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -0
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +4 -1
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +4 -4
- package/src/modules/query_index/lib/engine.ts +249 -61
- package/src/modules/query_index/lib/search-entity-policy.ts +46 -0
- package/src/modules/query_index/lib/search-tokens.ts +135 -3
- package/src/modules/resources/backend/resources/resource-types/page.tsx +4 -1
- package/src/modules/resources/backend/resources/resources/page.tsx +4 -1
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +4 -0
- package/src/modules/sales/backend/sales/channels/page.tsx +4 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +14 -8
- package/src/modules/sales/commands/documents.ts +93 -0
- package/src/modules/sales/components/PriceWithCurrency.tsx +17 -5
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +4 -0
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/ItemsSection.tsx +56 -18
- package/src/modules/sales/components/documents/LineItemDialog.tsx +137 -20
- package/src/modules/sales/components/documents/PaymentsSection.tsx +20 -9
- package/src/modules/sales/components/documents/ReturnsSection.tsx +6 -5
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +106 -14
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +6 -5
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +5 -4
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/lineItemShipmentLock.ts +74 -0
- package/src/modules/sales/components/documents/lineItemUtils.ts +7 -2
- package/src/modules/sales/data/entities.ts +6 -0
- package/src/modules/sales/extension-points.ts +1 -0
- package/src/modules/sales/i18n/de.json +8 -0
- package/src/modules/sales/i18n/en.json +8 -0
- package/src/modules/sales/i18n/es.json +8 -0
- package/src/modules/sales/i18n/ko.json +8 -0
- package/src/modules/sales/i18n/pl.json +8 -0
- package/src/modules/sales/lib/salesChannelsToggleSeed.ts +42 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +60 -0
- package/src/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.ts +87 -0
- package/src/modules/sales/setup.ts +2 -0
- package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +9 -1
- package/src/modules/sales/widgets/injection-table.ts +4 -7
- package/src/modules/staff/backend/staff/leave-requests/page.tsx +4 -0
- package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +4 -0
- package/src/modules/staff/backend/staff/team-members/page.tsx +6 -0
- package/src/modules/staff/backend/staff/team-roles/page.tsx +4 -0
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -0
- package/src/modules/staff/backend/staff/teams/page.tsx +4 -0
- package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +4 -0
- package/src/modules/sync_excel/lib/adapters/customers.ts +5 -0
- package/src/modules/warranty_claims/api/assignees/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +12 -9
- package/src/modules/warranty_claims/backend/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +4 -0
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +4 -0
- package/src/modules/wms/api/warehouseSearch.ts +23 -0
- package/src/modules/wms/api/warehouses/route.ts +13 -9
- package/src/modules/wms/api/zones/route.ts +16 -0
- package/src/modules/wms/commands/configuration.ts +159 -10
- package/src/modules/wms/commands/inventory-actions.ts +3 -0
- package/src/modules/wms/commands/shared.ts +17 -0
- package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +216 -0
- package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +95 -141
- package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +2 -0
- package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -0
- package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +36 -20
- package/src/modules/wms/components/backend/warehouseFormOptions.ts +72 -0
- package/src/modules/wms/i18n/de.json +2 -0
- package/src/modules/wms/i18n/en.json +2 -0
- package/src/modules/wms/i18n/es.json +2 -0
- package/src/modules/wms/i18n/ko.json +2 -0
- package/src/modules/wms/i18n/pl.json +2 -0
- package/src/modules/workflows/backend/definitions/page.tsx +4 -1
- package/src/modules/workflows/backend/events/page.tsx +4 -1
- package/src/modules/workflows/backend/instances/page.tsx +4 -1
- package/src/modules/workflows/backend/tasks/page.tsx +4 -1
- package/src/modules/workflows/lib/activity-executor.ts +9 -0
|
@@ -25,6 +25,7 @@ function CustomerRolesPage() {
|
|
|
25
25
|
const [pageSize] = React.useState(50);
|
|
26
26
|
const [total, setTotal] = React.useState(0);
|
|
27
27
|
const [totalPages, setTotalPages] = React.useState(1);
|
|
28
|
+
const [totalIsCapped, setTotalIsCapped] = React.useState(false);
|
|
28
29
|
const [search, setSearch] = React.useState("");
|
|
29
30
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
30
31
|
const [reloadToken, setReloadToken] = React.useState(0);
|
|
@@ -64,6 +65,7 @@ function CustomerRolesPage() {
|
|
|
64
65
|
setRows(items);
|
|
65
66
|
setTotal(typeof payload?.total === "number" ? payload.total : items.length);
|
|
66
67
|
setTotalPages(typeof payload?.totalPages === "number" ? payload.totalPages : 1);
|
|
68
|
+
setTotalIsCapped(payload?.totalIsCapped === true);
|
|
67
69
|
} catch (err) {
|
|
68
70
|
if (!cancelled) {
|
|
69
71
|
const message = err instanceof Error ? err.message : t("customer_accounts.admin.roles.error.load", "Failed to load roles");
|
|
@@ -194,7 +196,7 @@ function CustomerRolesPage() {
|
|
|
194
196
|
]
|
|
195
197
|
}
|
|
196
198
|
),
|
|
197
|
-
pagination: { page, pageSize, total, totalPages, onPageChange: setPage },
|
|
199
|
+
pagination: { page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage },
|
|
198
200
|
isLoading
|
|
199
201
|
}
|
|
200
202
|
) }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\n\ntype RoleRow = {\n id: string\n name: string\n slug: string\n description: string | null\n isSystem: boolean\n isDefault: boolean\n customerAssignable: boolean\n createdAt: string\n updatedAt?: string | null\n}\n\ntype RolesResponse = {\n items?: RoleRow[]\n total?: number\n totalPages?: number\n}\n\nexport default function CustomerRolesPage() {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<RoleRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const { runMutation } = useGuardedMutation<{ entityType: string }>({\n contextId: 'customer_accounts:roles-list',\n })\n\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { entityType: 'customer_accounts:role' },\n })\n },\n [runMutation],\n )\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n return params.toString()\n }, [page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: RolesResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<RolesResponse>(\n `/api/customer_accounts/admin/roles?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.roles.error.load', 'Failed to load roles'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.load', 'Failed to load roles')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleDelete = React.useCallback(async (role: RoleRow) => {\n if (role.isDefault) {\n flash(t('customer_accounts.admin.roles.error.deleteDefault', 'The default role cannot be deleted. Set another role as default first.'), 'error')\n return\n }\n const confirmed = await confirm({\n title: t('customer_accounts.admin.roles.confirm.delete', 'Delete role \"{{name}}\"?', { name: role.name }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(role.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/roles/${encodeURIComponent(role.id)}`,\n { method: 'DELETE' },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.roles.error.delete', 'Failed to delete role'), 'error')\n return\n }\n flash(t('customer_accounts.admin.roles.flash.deleted', 'Role deleted'), 'success')\n setReloadToken((token) => token + 1)\n }, { id: role.id })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.delete', 'Failed to delete role')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const columns = React.useMemo<ColumnDef<RoleRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('customer_accounts.admin.roles.columns.name', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/roles/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.name}\n </Link>\n ),\n },\n {\n accessorKey: 'slug',\n header: t('customer_accounts.admin.roles.columns.slug', 'Slug'),\n cell: ({ row }) => (\n <code className=\"rounded bg-muted px-1.5 py-0.5 text-xs\">{row.original.slug}</code>\n ),\n },\n {\n accessorKey: 'description',\n header: t('customer_accounts.admin.roles.columns.description', 'Description'),\n cell: ({ row }) => row.original.description || <span className=\"text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'isSystem',\n header: t('customer_accounts.admin.roles.columns.isSystem', 'System'),\n cell: ({ row }) => (\n row.original.isSystem ? (\n <span className=\"inline-flex items-center rounded-full bg-status-info-bg px-2 py-0.5 text-xs font-medium text-status-info-text\">\n {t('customer_accounts.admin.roles.system', 'System')}\n </span>\n ) : null\n ),\n },\n {\n accessorKey: 'isDefault',\n header: t('customer_accounts.admin.roles.columns.isDefault', 'Default'),\n cell: ({ row }) => (\n row.original.isDefault ? (\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {t('customer_accounts.admin.roles.default', 'Default')}\n </span>\n ) : null\n ),\n },\n {\n accessorKey: 'customerAssignable',\n header: t('customer_accounts.admin.roles.columns.customerAssignable', 'Self-assignable'),\n cell: ({ row }) => (\n row.original.customerAssignable ? (\n <Badge variant=\"brand\">\n {t('customer_accounts.admin.roles.assignable', 'Yes')}\n </Badge>\n ) : (\n <span className=\"text-muted-foreground text-sm\">\n {t('customer_accounts.admin.roles.notAssignable', 'No')}\n </span>\n )\n ),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<RoleRow>\n title={t('customer_accounts.admin.roles.title', 'Customer Roles')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customer_accounts/roles/create\">\n {t('customer_accounts.admin.roles.actions.create', 'Create Role')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.roles.searchPlaceholder', 'Search roles...')}\n perspective={{ tableId: extensionPoints.hosts.rolesTable.tableId }}\n emptyState={(\n <ListEmptyState\n entityName={t('customer_accounts.admin.roles.title', 'Customer Roles')}\n createHref=\"/backend/customer_accounts/roles/create\"\n createLabel={t('customer_accounts.admin.roles.actions.create', 'Create Role')}\n />\n )}\n onRowClick={(row) => router.push(`/backend/customer_accounts/roles/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('customer_accounts.admin.roles.actions.edit', 'Edit'),\n onSelect: () => { router.push(`/backend/customer_accounts/roles/${row.id}`) },\n },\n ...(!row.isDefault ? [{\n id: 'delete',\n label: t('customer_accounts.admin.roles.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n }] : []),\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\n\ntype RoleRow = {\n id: string\n name: string\n slug: string\n description: string | null\n isSystem: boolean\n isDefault: boolean\n customerAssignable: boolean\n createdAt: string\n updatedAt?: string | null\n}\n\ntype RolesResponse = {\n items?: RoleRow[]\n total?: number\n totalPages?: number\n totalIsCapped?: boolean\n}\n\nexport default function CustomerRolesPage() {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<RoleRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const { runMutation } = useGuardedMutation<{ entityType: string }>({\n contextId: 'customer_accounts:roles-list',\n })\n\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { entityType: 'customer_accounts:role' },\n })\n },\n [runMutation],\n )\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n return params.toString()\n }, [page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: RolesResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<RolesResponse>(\n `/api/customer_accounts/admin/roles?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.roles.error.load', 'Failed to load roles'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n setTotalIsCapped(payload?.totalIsCapped === true)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.load', 'Failed to load roles')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleDelete = React.useCallback(async (role: RoleRow) => {\n if (role.isDefault) {\n flash(t('customer_accounts.admin.roles.error.deleteDefault', 'The default role cannot be deleted. Set another role as default first.'), 'error')\n return\n }\n const confirmed = await confirm({\n title: t('customer_accounts.admin.roles.confirm.delete', 'Delete role \"{{name}}\"?', { name: role.name }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(role.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/roles/${encodeURIComponent(role.id)}`,\n { method: 'DELETE' },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.roles.error.delete', 'Failed to delete role'), 'error')\n return\n }\n flash(t('customer_accounts.admin.roles.flash.deleted', 'Role deleted'), 'success')\n setReloadToken((token) => token + 1)\n }, { id: role.id })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.delete', 'Failed to delete role')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const columns = React.useMemo<ColumnDef<RoleRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('customer_accounts.admin.roles.columns.name', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/roles/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.name}\n </Link>\n ),\n },\n {\n accessorKey: 'slug',\n header: t('customer_accounts.admin.roles.columns.slug', 'Slug'),\n cell: ({ row }) => (\n <code className=\"rounded bg-muted px-1.5 py-0.5 text-xs\">{row.original.slug}</code>\n ),\n },\n {\n accessorKey: 'description',\n header: t('customer_accounts.admin.roles.columns.description', 'Description'),\n cell: ({ row }) => row.original.description || <span className=\"text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'isSystem',\n header: t('customer_accounts.admin.roles.columns.isSystem', 'System'),\n cell: ({ row }) => (\n row.original.isSystem ? (\n <span className=\"inline-flex items-center rounded-full bg-status-info-bg px-2 py-0.5 text-xs font-medium text-status-info-text\">\n {t('customer_accounts.admin.roles.system', 'System')}\n </span>\n ) : null\n ),\n },\n {\n accessorKey: 'isDefault',\n header: t('customer_accounts.admin.roles.columns.isDefault', 'Default'),\n cell: ({ row }) => (\n row.original.isDefault ? (\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {t('customer_accounts.admin.roles.default', 'Default')}\n </span>\n ) : null\n ),\n },\n {\n accessorKey: 'customerAssignable',\n header: t('customer_accounts.admin.roles.columns.customerAssignable', 'Self-assignable'),\n cell: ({ row }) => (\n row.original.customerAssignable ? (\n <Badge variant=\"brand\">\n {t('customer_accounts.admin.roles.assignable', 'Yes')}\n </Badge>\n ) : (\n <span className=\"text-muted-foreground text-sm\">\n {t('customer_accounts.admin.roles.notAssignable', 'No')}\n </span>\n )\n ),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<RoleRow>\n title={t('customer_accounts.admin.roles.title', 'Customer Roles')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customer_accounts/roles/create\">\n {t('customer_accounts.admin.roles.actions.create', 'Create Role')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.roles.searchPlaceholder', 'Search roles...')}\n perspective={{ tableId: extensionPoints.hosts.rolesTable.tableId }}\n emptyState={(\n <ListEmptyState\n entityName={t('customer_accounts.admin.roles.title', 'Customer Roles')}\n createHref=\"/backend/customer_accounts/roles/create\"\n createLabel={t('customer_accounts.admin.roles.actions.create', 'Create Role')}\n />\n )}\n onRowClick={(row) => router.push(`/backend/customer_accounts/roles/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('customer_accounts.admin.roles.actions.edit', 'Edit'),\n onSelect: () => { router.push(`/backend/customer_accounts/roles/${row.id}`) },\n },\n ...(!row.isDefault ? [{\n id: 'delete',\n label: t('customer_accounts.admin.roles.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n }] : []),\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js
CHANGED
|
@@ -192,6 +192,7 @@ function PortalUsersPageClient({ portalOrigin }) {
|
|
|
192
192
|
const [pageSize] = React.useState(50);
|
|
193
193
|
const [total, setTotal] = React.useState(0);
|
|
194
194
|
const [totalPages, setTotalPages] = React.useState(1);
|
|
195
|
+
const [totalIsCapped, setTotalIsCapped] = React.useState(false);
|
|
195
196
|
const [search, setSearch] = React.useState("");
|
|
196
197
|
const [filterValues, setFilterValues] = React.useState({});
|
|
197
198
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
@@ -264,6 +265,7 @@ function PortalUsersPageClient({ portalOrigin }) {
|
|
|
264
265
|
setRows(items);
|
|
265
266
|
setTotal(typeof payload?.total === "number" ? payload.total : items.length);
|
|
266
267
|
setTotalPages(typeof payload?.totalPages === "number" ? payload.totalPages : 1);
|
|
268
|
+
setTotalIsCapped(payload?.totalIsCapped === true);
|
|
267
269
|
} catch (err) {
|
|
268
270
|
if (!cancelled) {
|
|
269
271
|
const message = err instanceof Error ? err.message : t("customer_accounts.admin.error.loadUsers", "Failed to load customer users");
|
|
@@ -500,7 +502,7 @@ function PortalUsersPageClient({ portalOrigin }) {
|
|
|
500
502
|
]
|
|
501
503
|
}
|
|
502
504
|
),
|
|
503
|
-
pagination: { page, pageSize, total, totalPages, onPageChange: setPage },
|
|
505
|
+
pagination: { page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage },
|
|
504
506
|
isLoading
|
|
505
507
|
}
|
|
506
508
|
),
|
package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Globe, Settings } from 'lucide-react'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { buildPortalRootUrl, buildPortalUrlPattern } from '../../../lib/portalUrl'\n\ntype UserRow = {\n id: string\n displayName: string\n email: string\n emailVerified: boolean\n isActive: boolean\n lastLoginAt: string | null\n roles: Array<{ id: string; name: string; slug: string }>\n createdAt: string\n updatedAt?: string | null\n personEntityId: string | null\n customerEntityId: string | null\n}\n\ntype UsersResponse = {\n items?: UserRow[]\n total?: number\n totalPages?: number\n}\n\nfunction formatDate(value: string | null | undefined, fallback: string): string {\n if (!value) return fallback\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallback\n return date.toLocaleDateString()\n}\n\nasync function fetchRoleFilterOptions(): Promise<Array<{ value: string; label: string; id: string }>> {\n try {\n const call = await apiCall<{ items?: Array<{ id: string; name: string }> }>(\n '/api/customer_accounts/admin/roles?pageSize=100',\n )\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items : []\n return items\n .filter((item) => typeof item?.id === 'string' && typeof item?.name === 'string')\n .map((item) => ({ value: item.id, label: item.name, id: item.id }))\n } catch {\n return []\n }\n}\n\nfunction CreateUserDialog({\n open,\n onOpenChange,\n roleOptions,\n onCreated,\n onRunMutation,\n}: {\n open: boolean\n onOpenChange: (next: boolean) => void\n roleOptions: Array<{ id: string; label: string }>\n onCreated: () => void\n onRunMutation: <T>(operation: () => Promise<T>) => Promise<T>\n}) {\n const t = useT()\n const [email, setEmail] = React.useState('')\n const [displayName, setDisplayName] = React.useState('')\n const [password, setPassword] = React.useState('')\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const resetForm = React.useCallback(() => {\n setEmail('')\n setDisplayName('')\n setPassword('')\n setSelectedRoleIds([])\n }, [])\n\n const handleSubmit = React.useCallback(async (event: React.FormEvent) => {\n event.preventDefault()\n if (!email.trim() || !displayName.trim() || !password.trim()) {\n flash(t('customer_accounts.admin.createUser.error.required', 'Email, name, and password are required'), 'error')\n return\n }\n setIsSubmitting(true)\n try {\n await onRunMutation(async () => {\n const call = await apiCall<{ ok: boolean; error?: string }>(\n '/api/customer_accounts/admin/users',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: email.trim(),\n displayName: displayName.trim(),\n password,\n roleIds: selectedRoleIds.length > 0 ? selectedRoleIds : undefined,\n }),\n },\n )\n if (!call.ok) {\n flash(call.result?.error || t('customer_accounts.admin.createUser.error.save', 'Failed to create user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.createUser.flash.created', 'User created'), 'success')\n resetForm()\n onOpenChange(false)\n onCreated()\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.createUser.error.save', 'Failed to create user')\n flash(message, 'error')\n } finally {\n setIsSubmitting(false)\n }\n }, [displayName, email, onCreated, onOpenChange, onRunMutation, password, resetForm, selectedRoleIds, t])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n const form = (event.target as HTMLElement).closest('form')\n if (form) form.requestSubmit()\n }\n }, [])\n\n return (\n <Dialog open={open} onOpenChange={(next) => { if (!next) resetForm(); onOpenChange(next) }}>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t('customer_accounts.admin.createUser.title', 'Create Customer User')}</DialogTitle>\n </DialogHeader>\n <form onSubmit={(event) => { void handleSubmit(event) }} onKeyDown={handleKeyDown} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-email\">\n {t('customer_accounts.admin.createUser.fields.email', 'Email')}\n </label>\n <EmailInput\n id=\"create-email\"\n required\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.emailPlaceholder', 'user@example.com')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-name\">\n {t('customer_accounts.admin.createUser.fields.displayName', 'Display Name')}\n </label>\n <Input\n id=\"create-name\"\n type=\"text\"\n required\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.displayNamePlaceholder', 'John Doe')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-password\">\n {t('customer_accounts.admin.createUser.fields.password', 'Password')}\n </label>\n <PasswordInput\n id=\"create-password\"\n required\n minLength={8}\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.passwordPlaceholder', 'Min. 8 characters')}\n autoComplete=\"new-password\"\n />\n </div>\n {roleOptions.length > 0 && (\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium\">{t('customer_accounts.admin.createUser.fields.roles', 'Roles')}</p>\n <div className=\"flex flex-wrap gap-2\">\n {roleOptions.map((role) => {\n const isSelected = selectedRoleIds.includes(role.id)\n return (\n <Button\n key={role.id}\n type=\"button\"\n size=\"2xs\"\n variant={isSelected ? 'secondary' : 'outline'}\n aria-pressed={isSelected}\n onClick={() => setSelectedRoleIds((prev) =>\n prev.includes(role.id) ? prev.filter((rid) => rid !== role.id) : [...prev, role.id],\n )}\n className=\"rounded-full\"\n >\n {role.label}\n </Button>\n )\n })}\n </div>\n </div>\n )}\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { resetForm(); onOpenChange(false) }}>\n {t('customer_accounts.admin.createUser.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={isSubmitting}>\n {isSubmitting\n ? t('customer_accounts.admin.createUser.actions.creating', 'Creating...')\n : t('customer_accounts.admin.createUser.actions.create', 'Create User')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type PortalUsersPageClientProps = {\n portalOrigin: string\n}\n\nexport function PortalUsersPageClient({ portalOrigin }: PortalUsersPageClientProps) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<UserRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [roleOptions, setRoleOptions] = React.useState<Array<{ value: string; label: string; id: string }>>([])\n const [createDialogOpen, setCreateDialogOpen] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n entityType: string\n entityId?: string\n }>({\n contextId: 'customer_accounts:users-list',\n })\n\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { entityType: 'customer_accounts:user' },\n })\n },\n [runMutation],\n )\n\n React.useEffect(() => {\n let cancelled = false\n fetchRoleFilterOptions().then((opts) => {\n if (!cancelled) setRoleOptions(opts)\n })\n return () => { cancelled = true }\n }, [])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('customer_accounts.admin.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'active', label: t('customer_accounts.admin.filters.active', 'Active') },\n { value: 'inactive', label: t('customer_accounts.admin.filters.inactive', 'Inactive') },\n ],\n },\n {\n id: 'roleId',\n label: t('customer_accounts.admin.filters.role', 'Role'),\n type: 'select',\n options: roleOptions,\n },\n ], [roleOptions, t])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) params.set('status', status)\n const roleId = filterValues.roleId\n if (typeof roleId === 'string' && roleId.trim()) params.set('roleId', roleId)\n return params.toString()\n }, [filterValues, page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: UsersResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<UsersResponse>(\n `/api/customer_accounts/admin/users?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleToggleActive = React.useCallback(async (user: UserRow) => {\n const nextActive = !user.isActive\n const actionLabel = nextActive\n ? t('customer_accounts.admin.actions.activate', 'Activate')\n : t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.toggleActive', '{{action}} user \"{{name}}\"?', {\n action: actionLabel,\n name: user.displayName || user.email,\n }),\n variant: nextActive ? 'default' : 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: nextActive }),\n },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.toggleActive', 'Failed to update user status'), 'error')\n return\n }\n flash(\n nextActive\n ? t('customer_accounts.admin.flash.activated', 'User activated')\n : t('customer_accounts.admin.flash.deactivated', 'User deactivated'),\n 'success',\n )\n setReloadToken((token) => token + 1)\n }, { userId: user.id, isActive: nextActive })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.toggleActive', 'Failed to update user status')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleDelete = React.useCallback(async (user: UserRow) => {\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.delete', 'Delete user \"{{name}}\"?', {\n name: user.displayName || user.email,\n }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n { method: 'DELETE' },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.delete', 'Failed to delete user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.flash.deleted', 'User deleted'), 'success')\n setReloadToken((token) => token + 1)\n }, { id: user.id })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.delete', 'Failed to delete user')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const columns = React.useMemo<ColumnDef<UserRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">-</span>\n return [\n {\n accessorKey: 'displayName',\n header: t('customer_accounts.admin.columns.displayName', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/users/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.displayName || row.original.email}\n </Link>\n ),\n },\n {\n accessorKey: 'email',\n header: t('customer_accounts.admin.columns.email', 'Email'),\n },\n {\n accessorKey: 'emailVerified',\n header: t('customer_accounts.admin.columns.emailVerified', 'Verified'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.emailVerified\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}>\n {row.original.emailVerified\n ? t('customer_accounts.admin.verified', 'Yes')\n : t('customer_accounts.admin.unverified', 'No')}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('customer_accounts.admin.columns.status', 'Status'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.isActive\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}>\n {row.original.isActive\n ? t('customer_accounts.admin.active', 'Active')\n : t('customer_accounts.admin.inactive', 'Inactive')}\n </span>\n ),\n },\n {\n accessorKey: 'lastLoginAt',\n header: t('customer_accounts.admin.columns.lastLogin', 'Last Login'),\n cell: ({ row }) => formatDate(row.original.lastLoginAt, '-') || noValue,\n },\n {\n accessorKey: 'roles',\n header: t('customer_accounts.admin.columns.roles', 'Roles'),\n cell: ({ row }) => {\n const roles = row.original.roles\n if (!roles || !roles.length) return noValue\n return <span className=\"text-sm\">{roles.map((r) => r.name).join(', ')}</span>\n },\n },\n {\n accessorKey: 'createdAt',\n header: t('customer_accounts.admin.columns.createdAt', 'Created'),\n cell: ({ row }) => formatDate(row.original.createdAt, '-'),\n },\n ]\n }, [t])\n\n return (\n <>\n <div className=\"rounded-lg border border-status-info-border bg-status-info-bg p-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h3 className=\"text-sm font-medium text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.title', 'Customer Portal')}\n </h3>\n <p className=\"mt-1 text-sm text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.description', 'Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.')}\n </p>\n <p className=\"mt-1.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.url', 'Portal URL: {url}', {\n url: buildPortalUrlPattern(portalOrigin),\n })}\n </p>\n <p className=\"mt-0.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.credentials', 'Demo credentials: alice.johnson@example.com / Password123!')}\n </p>\n </div>\n <div className=\"flex shrink-0 flex-col gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <Link href=\"/backend/customer_accounts/settings\">\n <Settings className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.openConfiguration', 'Open Configuration')}\n </Link>\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <a href={buildPortalRootUrl(portalOrigin)} target=\"_blank\" rel=\"noopener noreferrer\">\n <Globe className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.open', 'Open Portal')}\n </a>\n </Button>\n </div>\n </div>\n </div>\n <DataTable<UserRow>\n stickyActionsColumn\n title={t('customer_accounts.admin.title', 'Users')}\n actions={(\n <Button onClick={() => setCreateDialogOpen(true)}>\n {t('customer_accounts.admin.actions.createUser', 'Create User')}\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.searchPlaceholder', 'Search by name or email...')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n perspective={{ tableId: extensionPoints.hosts.usersTable.tableId }}\n emptyState={(\n <ListEmptyState\n entityName={t('customer_accounts.admin.title', 'Users')}\n onCreate={() => setCreateDialogOpen(true)}\n createLabel={t('customer_accounts.admin.actions.createUser', 'Create User')}\n />\n )}\n onRowClick={(row) => router.push(`/backend/customer_accounts/users/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'view',\n label: t('customer_accounts.admin.actions.view', 'View'),\n onSelect: () => { router.push(`/backend/customer_accounts/users/${row.id}`) },\n },\n {\n id: 'toggle-active',\n label: row.isActive\n ? t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n : t('customer_accounts.admin.actions.activate', 'Activate'),\n onSelect: () => { void handleToggleActive(row) },\n },\n {\n id: 'delete',\n label: t('customer_accounts.admin.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n },\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n <CreateUserDialog\n open={createDialogOpen}\n onOpenChange={setCreateDialogOpen}\n roleOptions={roleOptions}\n onCreated={() => setReloadToken((token) => token + 1)}\n onRunMutation={runMutationWithContext}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Globe, Settings } from 'lucide-react'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { buildPortalRootUrl, buildPortalUrlPattern } from '../../../lib/portalUrl'\n\ntype UserRow = {\n id: string\n displayName: string\n email: string\n emailVerified: boolean\n isActive: boolean\n lastLoginAt: string | null\n roles: Array<{ id: string; name: string; slug: string }>\n createdAt: string\n updatedAt?: string | null\n personEntityId: string | null\n customerEntityId: string | null\n}\n\ntype UsersResponse = {\n items?: UserRow[]\n total?: number\n totalPages?: number\n totalIsCapped?: boolean\n}\n\nfunction formatDate(value: string | null | undefined, fallback: string): string {\n if (!value) return fallback\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallback\n return date.toLocaleDateString()\n}\n\nasync function fetchRoleFilterOptions(): Promise<Array<{ value: string; label: string; id: string }>> {\n try {\n const call = await apiCall<{ items?: Array<{ id: string; name: string }> }>(\n '/api/customer_accounts/admin/roles?pageSize=100',\n )\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items : []\n return items\n .filter((item) => typeof item?.id === 'string' && typeof item?.name === 'string')\n .map((item) => ({ value: item.id, label: item.name, id: item.id }))\n } catch {\n return []\n }\n}\n\nfunction CreateUserDialog({\n open,\n onOpenChange,\n roleOptions,\n onCreated,\n onRunMutation,\n}: {\n open: boolean\n onOpenChange: (next: boolean) => void\n roleOptions: Array<{ id: string; label: string }>\n onCreated: () => void\n onRunMutation: <T>(operation: () => Promise<T>) => Promise<T>\n}) {\n const t = useT()\n const [email, setEmail] = React.useState('')\n const [displayName, setDisplayName] = React.useState('')\n const [password, setPassword] = React.useState('')\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const resetForm = React.useCallback(() => {\n setEmail('')\n setDisplayName('')\n setPassword('')\n setSelectedRoleIds([])\n }, [])\n\n const handleSubmit = React.useCallback(async (event: React.FormEvent) => {\n event.preventDefault()\n if (!email.trim() || !displayName.trim() || !password.trim()) {\n flash(t('customer_accounts.admin.createUser.error.required', 'Email, name, and password are required'), 'error')\n return\n }\n setIsSubmitting(true)\n try {\n await onRunMutation(async () => {\n const call = await apiCall<{ ok: boolean; error?: string }>(\n '/api/customer_accounts/admin/users',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: email.trim(),\n displayName: displayName.trim(),\n password,\n roleIds: selectedRoleIds.length > 0 ? selectedRoleIds : undefined,\n }),\n },\n )\n if (!call.ok) {\n flash(call.result?.error || t('customer_accounts.admin.createUser.error.save', 'Failed to create user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.createUser.flash.created', 'User created'), 'success')\n resetForm()\n onOpenChange(false)\n onCreated()\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.createUser.error.save', 'Failed to create user')\n flash(message, 'error')\n } finally {\n setIsSubmitting(false)\n }\n }, [displayName, email, onCreated, onOpenChange, onRunMutation, password, resetForm, selectedRoleIds, t])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n const form = (event.target as HTMLElement).closest('form')\n if (form) form.requestSubmit()\n }\n }, [])\n\n return (\n <Dialog open={open} onOpenChange={(next) => { if (!next) resetForm(); onOpenChange(next) }}>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t('customer_accounts.admin.createUser.title', 'Create Customer User')}</DialogTitle>\n </DialogHeader>\n <form onSubmit={(event) => { void handleSubmit(event) }} onKeyDown={handleKeyDown} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-email\">\n {t('customer_accounts.admin.createUser.fields.email', 'Email')}\n </label>\n <EmailInput\n id=\"create-email\"\n required\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.emailPlaceholder', 'user@example.com')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-name\">\n {t('customer_accounts.admin.createUser.fields.displayName', 'Display Name')}\n </label>\n <Input\n id=\"create-name\"\n type=\"text\"\n required\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.displayNamePlaceholder', 'John Doe')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"create-password\">\n {t('customer_accounts.admin.createUser.fields.password', 'Password')}\n </label>\n <PasswordInput\n id=\"create-password\"\n required\n minLength={8}\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n placeholder={t('customer_accounts.admin.createUser.fields.passwordPlaceholder', 'Min. 8 characters')}\n autoComplete=\"new-password\"\n />\n </div>\n {roleOptions.length > 0 && (\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium\">{t('customer_accounts.admin.createUser.fields.roles', 'Roles')}</p>\n <div className=\"flex flex-wrap gap-2\">\n {roleOptions.map((role) => {\n const isSelected = selectedRoleIds.includes(role.id)\n return (\n <Button\n key={role.id}\n type=\"button\"\n size=\"2xs\"\n variant={isSelected ? 'secondary' : 'outline'}\n aria-pressed={isSelected}\n onClick={() => setSelectedRoleIds((prev) =>\n prev.includes(role.id) ? prev.filter((rid) => rid !== role.id) : [...prev, role.id],\n )}\n className=\"rounded-full\"\n >\n {role.label}\n </Button>\n )\n })}\n </div>\n </div>\n )}\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { resetForm(); onOpenChange(false) }}>\n {t('customer_accounts.admin.createUser.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={isSubmitting}>\n {isSubmitting\n ? t('customer_accounts.admin.createUser.actions.creating', 'Creating...')\n : t('customer_accounts.admin.createUser.actions.create', 'Create User')}\n </Button>\n </div>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type PortalUsersPageClientProps = {\n portalOrigin: string\n}\n\nexport function PortalUsersPageClient({ portalOrigin }: PortalUsersPageClientProps) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<UserRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [roleOptions, setRoleOptions] = React.useState<Array<{ value: string; label: string; id: string }>>([])\n const [createDialogOpen, setCreateDialogOpen] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n entityType: string\n entityId?: string\n }>({\n contextId: 'customer_accounts:users-list',\n })\n\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { entityType: 'customer_accounts:user' },\n })\n },\n [runMutation],\n )\n\n React.useEffect(() => {\n let cancelled = false\n fetchRoleFilterOptions().then((opts) => {\n if (!cancelled) setRoleOptions(opts)\n })\n return () => { cancelled = true }\n }, [])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('customer_accounts.admin.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'active', label: t('customer_accounts.admin.filters.active', 'Active') },\n { value: 'inactive', label: t('customer_accounts.admin.filters.inactive', 'Inactive') },\n ],\n },\n {\n id: 'roleId',\n label: t('customer_accounts.admin.filters.role', 'Role'),\n type: 'select',\n options: roleOptions,\n },\n ], [roleOptions, t])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) params.set('status', status)\n const roleId = filterValues.roleId\n if (typeof roleId === 'string' && roleId.trim()) params.set('roleId', roleId)\n return params.toString()\n }, [filterValues, page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: UsersResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<UsersResponse>(\n `/api/customer_accounts/admin/users?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n setTotalIsCapped(payload?.totalIsCapped === true)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleToggleActive = React.useCallback(async (user: UserRow) => {\n const nextActive = !user.isActive\n const actionLabel = nextActive\n ? t('customer_accounts.admin.actions.activate', 'Activate')\n : t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.toggleActive', '{{action}} user \"{{name}}\"?', {\n action: actionLabel,\n name: user.displayName || user.email,\n }),\n variant: nextActive ? 'default' : 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: nextActive }),\n },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.toggleActive', 'Failed to update user status'), 'error')\n return\n }\n flash(\n nextActive\n ? t('customer_accounts.admin.flash.activated', 'User activated')\n : t('customer_accounts.admin.flash.deactivated', 'User deactivated'),\n 'success',\n )\n setReloadToken((token) => token + 1)\n }, { userId: user.id, isActive: nextActive })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.toggleActive', 'Failed to update user status')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleDelete = React.useCallback(async (user: UserRow) => {\n const confirmed = await confirm({\n title: t('customer_accounts.admin.confirm.delete', 'Delete user \"{{name}}\"?', {\n name: user.displayName || user.email,\n }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutationWithContext(async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(user.updatedAt),\n () => apiCall(\n `/api/customer_accounts/admin/users/${encodeURIComponent(user.id)}`,\n { method: 'DELETE' },\n ),\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.error.delete', 'Failed to delete user'), 'error')\n return\n }\n flash(t('customer_accounts.admin.flash.deleted', 'User deleted'), 'success')\n setReloadToken((token) => token + 1)\n }, { id: user.id })\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.delete', 'Failed to delete user')\n flash(message, 'error')\n }\n }, [confirm, runMutationWithContext, t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const columns = React.useMemo<ColumnDef<UserRow>[]>(() => {\n const noValue = <span className=\"text-muted-foreground text-sm\">-</span>\n return [\n {\n accessorKey: 'displayName',\n header: t('customer_accounts.admin.columns.displayName', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/users/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.displayName || row.original.email}\n </Link>\n ),\n },\n {\n accessorKey: 'email',\n header: t('customer_accounts.admin.columns.email', 'Email'),\n },\n {\n accessorKey: 'emailVerified',\n header: t('customer_accounts.admin.columns.emailVerified', 'Verified'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.emailVerified\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}>\n {row.original.emailVerified\n ? t('customer_accounts.admin.verified', 'Yes')\n : t('customer_accounts.admin.unverified', 'No')}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('customer_accounts.admin.columns.status', 'Status'),\n cell: ({ row }) => (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n row.original.isActive\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}>\n {row.original.isActive\n ? t('customer_accounts.admin.active', 'Active')\n : t('customer_accounts.admin.inactive', 'Inactive')}\n </span>\n ),\n },\n {\n accessorKey: 'lastLoginAt',\n header: t('customer_accounts.admin.columns.lastLogin', 'Last Login'),\n cell: ({ row }) => formatDate(row.original.lastLoginAt, '-') || noValue,\n },\n {\n accessorKey: 'roles',\n header: t('customer_accounts.admin.columns.roles', 'Roles'),\n cell: ({ row }) => {\n const roles = row.original.roles\n if (!roles || !roles.length) return noValue\n return <span className=\"text-sm\">{roles.map((r) => r.name).join(', ')}</span>\n },\n },\n {\n accessorKey: 'createdAt',\n header: t('customer_accounts.admin.columns.createdAt', 'Created'),\n cell: ({ row }) => formatDate(row.original.createdAt, '-'),\n },\n ]\n }, [t])\n\n return (\n <>\n <div className=\"rounded-lg border border-status-info-border bg-status-info-bg p-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h3 className=\"text-sm font-medium text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.title', 'Customer Portal')}\n </h3>\n <p className=\"mt-1 text-sm text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.description', 'Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.')}\n </p>\n <p className=\"mt-1.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.url', 'Portal URL: {url}', {\n url: buildPortalUrlPattern(portalOrigin),\n })}\n </p>\n <p className=\"mt-0.5 text-xs text-status-info-text\">\n {t('customer_accounts.admin.portalInfo.credentials', 'Demo credentials: alice.johnson@example.com / Password123!')}\n </p>\n </div>\n <div className=\"flex shrink-0 flex-col gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <Link href=\"/backend/customer_accounts/settings\">\n <Settings className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.openConfiguration', 'Open Configuration')}\n </Link>\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n asChild\n >\n <a href={buildPortalRootUrl(portalOrigin)} target=\"_blank\" rel=\"noopener noreferrer\">\n <Globe className=\"size-4\" />\n {t('customer_accounts.admin.portalInfo.open', 'Open Portal')}\n </a>\n </Button>\n </div>\n </div>\n </div>\n <DataTable<UserRow>\n stickyActionsColumn\n title={t('customer_accounts.admin.title', 'Users')}\n actions={(\n <Button onClick={() => setCreateDialogOpen(true)}>\n {t('customer_accounts.admin.actions.createUser', 'Create User')}\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.searchPlaceholder', 'Search by name or email...')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n perspective={{ tableId: extensionPoints.hosts.usersTable.tableId }}\n emptyState={(\n <ListEmptyState\n entityName={t('customer_accounts.admin.title', 'Users')}\n onCreate={() => setCreateDialogOpen(true)}\n createLabel={t('customer_accounts.admin.actions.createUser', 'Create User')}\n />\n )}\n onRowClick={(row) => router.push(`/backend/customer_accounts/users/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'view',\n label: t('customer_accounts.admin.actions.view', 'View'),\n onSelect: () => { router.push(`/backend/customer_accounts/users/${row.id}`) },\n },\n {\n id: 'toggle-active',\n label: row.isActive\n ? t('customer_accounts.admin.actions.deactivate', 'Deactivate')\n : t('customer_accounts.admin.actions.activate', 'Activate'),\n onSelect: () => { void handleToggleActive(row) },\n },\n {\n id: 'delete',\n label: t('customer_accounts.admin.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n },\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage }}\n isLoading={isLoading}\n />\n <CreateUserDialog\n open={createDialogOpen}\n onOpenChange={setCreateDialogOpen}\n roleOptions={roleOptions}\n onCreated={() => setReloadToken((token) => token + 1)}\n onRunMutation={runMutationWithContext}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,8 +7,15 @@ import {
|
|
|
7
7
|
import { Organization } from "@open-mercato/core/modules/directory/data/entities";
|
|
8
8
|
import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
|
|
9
9
|
import { normalizeHostname, tryNormalizeHostname } from "@open-mercato/core/modules/customer_accounts/lib/hostname";
|
|
10
|
+
import { findOrganizationInTenant } from "@open-mercato/core/modules/customer_accounts/lib/organizationLookup";
|
|
10
11
|
import { platformDomains } from "@open-mercato/core/modules/customer_accounts/lib/platformDomains";
|
|
11
12
|
import { detectProxy, isInKnownProxyRange } from "@open-mercato/core/modules/customer_accounts/lib/proxyRanges";
|
|
13
|
+
class DomainMappingOrgScopeError extends Error {
|
|
14
|
+
constructor(organizationId) {
|
|
15
|
+
super(`[internal] organizationId ${organizationId} does not belong to the caller's tenant`);
|
|
16
|
+
this.name = "DomainMappingOrgScopeError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
12
19
|
const DOMAIN_ROUTING_TAG = "domain_routing";
|
|
13
20
|
const RESOLVE_KEY_PREFIX = "domain_routing:resolve";
|
|
14
21
|
const ACTIVE_BY_ORG_KEY_PREFIX = "domain_routing:active-by-org";
|
|
@@ -186,6 +193,8 @@ class DomainMappingService {
|
|
|
186
193
|
// -------------------------------------------------------------------------
|
|
187
194
|
async register(input) {
|
|
188
195
|
const hostname = normalizeHostname(input.hostname);
|
|
196
|
+
const organization = await findOrganizationInTenant(this.em, input.organizationId, input.tenantId);
|
|
197
|
+
if (!organization) throw new DomainMappingOrgScopeError(input.organizationId);
|
|
189
198
|
let replacesDomain = null;
|
|
190
199
|
if (input.replacesDomainId) {
|
|
191
200
|
replacesDomain = await this.em.findOne(DomainMapping, {
|
|
@@ -491,6 +500,7 @@ const __testing__ = {
|
|
|
491
500
|
Resolver
|
|
492
501
|
};
|
|
493
502
|
export {
|
|
503
|
+
DomainMappingOrgScopeError,
|
|
494
504
|
DomainMappingService,
|
|
495
505
|
__testing__
|
|
496
506
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customer_accounts/services/domainMappingService.ts"],
|
|
4
|
-
"sourcesContent": ["import { Resolver, promises as dnsPromises } from 'node:dns'\nimport { request as httpsRequest } from 'node:https'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport {\n DomainMapping,\n type DomainStatus,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport { normalizeHostname, tryNormalizeHostname } from '@open-mercato/core/modules/customer_accounts/lib/hostname'\nimport { platformDomains } from '@open-mercato/core/modules/customer_accounts/lib/platformDomains'\nimport { detectProxy, isInKnownProxyRange } from '@open-mercato/core/modules/customer_accounts/lib/proxyRanges'\n\nconst DOMAIN_ROUTING_TAG = 'domain_routing'\nconst RESOLVE_KEY_PREFIX = 'domain_routing:resolve'\nconst ACTIVE_BY_ORG_KEY_PREFIX = 'domain_routing:active-by-org'\nconst RESOLVE_TTL_MS = 5 * 60_000\nconst TLS_HEALTH_CHECK_TIMEOUT_MS = 10_000\nconst TLS_HEALTH_CHECK_RETRY_DELAYS_MS = [1_000, 4_000, 16_000]\nconst DEFAULT_DNS_RECHECK_THRESHOLD_MS = 5 * 60_000\nconst DEFAULT_TLS_MAX_RETRIES = 6\n\nexport type ResolveResult = {\n domainMappingId: string\n hostname: string\n tenantId: string\n organizationId: string\n orgSlug: string | null\n status: DomainStatus\n}\n\nexport type DnsDiagnostics = {\n expectedCnameTarget: string\n expectedARecordTarget: string | null\n detectedRecords: Array<{ type: 'CNAME' | 'A'; value: string; proxy?: string }>\n reverseResolve?: { attempted: boolean; originHeaderPresent: boolean }\n suggestion: string\n}\n\nexport type VerifyResult = {\n domainMapping: DomainMapping\n diagnostics?: DnsDiagnostics\n}\n\nexport type RegisterInput = {\n hostname: string\n organizationId: string\n tenantId: string\n replacesDomainId?: string\n}\n\ntype CacheService = {\n get(key: string, options?: unknown): Promise<unknown>\n set(key: string, value: unknown, options?: { ttl?: number; tags?: string[] }): Promise<void>\n deleteByTags(tags: string[]): Promise<number>\n}\n\ntype DnsResolverContract = {\n resolveCname(hostname: string): Promise<string[]>\n resolve4(hostname: string): Promise<string[]>\n}\n\ntype HealthCheckContract = (hostname: string, timeoutMs: number) => Promise<{\n ok: boolean\n originHeaderPresent: boolean\n reason?: string\n}>\n\nconst defaultDnsResolver: DnsResolverContract = {\n async resolveCname(hostname) {\n try {\n return await dnsPromises.resolveCname(hostname)\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code\n if (code === 'ENODATA' || code === 'ENOTFOUND') return []\n throw err\n }\n },\n async resolve4(hostname) {\n try {\n return await dnsPromises.resolve4(hostname)\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code\n if (code === 'ENODATA' || code === 'ENOTFOUND') return []\n throw err\n }\n },\n}\n\nconst defaultHealthCheck: HealthCheckContract = (hostname, timeoutMs) =>\n new Promise((resolve) => {\n const headerName = (process.env.CUSTOMER_DOMAIN_ORIGIN_HEADER ?? 'X-Open-Mercato-Origin').toLowerCase()\n const req = httpsRequest(\n {\n host: hostname,\n port: 443,\n path: '/api/customer_accounts/domain-check',\n method: 'GET',\n headers: { 'X-Domain-Check-Secret': process.env.DOMAIN_CHECK_SECRET ?? '' },\n timeout: timeoutMs,\n },\n (res) => {\n const headerValue = res.headers[headerName]\n const originHeader = Array.isArray(headerValue) ? headerValue[0] : headerValue\n const originHeaderPresent = typeof originHeader === 'string' && originHeader === '1'\n const status = res.statusCode ?? 0\n res.resume() // discard body\n resolve({\n ok: status >= 200 && status < 400,\n originHeaderPresent,\n reason: status >= 200 && status < 400 ? undefined : `HTTP ${status}`,\n })\n },\n )\n req.on('timeout', () => {\n req.destroy(new Error('TLS health check timed out'))\n })\n req.on('error', (err) => {\n resolve({ ok: false, originHeaderPresent: false, reason: (err as Error).message })\n })\n req.end()\n })\n\nexport class DomainMappingService {\n private cache: CacheService | null\n private dns: DnsResolverContract\n private healthCheckImpl: HealthCheckContract\n\n constructor(\n private em: EntityManager,\n deps?: {\n cacheService?: CacheService\n dnsResolver?: DnsResolverContract\n healthCheck?: HealthCheckContract\n },\n ) {\n this.cache = deps?.cacheService ?? null\n this.dns = deps?.dnsResolver ?? defaultDnsResolver\n this.healthCheckImpl = deps?.healthCheck ?? defaultHealthCheck\n }\n\n // -------------------------------------------------------------------------\n // Read paths\n // -------------------------------------------------------------------------\n\n async findById(id: string, scope?: { tenantId?: string }): Promise<DomainMapping | null> {\n const where: Record<string, unknown> = { id }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n return this.em.findOne(DomainMapping, where as never)\n }\n\n async findByOrganization(\n organizationId: string,\n scope?: { tenantId?: string },\n ): Promise<DomainMapping[]> {\n const where: Record<string, unknown> = { organizationId }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n return this.em.find(DomainMapping, where as never, { orderBy: { createdAt: 'asc' } })\n }\n\n async resolveByHostname(input: string): Promise<ResolveResult | null> {\n const hostname = tryNormalizeHostname(input)\n if (!hostname) return null\n if (platformDomains().includes(hostname)) return null\n\n const cacheKey = `${RESOLVE_KEY_PREFIX}:${hostname}`\n if (this.cache) {\n const cached = (await this.cache.get(cacheKey)) as ResolveResult | null | undefined\n if (cached !== undefined && cached !== null) return cached\n }\n\n const result = await this.lookupResolveResult(hostname)\n if (this.cache) {\n await this.cache.set(cacheKey, result, {\n ttl: RESOLVE_TTL_MS,\n tags: [DOMAIN_ROUTING_TAG, `${DOMAIN_ROUTING_TAG}:${hostname}`],\n })\n }\n return result\n }\n\n async isAllowedForTls(input: string): Promise<{ organizationId: string; status: DomainStatus } | null> {\n const hostname = tryNormalizeHostname(input)\n if (!hostname) return null\n if (platformDomains().includes(hostname)) return null\n const row = await this.em.findOne(DomainMapping, {\n hostname,\n status: { $in: ['active', 'verified'] },\n } as never)\n if (!row) return null\n return { organizationId: row.organizationId, status: row.status }\n }\n\n async resolveActiveByOrg(organizationId: string): Promise<{ hostname: string; status: DomainStatus } | null> {\n const cacheKey = `${ACTIVE_BY_ORG_KEY_PREFIX}:${organizationId}`\n if (this.cache) {\n const cached = (await this.cache.get(cacheKey)) as { hostname: string; status: DomainStatus } | null | undefined\n if (cached !== undefined && cached !== null) return cached\n }\n\n const row = await this.em.findOne(DomainMapping, {\n organizationId,\n status: 'active',\n } as never)\n const result = row ? { hostname: row.hostname, status: row.status } : null\n\n if (this.cache) {\n await this.cache.set(cacheKey, result, {\n ttl: RESOLVE_TTL_MS,\n tags: [DOMAIN_ROUTING_TAG, `${DOMAIN_ROUTING_TAG}:org:${organizationId}`],\n })\n }\n return result\n }\n\n async resolveAll(): Promise<ResolveResult[]> {\n const rows = await this.em.find(DomainMapping, { status: 'active' } as never)\n if (rows.length === 0) return []\n const orgIds = Array.from(new Set(rows.map((r) => r.organizationId)))\n const orgs = await this.em.find(Organization, { id: { $in: orgIds } } as never)\n const slugByOrg = new Map<string, string | null>(orgs.map((o) => [o.id, o.slug ?? null]))\n\n return rows.map<ResolveResult>((row) => ({\n domainMappingId: row.id,\n hostname: row.hostname,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n orgSlug: slugByOrg.get(row.organizationId) ?? null,\n status: row.status,\n }))\n }\n\n // -------------------------------------------------------------------------\n // Worker queries\n // -------------------------------------------------------------------------\n\n async findPendingVerification(threshold?: { olderThanMs?: number }): Promise<DomainMapping[]> {\n const olderThan = threshold?.olderThanMs ?? DEFAULT_DNS_RECHECK_THRESHOLD_MS\n const cutoff = new Date(Date.now() - olderThan)\n return this.em.find(\n DomainMapping,\n {\n status: { $in: ['pending', 'dns_failed'] },\n $or: [{ lastDnsCheckAt: null }, { lastDnsCheckAt: { $lt: cutoff } }],\n } as never,\n { orderBy: { lastDnsCheckAt: 'asc' } },\n )\n }\n\n async findPendingTls(options?: { maxRetries?: number; batchSize?: number }): Promise<DomainMapping[]> {\n const maxRetries = options?.maxRetries ?? DEFAULT_TLS_MAX_RETRIES\n const limit = options?.batchSize ?? 50\n return this.em.find(\n DomainMapping,\n {\n $or: [\n { status: 'verified' },\n { status: 'tls_failed', tlsRetryCount: { $lt: maxRetries } },\n ],\n } as never,\n { orderBy: { updatedAt: 'asc' }, limit },\n )\n }\n\n // -------------------------------------------------------------------------\n // Write paths\n // -------------------------------------------------------------------------\n\n async register(input: RegisterInput): Promise<DomainMapping> {\n const hostname = normalizeHostname(input.hostname)\n\n let replacesDomain: DomainMapping | null = null\n if (input.replacesDomainId) {\n replacesDomain = await this.em.findOne(DomainMapping, {\n id: input.replacesDomainId,\n tenantId: input.tenantId,\n } as never)\n }\n\n const entity = this.em.create(DomainMapping, {\n hostname,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n replacesDomain: replacesDomain ?? null,\n provider: 'traefik',\n status: 'pending',\n tlsRetryCount: 0,\n createdAt: new Date(),\n } as never)\n await this.em.persist(entity).flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.created', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n status: entity.status,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n\n return entity\n }\n\n async verify(id: string): Promise<VerifyResult> {\n const entity = await this.em.findOne(DomainMapping, { id } as never)\n if (!entity) throw new Error(`DomainMapping ${id} not found`)\n\n const expectedCname = process.env.CUSTOM_DOMAIN_CNAME_TARGET ?? ''\n const expectedARecord = process.env.CUSTOM_DOMAIN_A_RECORD_TARGET ?? null\n const now = new Date()\n entity.lastDnsCheckAt = now\n\n const verification = await this.runDnsVerification(entity.hostname, {\n expectedCname,\n expectedARecord,\n })\n\n if (verification.ok) {\n entity.status = 'verified'\n entity.verifiedAt = now\n entity.dnsFailureReason = null\n entity.tlsRetryCount = 0\n entity.tlsFailureReason = null\n await this.em.persist(entity).flush()\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.verified', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return { domainMapping: entity }\n }\n\n entity.status = 'dns_failed'\n entity.dnsFailureReason = verification.reason\n await this.em.persist(entity).flush()\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.dns_failed', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n reason: verification.reason,\n detectedRecords: verification.diagnostics.detectedRecords,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return { domainMapping: entity, diagnostics: verification.diagnostics }\n }\n\n async activate(id: string): Promise<DomainMapping> {\n const entity = await this.em.findOne(DomainMapping, { id } as never)\n if (!entity) throw new Error(`DomainMapping ${id} not found`)\n if (entity.status === 'active') return entity\n if (entity.status !== 'verified' && entity.status !== 'tls_failed') {\n throw new Error(`DomainMapping ${id} cannot transition to active from ${entity.status}`)\n }\n\n entity.status = 'active'\n entity.tlsRetryCount = 0\n entity.tlsFailureReason = null\n await this.em.persist(entity).flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.activated', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n } as never)\n\n if (entity.replacesDomain) {\n const replaced = await this.em.findOne(DomainMapping, {\n id: (entity.replacesDomain as unknown as { id: string }).id,\n } as never)\n if (replaced) {\n const replacedHostname = replaced.hostname\n const replacedOrg = replaced.organizationId\n this.em.remove(replaced)\n await this.em.flush()\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.replaced', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n replacedDomainId: replaced.id,\n replacedHostname,\n } as never)\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.deleted', {\n id: replaced.id,\n hostname: replacedHostname,\n organizationId: replacedOrg,\n tenantId: replaced.tenantId,\n } as never)\n await this.invalidateCacheFor(replacedHostname, replacedOrg)\n }\n }\n\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return entity\n }\n\n async remove(id: string, scope?: { tenantId?: string }): Promise<void> {\n const where: Record<string, unknown> = { id }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n const entity = await this.em.findOne(DomainMapping, where as never)\n if (!entity) return\n\n const hostname = entity.hostname\n const organizationId = entity.organizationId\n const tenantId = entity.tenantId\n\n this.em.remove(entity)\n await this.em.flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.deleted', {\n id,\n hostname,\n organizationId,\n tenantId,\n } as never)\n await this.invalidateCacheFor(hostname, organizationId)\n }\n\n async healthCheck(id: string): Promise<DomainMapping> {\n const entity = await this.em.findOne(DomainMapping, { id } as never)\n if (!entity) throw new Error(`DomainMapping ${id} not found`)\n if (entity.status === 'active') return entity\n if (entity.status !== 'verified' && entity.status !== 'tls_failed') {\n throw new Error(`DomainMapping ${id} cannot run health check from status ${entity.status}`)\n }\n\n let lastReason: string | null = null\n for (let attempt = 0; attempt < TLS_HEALTH_CHECK_RETRY_DELAYS_MS.length; attempt++) {\n const result = await this.healthCheckImpl(entity.hostname, TLS_HEALTH_CHECK_TIMEOUT_MS)\n if (result.ok) {\n return this.activate(entity.id)\n }\n lastReason = result.reason ?? 'TLS health check failed'\n if (attempt + 1 < TLS_HEALTH_CHECK_RETRY_DELAYS_MS.length) {\n await delay(TLS_HEALTH_CHECK_RETRY_DELAYS_MS[attempt])\n }\n }\n\n entity.status = 'tls_failed'\n entity.tlsFailureReason = lastReason ?? 'TLS health check failed'\n entity.tlsRetryCount = (entity.tlsRetryCount ?? 0) + 1\n await this.em.persist(entity).flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.tls_failed', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n reason: entity.tlsFailureReason,\n retryCount: entity.tlsRetryCount,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return entity\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n private async lookupResolveResult(hostname: string): Promise<ResolveResult | null> {\n const row = await this.em.findOne(DomainMapping, { hostname, status: 'active' } as never)\n if (!row) return null\n const org = await this.em.findOne(Organization, { id: row.organizationId } as never)\n return {\n domainMappingId: row.id,\n hostname: row.hostname,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n orgSlug: org?.slug ?? null,\n status: row.status,\n }\n }\n\n private async invalidateCacheFor(hostname: string, organizationId: string | null): Promise<void> {\n if (!this.cache) return\n const tags = [DOMAIN_ROUTING_TAG, `${DOMAIN_ROUTING_TAG}:${hostname}`]\n if (organizationId) tags.push(`${DOMAIN_ROUTING_TAG}:org:${organizationId}`)\n try {\n await this.cache.deleteByTags(tags)\n } catch {\n // Cache invalidation is best-effort \u2014 TTL backstop ensures eventual consistency.\n }\n }\n\n private async runDnsVerification(\n hostname: string,\n expected: { expectedCname: string; expectedARecord: string | null },\n ): Promise<\n | {\n ok: true\n method: 'cname' | 'a-record' | 'reverse-resolve'\n diagnostics: DnsDiagnostics\n }\n | { ok: false; reason: string; diagnostics: DnsDiagnostics }\n > {\n const detectedRecords: DnsDiagnostics['detectedRecords'] = []\n const baseDiag = (overrides?: Partial<DnsDiagnostics>): DnsDiagnostics => ({\n expectedCnameTarget: expected.expectedCname,\n expectedARecordTarget: expected.expectedARecord,\n detectedRecords,\n suggestion: overrides?.suggestion ?? '',\n reverseResolve: overrides?.reverseResolve,\n })\n\n // Phase 1: CNAME\n let cnameRecords: string[] = []\n try {\n cnameRecords = await this.dns.resolveCname(hostname)\n } catch (err) {\n return {\n ok: false,\n reason: `DNS lookup error (CNAME): ${(err as Error).message}`,\n diagnostics: baseDiag({\n suggestion: 'DNS lookup failed unexpectedly. Try again in a few minutes.',\n }),\n }\n }\n for (const cname of cnameRecords) detectedRecords.push({ type: 'CNAME', value: cname })\n\n if (expected.expectedCname && cnameRecords.length > 0) {\n const expectedCname = tryNormalizeHostname(expected.expectedCname) ?? expected.expectedCname.toLowerCase()\n const match = cnameRecords.some((c) => (tryNormalizeHostname(c) ?? c.toLowerCase()) === expectedCname)\n if (match) {\n return {\n ok: true,\n method: 'cname',\n diagnostics: baseDiag({\n suggestion: 'CNAME record matches the expected target.',\n }),\n }\n }\n return {\n ok: false,\n reason: `CNAME points to ${cnameRecords.join(', ')} instead of ${expected.expectedCname}`,\n diagnostics: baseDiag({\n suggestion: `Update your CNAME record to point to ${expected.expectedCname}.`,\n }),\n }\n }\n\n // Phase 2: A record\n let aRecords: string[] = []\n try {\n aRecords = await this.dns.resolve4(hostname)\n } catch (err) {\n return {\n ok: false,\n reason: `DNS lookup error (A): ${(err as Error).message}`,\n diagnostics: baseDiag({ suggestion: 'DNS lookup failed unexpectedly. Try again in a few minutes.' }),\n }\n }\n for (const a of aRecords) {\n const proxy = detectProxy(a)\n detectedRecords.push({ type: 'A', value: a, ...(proxy ? { proxy } : {}) })\n }\n\n if (aRecords.length === 0) {\n return {\n ok: false,\n reason: `No CNAME or A record found for ${hostname}`,\n diagnostics: baseDiag({\n suggestion: expected.expectedARecord\n ? `For a subdomain, add a CNAME record pointing to ${expected.expectedCname}. For an apex domain, add an A record pointing to ${expected.expectedARecord}. DNS propagation can take up to 48 hours.`\n : `Add a CNAME record pointing to ${expected.expectedCname}. DNS propagation can take up to 48 hours.`,\n }),\n }\n }\n\n if (expected.expectedARecord && aRecords.includes(expected.expectedARecord)) {\n return {\n ok: true,\n method: 'a-record',\n diagnostics: baseDiag({ suggestion: 'A record matches the expected target.' }),\n }\n }\n\n // Phase 3: reverse-resolve through proxy\n const proxiedRecords = aRecords.filter((ip) => isInKnownProxyRange(ip))\n if (proxiedRecords.length > 0) {\n const probe = await this.healthCheckImpl(hostname, TLS_HEALTH_CHECK_TIMEOUT_MS)\n if (probe.ok && probe.originHeaderPresent) {\n return {\n ok: true,\n method: 'reverse-resolve',\n diagnostics: baseDiag({\n reverseResolve: { attempted: true, originHeaderPresent: true },\n suggestion: 'Domain is proxied \u2014 reverse-resolve confirmed traffic reaches our origin.',\n }),\n }\n }\n return {\n ok: false,\n reason: 'A record points to a known proxy IP, but reverse-resolve over HTTPS did not reach our server',\n diagnostics: baseDiag({\n reverseResolve: { attempted: true, originHeaderPresent: false },\n suggestion: expected.expectedCname\n ? `Your DNS uses a proxy. Either disable the proxy and add a CNAME \u2192 ${expected.expectedCname}, or configure your proxy to forward traffic to ${expected.expectedCname}.`\n : 'Disable the DNS proxy or configure it to forward traffic to our platform.',\n }),\n }\n }\n\n return {\n ok: false,\n reason: expected.expectedARecord\n ? `A record points to ${aRecords.join(', ')} instead of ${expected.expectedARecord}`\n : `A record points to ${aRecords.join(', ')} but apex-domain registration is not enabled on this deployment`,\n diagnostics: baseDiag({\n suggestion: expected.expectedARecord\n ? `Update your A record to point to ${expected.expectedARecord}.`\n : `Apex-domain registration is not enabled on this deployment. Use a subdomain (e.g., shop.${hostname}) and add a CNAME pointing to ${expected.expectedCname}.`,\n }),\n }\n }\n}\n\n// Re-exported for tests; allow injection of fakes.\nexport const __testing__ = {\n DEFAULT_DNS_RECHECK_THRESHOLD_MS,\n DEFAULT_TLS_MAX_RETRIES,\n TLS_HEALTH_CHECK_RETRY_DELAYS_MS,\n RESOLVE_TTL_MS,\n DOMAIN_ROUTING_TAG,\n defaultHealthCheck,\n defaultDnsResolver,\n Resolver,\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { Resolver, promises as dnsPromises } from 'node:dns'\nimport { request as httpsRequest } from 'node:https'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport {\n DomainMapping,\n type DomainStatus,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport { normalizeHostname, tryNormalizeHostname } from '@open-mercato/core/modules/customer_accounts/lib/hostname'\nimport { findOrganizationInTenant } from '@open-mercato/core/modules/customer_accounts/lib/organizationLookup'\nimport { platformDomains } from '@open-mercato/core/modules/customer_accounts/lib/platformDomains'\nimport { detectProxy, isInKnownProxyRange } from '@open-mercato/core/modules/customer_accounts/lib/proxyRanges'\n\nexport class DomainMappingOrgScopeError extends Error {\n constructor(organizationId: string) {\n super(`[internal] organizationId ${organizationId} does not belong to the caller's tenant`)\n this.name = 'DomainMappingOrgScopeError'\n }\n}\n\nconst DOMAIN_ROUTING_TAG = 'domain_routing'\nconst RESOLVE_KEY_PREFIX = 'domain_routing:resolve'\nconst ACTIVE_BY_ORG_KEY_PREFIX = 'domain_routing:active-by-org'\nconst RESOLVE_TTL_MS = 5 * 60_000\nconst TLS_HEALTH_CHECK_TIMEOUT_MS = 10_000\nconst TLS_HEALTH_CHECK_RETRY_DELAYS_MS = [1_000, 4_000, 16_000]\nconst DEFAULT_DNS_RECHECK_THRESHOLD_MS = 5 * 60_000\nconst DEFAULT_TLS_MAX_RETRIES = 6\n\nexport type ResolveResult = {\n domainMappingId: string\n hostname: string\n tenantId: string\n organizationId: string\n orgSlug: string | null\n status: DomainStatus\n}\n\nexport type DnsDiagnostics = {\n expectedCnameTarget: string\n expectedARecordTarget: string | null\n detectedRecords: Array<{ type: 'CNAME' | 'A'; value: string; proxy?: string }>\n reverseResolve?: { attempted: boolean; originHeaderPresent: boolean }\n suggestion: string\n}\n\nexport type VerifyResult = {\n domainMapping: DomainMapping\n diagnostics?: DnsDiagnostics\n}\n\nexport type RegisterInput = {\n hostname: string\n organizationId: string\n tenantId: string\n replacesDomainId?: string\n}\n\ntype CacheService = {\n get(key: string, options?: unknown): Promise<unknown>\n set(key: string, value: unknown, options?: { ttl?: number; tags?: string[] }): Promise<void>\n deleteByTags(tags: string[]): Promise<number>\n}\n\ntype DnsResolverContract = {\n resolveCname(hostname: string): Promise<string[]>\n resolve4(hostname: string): Promise<string[]>\n}\n\ntype HealthCheckContract = (hostname: string, timeoutMs: number) => Promise<{\n ok: boolean\n originHeaderPresent: boolean\n reason?: string\n}>\n\nconst defaultDnsResolver: DnsResolverContract = {\n async resolveCname(hostname) {\n try {\n return await dnsPromises.resolveCname(hostname)\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code\n if (code === 'ENODATA' || code === 'ENOTFOUND') return []\n throw err\n }\n },\n async resolve4(hostname) {\n try {\n return await dnsPromises.resolve4(hostname)\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code\n if (code === 'ENODATA' || code === 'ENOTFOUND') return []\n throw err\n }\n },\n}\n\nconst defaultHealthCheck: HealthCheckContract = (hostname, timeoutMs) =>\n new Promise((resolve) => {\n const headerName = (process.env.CUSTOMER_DOMAIN_ORIGIN_HEADER ?? 'X-Open-Mercato-Origin').toLowerCase()\n const req = httpsRequest(\n {\n host: hostname,\n port: 443,\n path: '/api/customer_accounts/domain-check',\n method: 'GET',\n headers: { 'X-Domain-Check-Secret': process.env.DOMAIN_CHECK_SECRET ?? '' },\n timeout: timeoutMs,\n },\n (res) => {\n const headerValue = res.headers[headerName]\n const originHeader = Array.isArray(headerValue) ? headerValue[0] : headerValue\n const originHeaderPresent = typeof originHeader === 'string' && originHeader === '1'\n const status = res.statusCode ?? 0\n res.resume() // discard body\n resolve({\n ok: status >= 200 && status < 400,\n originHeaderPresent,\n reason: status >= 200 && status < 400 ? undefined : `HTTP ${status}`,\n })\n },\n )\n req.on('timeout', () => {\n req.destroy(new Error('TLS health check timed out'))\n })\n req.on('error', (err) => {\n resolve({ ok: false, originHeaderPresent: false, reason: (err as Error).message })\n })\n req.end()\n })\n\nexport class DomainMappingService {\n private cache: CacheService | null\n private dns: DnsResolverContract\n private healthCheckImpl: HealthCheckContract\n\n constructor(\n private em: EntityManager,\n deps?: {\n cacheService?: CacheService\n dnsResolver?: DnsResolverContract\n healthCheck?: HealthCheckContract\n },\n ) {\n this.cache = deps?.cacheService ?? null\n this.dns = deps?.dnsResolver ?? defaultDnsResolver\n this.healthCheckImpl = deps?.healthCheck ?? defaultHealthCheck\n }\n\n // -------------------------------------------------------------------------\n // Read paths\n // -------------------------------------------------------------------------\n\n async findById(id: string, scope?: { tenantId?: string }): Promise<DomainMapping | null> {\n const where: Record<string, unknown> = { id }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n return this.em.findOne(DomainMapping, where as never)\n }\n\n async findByOrganization(\n organizationId: string,\n scope?: { tenantId?: string },\n ): Promise<DomainMapping[]> {\n const where: Record<string, unknown> = { organizationId }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n return this.em.find(DomainMapping, where as never, { orderBy: { createdAt: 'asc' } })\n }\n\n async resolveByHostname(input: string): Promise<ResolveResult | null> {\n const hostname = tryNormalizeHostname(input)\n if (!hostname) return null\n if (platformDomains().includes(hostname)) return null\n\n const cacheKey = `${RESOLVE_KEY_PREFIX}:${hostname}`\n if (this.cache) {\n const cached = (await this.cache.get(cacheKey)) as ResolveResult | null | undefined\n if (cached !== undefined && cached !== null) return cached\n }\n\n const result = await this.lookupResolveResult(hostname)\n if (this.cache) {\n await this.cache.set(cacheKey, result, {\n ttl: RESOLVE_TTL_MS,\n tags: [DOMAIN_ROUTING_TAG, `${DOMAIN_ROUTING_TAG}:${hostname}`],\n })\n }\n return result\n }\n\n async isAllowedForTls(input: string): Promise<{ organizationId: string; status: DomainStatus } | null> {\n const hostname = tryNormalizeHostname(input)\n if (!hostname) return null\n if (platformDomains().includes(hostname)) return null\n const row = await this.em.findOne(DomainMapping, {\n hostname,\n status: { $in: ['active', 'verified'] },\n } as never)\n if (!row) return null\n return { organizationId: row.organizationId, status: row.status }\n }\n\n async resolveActiveByOrg(organizationId: string): Promise<{ hostname: string; status: DomainStatus } | null> {\n const cacheKey = `${ACTIVE_BY_ORG_KEY_PREFIX}:${organizationId}`\n if (this.cache) {\n const cached = (await this.cache.get(cacheKey)) as { hostname: string; status: DomainStatus } | null | undefined\n if (cached !== undefined && cached !== null) return cached\n }\n\n const row = await this.em.findOne(DomainMapping, {\n organizationId,\n status: 'active',\n } as never)\n const result = row ? { hostname: row.hostname, status: row.status } : null\n\n if (this.cache) {\n await this.cache.set(cacheKey, result, {\n ttl: RESOLVE_TTL_MS,\n tags: [DOMAIN_ROUTING_TAG, `${DOMAIN_ROUTING_TAG}:org:${organizationId}`],\n })\n }\n return result\n }\n\n async resolveAll(): Promise<ResolveResult[]> {\n const rows = await this.em.find(DomainMapping, { status: 'active' } as never)\n if (rows.length === 0) return []\n const orgIds = Array.from(new Set(rows.map((r) => r.organizationId)))\n const orgs = await this.em.find(Organization, { id: { $in: orgIds } } as never)\n const slugByOrg = new Map<string, string | null>(orgs.map((o) => [o.id, o.slug ?? null]))\n\n return rows.map<ResolveResult>((row) => ({\n domainMappingId: row.id,\n hostname: row.hostname,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n orgSlug: slugByOrg.get(row.organizationId) ?? null,\n status: row.status,\n }))\n }\n\n // -------------------------------------------------------------------------\n // Worker queries\n // -------------------------------------------------------------------------\n\n async findPendingVerification(threshold?: { olderThanMs?: number }): Promise<DomainMapping[]> {\n const olderThan = threshold?.olderThanMs ?? DEFAULT_DNS_RECHECK_THRESHOLD_MS\n const cutoff = new Date(Date.now() - olderThan)\n return this.em.find(\n DomainMapping,\n {\n status: { $in: ['pending', 'dns_failed'] },\n $or: [{ lastDnsCheckAt: null }, { lastDnsCheckAt: { $lt: cutoff } }],\n } as never,\n { orderBy: { lastDnsCheckAt: 'asc' } },\n )\n }\n\n async findPendingTls(options?: { maxRetries?: number; batchSize?: number }): Promise<DomainMapping[]> {\n const maxRetries = options?.maxRetries ?? DEFAULT_TLS_MAX_RETRIES\n const limit = options?.batchSize ?? 50\n return this.em.find(\n DomainMapping,\n {\n $or: [\n { status: 'verified' },\n { status: 'tls_failed', tlsRetryCount: { $lt: maxRetries } },\n ],\n } as never,\n { orderBy: { updatedAt: 'asc' }, limit },\n )\n }\n\n // -------------------------------------------------------------------------\n // Write paths\n // -------------------------------------------------------------------------\n\n async register(input: RegisterInput): Promise<DomainMapping> {\n const hostname = normalizeHostname(input.hostname)\n const organization = await findOrganizationInTenant(this.em, input.organizationId, input.tenantId)\n if (!organization) throw new DomainMappingOrgScopeError(input.organizationId)\n\n let replacesDomain: DomainMapping | null = null\n if (input.replacesDomainId) {\n replacesDomain = await this.em.findOne(DomainMapping, {\n id: input.replacesDomainId,\n tenantId: input.tenantId,\n } as never)\n }\n\n const entity = this.em.create(DomainMapping, {\n hostname,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n replacesDomain: replacesDomain ?? null,\n provider: 'traefik',\n status: 'pending',\n tlsRetryCount: 0,\n createdAt: new Date(),\n } as never)\n await this.em.persist(entity).flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.created', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n status: entity.status,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n\n return entity\n }\n\n async verify(id: string): Promise<VerifyResult> {\n const entity = await this.em.findOne(DomainMapping, { id } as never)\n if (!entity) throw new Error(`DomainMapping ${id} not found`)\n\n const expectedCname = process.env.CUSTOM_DOMAIN_CNAME_TARGET ?? ''\n const expectedARecord = process.env.CUSTOM_DOMAIN_A_RECORD_TARGET ?? null\n const now = new Date()\n entity.lastDnsCheckAt = now\n\n const verification = await this.runDnsVerification(entity.hostname, {\n expectedCname,\n expectedARecord,\n })\n\n if (verification.ok) {\n entity.status = 'verified'\n entity.verifiedAt = now\n entity.dnsFailureReason = null\n entity.tlsRetryCount = 0\n entity.tlsFailureReason = null\n await this.em.persist(entity).flush()\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.verified', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return { domainMapping: entity }\n }\n\n entity.status = 'dns_failed'\n entity.dnsFailureReason = verification.reason\n await this.em.persist(entity).flush()\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.dns_failed', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n reason: verification.reason,\n detectedRecords: verification.diagnostics.detectedRecords,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return { domainMapping: entity, diagnostics: verification.diagnostics }\n }\n\n async activate(id: string): Promise<DomainMapping> {\n const entity = await this.em.findOne(DomainMapping, { id } as never)\n if (!entity) throw new Error(`DomainMapping ${id} not found`)\n if (entity.status === 'active') return entity\n if (entity.status !== 'verified' && entity.status !== 'tls_failed') {\n throw new Error(`DomainMapping ${id} cannot transition to active from ${entity.status}`)\n }\n\n entity.status = 'active'\n entity.tlsRetryCount = 0\n entity.tlsFailureReason = null\n await this.em.persist(entity).flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.activated', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n } as never)\n\n if (entity.replacesDomain) {\n const replaced = await this.em.findOne(DomainMapping, {\n id: (entity.replacesDomain as unknown as { id: string }).id,\n } as never)\n if (replaced) {\n const replacedHostname = replaced.hostname\n const replacedOrg = replaced.organizationId\n this.em.remove(replaced)\n await this.em.flush()\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.replaced', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n replacedDomainId: replaced.id,\n replacedHostname,\n } as never)\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.deleted', {\n id: replaced.id,\n hostname: replacedHostname,\n organizationId: replacedOrg,\n tenantId: replaced.tenantId,\n } as never)\n await this.invalidateCacheFor(replacedHostname, replacedOrg)\n }\n }\n\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return entity\n }\n\n async remove(id: string, scope?: { tenantId?: string }): Promise<void> {\n const where: Record<string, unknown> = { id }\n if (scope?.tenantId) where.tenantId = scope.tenantId\n const entity = await this.em.findOne(DomainMapping, where as never)\n if (!entity) return\n\n const hostname = entity.hostname\n const organizationId = entity.organizationId\n const tenantId = entity.tenantId\n\n this.em.remove(entity)\n await this.em.flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.deleted', {\n id,\n hostname,\n organizationId,\n tenantId,\n } as never)\n await this.invalidateCacheFor(hostname, organizationId)\n }\n\n async healthCheck(id: string): Promise<DomainMapping> {\n const entity = await this.em.findOne(DomainMapping, { id } as never)\n if (!entity) throw new Error(`DomainMapping ${id} not found`)\n if (entity.status === 'active') return entity\n if (entity.status !== 'verified' && entity.status !== 'tls_failed') {\n throw new Error(`DomainMapping ${id} cannot run health check from status ${entity.status}`)\n }\n\n let lastReason: string | null = null\n for (let attempt = 0; attempt < TLS_HEALTH_CHECK_RETRY_DELAYS_MS.length; attempt++) {\n const result = await this.healthCheckImpl(entity.hostname, TLS_HEALTH_CHECK_TIMEOUT_MS)\n if (result.ok) {\n return this.activate(entity.id)\n }\n lastReason = result.reason ?? 'TLS health check failed'\n if (attempt + 1 < TLS_HEALTH_CHECK_RETRY_DELAYS_MS.length) {\n await delay(TLS_HEALTH_CHECK_RETRY_DELAYS_MS[attempt])\n }\n }\n\n entity.status = 'tls_failed'\n entity.tlsFailureReason = lastReason ?? 'TLS health check failed'\n entity.tlsRetryCount = (entity.tlsRetryCount ?? 0) + 1\n await this.em.persist(entity).flush()\n\n await emitCustomerAccountsEvent('customer_accounts.domain_mapping.tls_failed', {\n id: entity.id,\n hostname: entity.hostname,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n reason: entity.tlsFailureReason,\n retryCount: entity.tlsRetryCount,\n } as never)\n await this.invalidateCacheFor(entity.hostname, entity.organizationId)\n return entity\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n private async lookupResolveResult(hostname: string): Promise<ResolveResult | null> {\n const row = await this.em.findOne(DomainMapping, { hostname, status: 'active' } as never)\n if (!row) return null\n const org = await this.em.findOne(Organization, { id: row.organizationId } as never)\n return {\n domainMappingId: row.id,\n hostname: row.hostname,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n orgSlug: org?.slug ?? null,\n status: row.status,\n }\n }\n\n private async invalidateCacheFor(hostname: string, organizationId: string | null): Promise<void> {\n if (!this.cache) return\n const tags = [DOMAIN_ROUTING_TAG, `${DOMAIN_ROUTING_TAG}:${hostname}`]\n if (organizationId) tags.push(`${DOMAIN_ROUTING_TAG}:org:${organizationId}`)\n try {\n await this.cache.deleteByTags(tags)\n } catch {\n // Cache invalidation is best-effort \u2014 TTL backstop ensures eventual consistency.\n }\n }\n\n private async runDnsVerification(\n hostname: string,\n expected: { expectedCname: string; expectedARecord: string | null },\n ): Promise<\n | {\n ok: true\n method: 'cname' | 'a-record' | 'reverse-resolve'\n diagnostics: DnsDiagnostics\n }\n | { ok: false; reason: string; diagnostics: DnsDiagnostics }\n > {\n const detectedRecords: DnsDiagnostics['detectedRecords'] = []\n const baseDiag = (overrides?: Partial<DnsDiagnostics>): DnsDiagnostics => ({\n expectedCnameTarget: expected.expectedCname,\n expectedARecordTarget: expected.expectedARecord,\n detectedRecords,\n suggestion: overrides?.suggestion ?? '',\n reverseResolve: overrides?.reverseResolve,\n })\n\n // Phase 1: CNAME\n let cnameRecords: string[] = []\n try {\n cnameRecords = await this.dns.resolveCname(hostname)\n } catch (err) {\n return {\n ok: false,\n reason: `DNS lookup error (CNAME): ${(err as Error).message}`,\n diagnostics: baseDiag({\n suggestion: 'DNS lookup failed unexpectedly. Try again in a few minutes.',\n }),\n }\n }\n for (const cname of cnameRecords) detectedRecords.push({ type: 'CNAME', value: cname })\n\n if (expected.expectedCname && cnameRecords.length > 0) {\n const expectedCname = tryNormalizeHostname(expected.expectedCname) ?? expected.expectedCname.toLowerCase()\n const match = cnameRecords.some((c) => (tryNormalizeHostname(c) ?? c.toLowerCase()) === expectedCname)\n if (match) {\n return {\n ok: true,\n method: 'cname',\n diagnostics: baseDiag({\n suggestion: 'CNAME record matches the expected target.',\n }),\n }\n }\n return {\n ok: false,\n reason: `CNAME points to ${cnameRecords.join(', ')} instead of ${expected.expectedCname}`,\n diagnostics: baseDiag({\n suggestion: `Update your CNAME record to point to ${expected.expectedCname}.`,\n }),\n }\n }\n\n // Phase 2: A record\n let aRecords: string[] = []\n try {\n aRecords = await this.dns.resolve4(hostname)\n } catch (err) {\n return {\n ok: false,\n reason: `DNS lookup error (A): ${(err as Error).message}`,\n diagnostics: baseDiag({ suggestion: 'DNS lookup failed unexpectedly. Try again in a few minutes.' }),\n }\n }\n for (const a of aRecords) {\n const proxy = detectProxy(a)\n detectedRecords.push({ type: 'A', value: a, ...(proxy ? { proxy } : {}) })\n }\n\n if (aRecords.length === 0) {\n return {\n ok: false,\n reason: `No CNAME or A record found for ${hostname}`,\n diagnostics: baseDiag({\n suggestion: expected.expectedARecord\n ? `For a subdomain, add a CNAME record pointing to ${expected.expectedCname}. For an apex domain, add an A record pointing to ${expected.expectedARecord}. DNS propagation can take up to 48 hours.`\n : `Add a CNAME record pointing to ${expected.expectedCname}. DNS propagation can take up to 48 hours.`,\n }),\n }\n }\n\n if (expected.expectedARecord && aRecords.includes(expected.expectedARecord)) {\n return {\n ok: true,\n method: 'a-record',\n diagnostics: baseDiag({ suggestion: 'A record matches the expected target.' }),\n }\n }\n\n // Phase 3: reverse-resolve through proxy\n const proxiedRecords = aRecords.filter((ip) => isInKnownProxyRange(ip))\n if (proxiedRecords.length > 0) {\n const probe = await this.healthCheckImpl(hostname, TLS_HEALTH_CHECK_TIMEOUT_MS)\n if (probe.ok && probe.originHeaderPresent) {\n return {\n ok: true,\n method: 'reverse-resolve',\n diagnostics: baseDiag({\n reverseResolve: { attempted: true, originHeaderPresent: true },\n suggestion: 'Domain is proxied \u2014 reverse-resolve confirmed traffic reaches our origin.',\n }),\n }\n }\n return {\n ok: false,\n reason: 'A record points to a known proxy IP, but reverse-resolve over HTTPS did not reach our server',\n diagnostics: baseDiag({\n reverseResolve: { attempted: true, originHeaderPresent: false },\n suggestion: expected.expectedCname\n ? `Your DNS uses a proxy. Either disable the proxy and add a CNAME \u2192 ${expected.expectedCname}, or configure your proxy to forward traffic to ${expected.expectedCname}.`\n : 'Disable the DNS proxy or configure it to forward traffic to our platform.',\n }),\n }\n }\n\n return {\n ok: false,\n reason: expected.expectedARecord\n ? `A record points to ${aRecords.join(', ')} instead of ${expected.expectedARecord}`\n : `A record points to ${aRecords.join(', ')} but apex-domain registration is not enabled on this deployment`,\n diagnostics: baseDiag({\n suggestion: expected.expectedARecord\n ? `Update your A record to point to ${expected.expectedARecord}.`\n : `Apex-domain registration is not enabled on this deployment. Use a subdomain (e.g., shop.${hostname}) and add a CNAME pointing to ${expected.expectedCname}.`,\n }),\n }\n }\n}\n\n// Re-exported for tests; allow injection of fakes.\nexport const __testing__ = {\n DEFAULT_DNS_RECHECK_THRESHOLD_MS,\n DEFAULT_TLS_MAX_RETRIES,\n TLS_HEALTH_CHECK_RETRY_DELAYS_MS,\n RESOLVE_TTL_MS,\n DOMAIN_ROUTING_TAG,\n defaultHealthCheck,\n defaultDnsResolver,\n Resolver,\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -18,7 +18,7 @@ const listCompaniesInput = z.object({
|
|
|
18
18
|
const listCompaniesTool = defineApiBackedAiTool({
|
|
19
19
|
name: "customers.list_companies",
|
|
20
20
|
displayName: "List companies",
|
|
21
|
-
description:
|
|
21
|
+
description: 'Search / list companies for the caller tenant + organization. Returns { items, total, totalIsCapped, limit, offset }. When totalIsCapped is true, total is a floor ("at least N", render it as "N+"), and pagination is exhausted only when a page returns fewer than limit items \u2014 never when offset reaches total.',
|
|
22
22
|
inputSchema: listCompaniesInput,
|
|
23
23
|
requiredFeatures: ["customers.companies.view"],
|
|
24
24
|
toOperation: (input, ctx) => {
|
|
@@ -53,6 +53,9 @@ const listCompaniesTool = defineApiBackedAiTool({
|
|
|
53
53
|
sizeBucket: row.size_bucket ?? row.sizeBucket ?? null
|
|
54
54
|
})),
|
|
55
55
|
total: typeof data.total === "number" ? data.total : 0,
|
|
56
|
+
// A capped count reports a floor: phrase the total as "at least N" and
|
|
57
|
+
// never treat it as proof that pagination is exhausted.
|
|
58
|
+
totalIsCapped: data.totalIsCapped === true,
|
|
56
59
|
limit,
|
|
57
60
|
offset
|
|
58
61
|
};
|