@open-mercato/core 0.4.2-canary-ed15f2e753 → 0.4.2-canary-e2aeb1a7bf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/entities/notification/index.js +57 -0
- package/dist/generated/entities/notification/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +5 -2
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -2
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js +3 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +1 -1
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/api_keys/setup.js +11 -0
- package/dist/modules/api_keys/setup.js.map +7 -0
- package/dist/modules/attachments/components/AttachmentLibrary.js +5 -1
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +2 -0
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/attachments/lib/assignmentDetails.js +31 -17
- package/dist/modules/attachments/lib/assignmentDetails.js.map +2 -2
- package/dist/modules/attachments/lib/partitions.js +3 -3
- package/dist/modules/attachments/lib/partitions.js.map +2 -2
- package/dist/modules/attachments/setup.js +11 -0
- package/dist/modules/attachments/setup.js.map +7 -0
- package/dist/modules/audit_logs/setup.js +12 -0
- package/dist/modules/audit_logs/setup.js.map +7 -0
- package/dist/modules/auth/api/admin/nav.js +4 -3
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/login.js +25 -6
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +157 -0
- package/dist/modules/auth/api/profile/route.js.map +7 -0
- package/dist/modules/auth/api/reset/confirm.js +25 -2
- package/dist/modules/auth/api/reset/confirm.js.map +2 -2
- package/dist/modules/auth/api/reset.js +23 -0
- package/dist/modules/auth/api/reset.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +4 -2
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/auth/profile/page.js +141 -0
- package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
- package/dist/modules/auth/backend/auth/profile/page.meta.js +13 -0
- package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +4 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +3 -3
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +18 -3
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +15 -2
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +3 -3
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/cli.js +25 -11
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/users.js +59 -2
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +6 -3
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/frontend/login.js +112 -3
- package/dist/modules/auth/frontend/login.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +70 -167
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/notifications.js +112 -0
- package/dist/modules/auth/notifications.js.map +7 -0
- package/dist/modules/auth/services/authService.js +24 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/setup.js +11 -0
- package/dist/modules/auth/setup.js.map +7 -0
- package/dist/modules/business_rules/api/execute/route.js +7 -1
- package/dist/modules/business_rules/api/execute/route.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +4 -0
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +3 -0
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/cli.js +2 -1
- package/dist/modules/business_rules/cli.js.map +2 -2
- package/dist/modules/business_rules/data/validators.js +0 -34
- package/dist/modules/business_rules/data/validators.js.map +2 -2
- package/dist/modules/business_rules/index.js +1 -21
- package/dist/modules/business_rules/index.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +34 -185
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/business_rules/notifications.js +28 -0
- package/dist/modules/business_rules/notifications.js.map +7 -0
- package/dist/modules/business_rules/setup.js +11 -0
- package/dist/modules/business_rules/setup.js.map +7 -0
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
- package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/notifications.js +28 -0
- package/dist/modules/catalog/notifications.js.map +7 -0
- package/dist/modules/catalog/setup.js +22 -0
- package/dist/modules/catalog/setup.js.map +7 -0
- package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
- package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
- package/dist/modules/configs/cli.js +6 -0
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/configs/components/CachePanel.js +4 -4
- package/dist/modules/configs/components/CachePanel.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +48 -1
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +1 -302
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/configs/setup.js +16 -0
- package/dist/modules/configs/setup.js.map +7 -0
- package/dist/modules/currencies/backend/currencies/page.js +3 -0
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/setup.js +16 -0
- package/dist/modules/currencies/setup.js.map +7 -0
- package/dist/modules/customers/backend/customers/companies/page.js +3 -0
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +3 -0
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +3 -0
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +31 -0
- package/dist/modules/customers/commands/deals.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/notifications.js +48 -0
- package/dist/modules/customers/notifications.js.map +7 -0
- package/dist/modules/customers/setup.js +36 -0
- package/dist/modules/customers/setup.js.map +7 -0
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.js +2 -1
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.js.map +2 -2
- package/dist/modules/dashboards/cli.js +44 -5
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
- package/dist/modules/dashboards/lib/role-widgets.js +58 -0
- package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
- package/dist/modules/dashboards/services/widgetDataService.js +139 -3
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/dashboards/setup.js +27 -0
- package/dist/modules/dashboards/setup.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +2 -1
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/dictionaries/setup.js +12 -0
- package/dist/modules/dictionaries/setup.js.map +7 -0
- package/dist/modules/directory/api/get/tenants/lookup.js +70 -0
- package/dist/modules/directory/api/get/tenants/lookup.js.map +7 -0
- package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/directory/setup.js +12 -0
- package/dist/modules/directory/setup.js.map +7 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
- package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
- package/dist/modules/entities/setup.js +11 -0
- package/dist/modules/entities/setup.js.map +7 -0
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/feature_toggles/setup.js +11 -0
- package/dist/modules/feature_toggles/setup.js.map +7 -0
- package/dist/modules/notifications/acl.js +11 -0
- package/dist/modules/notifications/acl.js.map +7 -0
- package/dist/modules/notifications/api/[id]/action/route.js +74 -0
- package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
- package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
- package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
- package/dist/modules/notifications/api/[id]/read/route.js +15 -0
- package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
- package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
- package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
- package/dist/modules/notifications/api/batch/route.js +17 -0
- package/dist/modules/notifications/api/batch/route.js.map +7 -0
- package/dist/modules/notifications/api/feature/route.js +17 -0
- package/dist/modules/notifications/api/feature/route.js.map +7 -0
- package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
- package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
- package/dist/modules/notifications/api/openapi.js +76 -0
- package/dist/modules/notifications/api/openapi.js.map +7 -0
- package/dist/modules/notifications/api/role/route.js +17 -0
- package/dist/modules/notifications/api/role/route.js.map +7 -0
- package/dist/modules/notifications/api/route.js +85 -0
- package/dist/modules/notifications/api/route.js.map +7 -0
- package/dist/modules/notifications/api/settings/route.js +155 -0
- package/dist/modules/notifications/api/settings/route.js.map +7 -0
- package/dist/modules/notifications/api/unread-count/route.js +38 -0
- package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
- package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
- package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
- package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
- package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
- package/dist/modules/notifications/cli.js +16 -0
- package/dist/modules/notifications/cli.js.map +7 -0
- package/dist/modules/notifications/data/entities.js +112 -0
- package/dist/modules/notifications/data/entities.js.map +7 -0
- package/dist/modules/notifications/data/validators.js +98 -0
- package/dist/modules/notifications/data/validators.js.map +7 -0
- package/dist/modules/notifications/di.js +13 -0
- package/dist/modules/notifications/di.js.map +7 -0
- package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
- package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +220 -0
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
- package/dist/modules/notifications/index.js +14 -0
- package/dist/modules/notifications/index.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryConfig.js +107 -0
- package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
- package/dist/modules/notifications/lib/events.js +12 -0
- package/dist/modules/notifications/lib/events.js.map +7 -0
- package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
- package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
- package/dist/modules/notifications/lib/notificationFactory.js +54 -0
- package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
- package/dist/modules/notifications/lib/notificationMapper.js +34 -0
- package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
- package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
- package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +279 -0
- package/dist/modules/notifications/lib/notificationService.js.map +7 -0
- package/dist/modules/notifications/lib/routeHelpers.js +101 -0
- package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
- package/dist/modules/notifications/lib/safeHref.js +24 -0
- package/dist/modules/notifications/lib/safeHref.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
- package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
- package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260129082610.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260129082610.js.map +7 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js +165 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
- package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
- package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
- package/dist/modules/perspectives/setup.js +12 -0
- package/dist/modules/perspectives/setup.js.map +7 -0
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/setup.js +21 -0
- package/dist/modules/planner/setup.js.map +7 -0
- package/dist/modules/query_index/cli.js +63 -7
- package/dist/modules/query_index/cli.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/query_index/setup.js +11 -0
- package/dist/modules/query_index/setup.js.map +7 -0
- package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/resources/setup.js +21 -0
- package/dist/modules/resources/setup.js.map +7 -0
- package/dist/modules/sales/acl.js +0 -1
- package/dist/modules/sales/acl.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +2 -0
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +0 -12
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/cli.js +2 -42
- package/dist/modules/sales/cli.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +53 -62
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +26 -0
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/lib/dictionaries.js +0 -3
- package/dist/modules/sales/lib/dictionaries.js.map +2 -2
- package/dist/modules/sales/lib/seeds.js +48 -0
- package/dist/modules/sales/lib/seeds.js.map +7 -0
- package/dist/modules/sales/notifications.client.js +51 -0
- package/dist/modules/sales/notifications.client.js.map +7 -0
- package/dist/modules/sales/notifications.js +88 -0
- package/dist/modules/sales/notifications.js.map +7 -0
- package/dist/modules/sales/setup.js +99 -0
- package/dist/modules/sales/setup.js.map +7 -0
- package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
- package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/index.js +7 -0
- package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
- package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +79 -0
- package/dist/modules/staff/commands/leave-requests.js.map +2 -2
- package/dist/modules/staff/notifications.js +75 -0
- package/dist/modules/staff/notifications.js.map +7 -0
- package/dist/modules/staff/setup.js +27 -0
- package/dist/modules/staff/setup.js.map +7 -0
- package/dist/modules/workflows/acl.js +0 -2
- package/dist/modules/workflows/acl.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +6 -18
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/route.js +1 -6
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +1 -9
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +15 -24
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +5 -0
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +132 -150
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +3 -0
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +9 -5
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/cli.js +15 -93
- package/dist/modules/workflows/cli.js.map +3 -3
- package/dist/modules/workflows/data/entities.js +1 -64
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +0 -115
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/examples/checkout-demo-definition.json +5 -1
- package/dist/modules/workflows/lib/activity-executor.js +13 -75
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/graph-utils.js +2 -71
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +7 -36
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +23 -33
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +57 -161
- package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
- package/dist/modules/workflows/migrations/Migration20251207131955.js +76 -72
- package/dist/modules/workflows/migrations/Migration20251207131955.js.map +2 -2
- package/dist/modules/workflows/notifications.js +28 -0
- package/dist/modules/workflows/notifications.js.map +7 -0
- package/dist/modules/workflows/setup.js +16 -0
- package/dist/modules/workflows/setup.js.map +7 -0
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
- package/generated/entities/notification/index.ts +27 -0
- package/generated/entities.ids.generated.ts +5 -2
- package/generated/entity-fields-registry.ts +2 -2
- package/package.json +2 -2
- package/src/__tests__/module-decoupling.test.ts +356 -0
- package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
- package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
- package/src/modules/api_keys/setup.ts +9 -0
- package/src/modules/attachments/components/AttachmentLibrary.tsx +6 -2
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
- package/src/modules/attachments/lib/assignmentDetails.ts +32 -16
- package/src/modules/attachments/lib/partitions.ts +3 -3
- package/src/modules/attachments/setup.ts +9 -0
- package/src/modules/audit_logs/setup.ts +10 -0
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +31 -1
- package/src/modules/auth/api/__tests__/login.test.ts +2 -0
- package/src/modules/auth/api/admin/nav.ts +10 -6
- package/src/modules/auth/api/login.ts +26 -7
- package/src/modules/auth/api/profile/route.ts +163 -0
- package/src/modules/auth/api/reset/confirm.ts +25 -2
- package/src/modules/auth/api/reset.ts +23 -0
- package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
- package/src/modules/auth/api/users/route.ts +5 -2
- package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
- package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
- package/src/modules/auth/backend/roles/page.tsx +3 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +22 -3
- package/src/modules/auth/backend/users/create/page.tsx +19 -2
- package/src/modules/auth/backend/users/page.tsx +3 -3
- package/src/modules/auth/cli.ts +38 -11
- package/src/modules/auth/commands/users.ts +73 -2
- package/src/modules/auth/data/validators.ts +6 -2
- package/src/modules/auth/frontend/login.tsx +134 -5
- package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
- package/src/modules/auth/i18n/de.json +48 -1
- package/src/modules/auth/i18n/en.json +48 -1
- package/src/modules/auth/i18n/es.json +48 -1
- package/src/modules/auth/i18n/pl.json +48 -1
- package/src/modules/auth/lib/setup-app.ts +97 -186
- package/src/modules/auth/notifications.ts +109 -0
- package/src/modules/auth/services/authService.ts +27 -4
- package/src/modules/auth/setup.ts +9 -0
- package/src/modules/business_rules/api/execute/route.ts +8 -1
- package/src/modules/business_rules/backend/rules/page.tsx +4 -0
- package/src/modules/business_rules/backend/sets/page.tsx +3 -0
- package/src/modules/business_rules/cli.ts +2 -1
- package/src/modules/business_rules/data/validators.ts +0 -40
- package/src/modules/business_rules/i18n/en.json +3 -1
- package/src/modules/business_rules/index.ts +0 -25
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
- package/src/modules/business_rules/lib/rule-engine.ts +51 -277
- package/src/modules/business_rules/notifications.ts +25 -0
- package/src/modules/business_rules/setup.ts +9 -0
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
- package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
- package/src/modules/catalog/i18n/en.json +3 -1
- package/src/modules/catalog/notifications.ts +25 -0
- package/src/modules/catalog/setup.ts +22 -0
- package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
- package/src/modules/configs/cli.ts +6 -0
- package/src/modules/configs/components/CachePanel.tsx +4 -4
- package/src/modules/configs/i18n/en.json +12 -2
- package/src/modules/configs/i18n/pl.json +12 -2
- package/src/modules/configs/lib/system-status.ts +48 -1
- package/src/modules/configs/lib/system-status.types.ts +1 -0
- package/src/modules/configs/lib/upgrade-actions.ts +5 -338
- package/src/modules/configs/setup.ts +14 -0
- package/src/modules/currencies/backend/currencies/page.tsx +3 -0
- package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
- package/src/modules/currencies/setup.ts +15 -0
- package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
- package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
- package/src/modules/customers/backend/customers/people/page.tsx +3 -0
- package/src/modules/customers/commands/deals.ts +39 -0
- package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
- package/src/modules/customers/i18n/en.json +5 -1
- package/src/modules/customers/notifications.ts +44 -0
- package/src/modules/customers/setup.ts +36 -0
- package/src/modules/customers/widgets/dashboard/customer-todos/widget.ts +2 -2
- package/src/modules/customers/widgets/dashboard/new-customers/widget.ts +2 -2
- package/src/modules/customers/widgets/dashboard/new-deals/widget.ts +2 -2
- package/src/modules/customers/widgets/dashboard/next-interactions/widget.ts +2 -2
- package/src/modules/dashboards/cli.ts +55 -5
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
- package/src/modules/dashboards/lib/role-widgets.ts +80 -0
- package/src/modules/dashboards/services/widgetDataService.ts +164 -4
- package/src/modules/dashboards/setup.ts +26 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +2 -2
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +2 -2
- package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
- package/src/modules/dictionaries/setup.ts +10 -0
- package/src/modules/directory/api/get/tenants/lookup.ts +75 -0
- package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
- package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
- package/src/modules/directory/setup.ts +10 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
- package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
- package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
- package/src/modules/entities/setup.ts +9 -0
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
- package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
- package/src/modules/feature_toggles/setup.ts +9 -0
- package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
- package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
- package/src/modules/notifications/acl.ts +7 -0
- package/src/modules/notifications/api/[id]/action/route.ts +75 -0
- package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
- package/src/modules/notifications/api/[id]/read/route.ts +12 -0
- package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
- package/src/modules/notifications/api/batch/route.ts +14 -0
- package/src/modules/notifications/api/feature/route.ts +14 -0
- package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
- package/src/modules/notifications/api/openapi.ts +76 -0
- package/src/modules/notifications/api/role/route.ts +14 -0
- package/src/modules/notifications/api/route.ts +92 -0
- package/src/modules/notifications/api/settings/route.ts +157 -0
- package/src/modules/notifications/api/unread-count/route.ts +38 -0
- package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
- package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
- package/src/modules/notifications/cli.ts +18 -0
- package/src/modules/notifications/data/entities.ts +99 -0
- package/src/modules/notifications/data/validators.ts +115 -0
- package/src/modules/notifications/di.ts +11 -0
- package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
- package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +233 -0
- package/src/modules/notifications/i18n/de.json +50 -0
- package/src/modules/notifications/i18n/en.json +50 -0
- package/src/modules/notifications/i18n/es.json +50 -0
- package/src/modules/notifications/i18n/pl.json +50 -0
- package/src/modules/notifications/index.ts +12 -0
- package/src/modules/notifications/lib/deliveryConfig.ts +153 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
- package/src/modules/notifications/lib/events.ts +48 -0
- package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
- package/src/modules/notifications/lib/notificationFactory.ts +76 -0
- package/src/modules/notifications/lib/notificationMapper.ts +33 -0
- package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
- package/src/modules/notifications/lib/notificationService.ts +414 -0
- package/src/modules/notifications/lib/routeHelpers.ts +151 -0
- package/src/modules/notifications/lib/safeHref.ts +29 -0
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +336 -0
- package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
- package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
- package/src/modules/notifications/migrations/Migration20260129082610.ts +13 -0
- package/src/modules/notifications/subscribers/deliver-notification.ts +204 -0
- package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
- package/src/modules/perspectives/setup.ts +10 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
- package/src/modules/planner/setup.ts +21 -0
- package/src/modules/query_index/cli.ts +82 -13
- package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
- package/src/modules/query_index/setup.ts +9 -0
- package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
- package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
- package/src/modules/resources/setup.ts +21 -0
- package/src/modules/sales/acl.ts +0 -1
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
- package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +0 -16
- package/src/modules/sales/cli.ts +2 -43
- package/src/modules/sales/commands/documents.ts +62 -70
- package/src/modules/sales/commands/payments.ts +33 -0
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
- package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
- package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
- package/src/modules/sales/i18n/de.json +20 -0
- package/src/modules/sales/i18n/en.json +25 -1
- package/src/modules/sales/i18n/es.json +20 -0
- package/src/modules/sales/i18n/pl.json +20 -0
- package/src/modules/sales/lib/dictionaries.ts +0 -3
- package/src/modules/sales/lib/seeds.ts +53 -0
- package/src/modules/sales/notifications.client.ts +65 -0
- package/src/modules/sales/notifications.ts +82 -0
- package/src/modules/sales/setup.ts +108 -0
- package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
- package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
- package/src/modules/sales/widgets/notifications/index.ts +2 -0
- package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
- package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
- package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
- package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
- package/src/modules/staff/commands/leave-requests.ts +94 -0
- package/src/modules/staff/i18n/de.json +4 -0
- package/src/modules/staff/i18n/en.json +9 -1
- package/src/modules/staff/i18n/es.json +4 -0
- package/src/modules/staff/i18n/pl.json +4 -0
- package/src/modules/staff/notifications.ts +71 -0
- package/src/modules/staff/setup.ts +27 -0
- package/src/modules/workflows/acl.ts +0 -2
- package/src/modules/workflows/api/__tests__/instances.route.test.ts +2 -5
- package/src/modules/workflows/api/instances/route.ts +7 -21
- package/src/modules/workflows/api/tasks/route.ts +1 -7
- package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +0 -9
- package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/create/page.tsx +0 -9
- package/src/modules/workflows/backend/definitions/page.tsx +5 -0
- package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -21
- package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/instances/page.tsx +4 -1
- package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/tasks/page.tsx +10 -6
- package/src/modules/workflows/cli.ts +12 -123
- package/src/modules/workflows/data/entities.ts +0 -124
- package/src/modules/workflows/data/validators.ts +0 -138
- package/src/modules/workflows/examples/checkout-demo-definition.json +5 -1
- package/src/modules/workflows/i18n/en.json +3 -72
- package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +36 -43
- package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +90 -170
- package/src/modules/workflows/lib/activity-executor.ts +16 -129
- package/src/modules/workflows/lib/graph-utils.ts +2 -117
- package/src/modules/workflows/lib/seeds.ts +12 -50
- package/src/modules/workflows/lib/start-validator.ts +28 -38
- package/src/modules/workflows/lib/transition-handler.ts +71 -212
- package/src/modules/workflows/migrations/Migration20251207131955.ts +77 -143
- package/src/modules/workflows/notifications.ts +25 -0
- package/src/modules/workflows/setup.ts +15 -0
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
- package/dist/generated/entities/workflow_event_trigger/index.js +0 -33
- package/dist/generated/entities/workflow_event_trigger/index.js.map +0 -7
- package/dist/modules/auth/events.js +0 -30
- package/dist/modules/auth/events.js.map +0 -7
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js +0 -145
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +0 -7
- package/dist/modules/catalog/events.js +0 -34
- package/dist/modules/catalog/events.js.map +0 -7
- package/dist/modules/customers/events.js +0 -49
- package/dist/modules/customers/events.js.map +0 -7
- package/dist/modules/directory/events.js +0 -23
- package/dist/modules/directory/events.js.map +0 -7
- package/dist/modules/sales/events.js +0 -63
- package/dist/modules/sales/events.js.map +0 -7
- package/dist/modules/sales/lib/frontend/documentDataEvents.js +0 -25
- package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +0 -7
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +0 -481
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +0 -7
- package/dist/modules/workflows/components/EventTriggersEditor.js +0 -553
- package/dist/modules/workflows/components/EventTriggersEditor.js.map +0 -7
- package/dist/modules/workflows/events.js +0 -38
- package/dist/modules/workflows/events.js.map +0 -7
- package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
- package/dist/modules/workflows/examples/order-approval-guard-rules.json +0 -32
- package/dist/modules/workflows/lib/event-trigger-service.js +0 -308
- package/dist/modules/workflows/lib/event-trigger-service.js.map +0 -7
- package/dist/modules/workflows/migrations/Migration20260123143500.js +0 -36
- package/dist/modules/workflows/migrations/Migration20260123143500.js.map +0 -7
- package/dist/modules/workflows/subscribers/event-trigger.js +0 -78
- package/dist/modules/workflows/subscribers/event-trigger.js.map +0 -7
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +0 -323
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +0 -7
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js +0 -17
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +0 -7
- package/dist/modules/workflows/widgets/injection-table.js +0 -19
- package/dist/modules/workflows/widgets/injection-table.js.map +0 -7
- package/generated/entities/workflow_event_trigger/index.ts +0 -15
- package/src/modules/auth/events.ts +0 -39
- package/src/modules/business_rules/api/execute/[ruleId]/route.ts +0 -163
- package/src/modules/catalog/events.ts +0 -45
- package/src/modules/customers/events.ts +0 -63
- package/src/modules/directory/events.ts +0 -31
- package/src/modules/sales/events.ts +0 -82
- package/src/modules/sales/lib/frontend/documentDataEvents.ts +0 -28
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +0 -581
- package/src/modules/workflows/components/EventTriggersEditor.tsx +0 -664
- package/src/modules/workflows/events.ts +0 -49
- package/src/modules/workflows/examples/order-approval-definition.json +0 -257
- package/src/modules/workflows/examples/order-approval-guard-rules.json +0 -32
- package/src/modules/workflows/lib/event-trigger-service.ts +0 -557
- package/src/modules/workflows/migrations/Migration20260123143500.ts +0 -38
- package/src/modules/workflows/subscribers/event-trigger.ts +0 -109
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +0 -446
- package/src/modules/workflows/widgets/injection/order-approval/widget.ts +0 -16
- package/src/modules/workflows/widgets/injection-table.ts +0 -21
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/attachments/lib/assignmentDetails.ts"],
|
|
4
|
-
"sourcesContent": ["import type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { AttachmentAssignment } from './metadata'\nimport type { CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\n\ntype AssignmentLinkSpec = {\n labelFields?: string[]\n extraFields?: string[]\n buildHref?: (record: Record<string, unknown>) => string | null | undefined\n}\n\nlet _entityLinkSpecsCache: Record<string, AssignmentLinkSpec> | null = null\n\nfunction getEntityLinkSpecs(): Record<string, AssignmentLinkSpec> {\n if (_entityLinkSpecsCache) return _entityLinkSpecsCache\n const E = getEntityIds() as any\n _entityLinkSpecsCache = {\n [E.catalog.catalog_product]: {\n labelFields: ['title', 'sku', 'handle'],\n buildHref: (record) => buildSimpleHref('/backend/catalog/products', record.id),\n },\n [E.catalog.catalog_product_variant]: {\n labelFields: ['name', 'sku'],\n extraFields: ['product_id'],\n buildHref: (record) => {\n const productId = readRecordValue(record, 'product_id')\n if (!productId) return null\n return `/backend/catalog/products/${encodeURIComponent(productId)}/variants/${encodeURIComponent(String(record.id ?? ''))}`\n },\n },\n [E.customers.customer_entity]: {\n labelFields: ['display_name'],\n extraFields: ['kind'],\n buildHref: (record) => {\n const kind = String(readRecordValue(record, 'kind') || '').toLowerCase()\n if (kind === 'company') return buildSimpleHref('/backend/customers/companies', record.id)\n if (kind === 'person') return buildSimpleHref('/backend/customers/people', record.id)\n return null\n },\n },\n [E.customers.customer_person_profile]: {\n labelFields: ['preferred_name', 'display_name', 'first_name', 'last_name'],\n extraFields: ['entity_id', 'first_name', 'last_name'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/people', entityId) : null\n },\n },\n [E.customers.customer_company_profile]: {\n labelFields: ['brand_name', 'display_name', 'legal_name'],\n extraFields: ['entity_id'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/companies', entityId) : null\n },\n },\n [E.customers.customer_deal]: {\n labelFields: ['title'],\n buildHref: (record) => buildSimpleHref('/backend/customers/deals', record.id),\n },\n [E.sales.sales_channel]: {\n labelFields: ['name', 'title'],\n buildHref: (record) => buildSimpleHref('/backend/sales/channels', record.id, '/edit'),\n },\n }\n return _entityLinkSpecsCache\n}\n\nconst LIBRARY_ENTITY_ID = 'attachments:library'\nconst DEFAULT_LABEL_FIELDS = [\n 'label',\n 'title',\n 'name',\n 'display_name',\n 'displayName',\n 'subject',\n 'sku',\n 'handle',\n 'order_number',\n 'quote_number',\n 'invoice_number',\n 'email',\n 'company_name',\n 'legal_name',\n 'brand_name',\n]\n\nlet entitySpecsPromise: Promise<Map<string, CustomEntitySpec>> | null = null\n\nasync function loadEntitySpecs(): Promise<Map<string, CustomEntitySpec>> {\n if (!entitySpecsPromise) {\n entitySpecsPromise = Promise.resolve()\n .then(() => {\n const map = new Map<string, CustomEntitySpec>()\n const mods = getModules()\n for (const mod of mods) {\n const specs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of specs) {\n if (spec?.id && !map.has(spec.id)) {\n map.set(spec.id, spec)\n }\n }\n }\n return map\n })\n .catch(() => new Map<string, CustomEntitySpec>())\n }\n return entitySpecsPromise\n}\n\nexport type AssignmentEnrichment = {\n label?: string\n href?: string\n}\n\nexport type AssignmentEnrichmentMap = Map<string, AssignmentEnrichment>\n\nfunction camelToSnake(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toLowerCase()\n}\n\nfunction snakeToCamel(value: string): string {\n return value.replace(/[_-](\\w)/g, (_, c: string) => c.toUpperCase())\n}\n\nfunction normalizeValue(value: unknown): string | null {\n if (value === undefined || value === null) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n return null\n}\n\nfunction readRecordValue(record: Record<string, unknown>, field: string): string | null {\n if (!field) return null\n if (record[field] !== undefined) return normalizeValue(record[field])\n const snake = camelToSnake(field)\n if (snake !== field && record[snake] !== undefined) return normalizeValue(record[snake])\n const camel = snakeToCamel(field)\n if (camel !== field && record[camel] !== undefined) return normalizeValue(record[camel])\n return null\n}\n\nfunction buildSimpleHref(base: string, idValue: unknown, suffix: string = ''): string | null {\n const id = normalizeValue(idValue)\n if (!id) return null\n return `${base}/${encodeURIComponent(id)}${suffix}`\n}\n\nfunction isUuid(value: string | null | undefined): boolean {\n return typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value.trim())\n}\n\nfunction filterIdsForEntity(entityId: string, ids: string[]): string[] {\n const E = getEntityIds() as any\n if (entityId === E.catalog.catalog_product_variant || entityId === E.catalog.catalog_product) {\n return ids.filter((id) => isUuid(id))\n }\n return ids\n}\n\nfunction resolveLabelCandidates(entityId: string, linkSpec: AssignmentLinkSpec | undefined, entitySpecs: Map<string, CustomEntitySpec>): string[] {\n const candidates = new Set<string>()\n const spec = entitySpecs.get(entityId)\n if (spec?.labelField) {\n candidates.add(spec.labelField)\n candidates.add(camelToSnake(spec.labelField))\n }\n for (const field of linkSpec?.labelFields ?? []) {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n }\n DEFAULT_LABEL_FIELDS.forEach((field) => {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n })\n return Array.from(candidates).filter((field) => field.length > 0)\n}\n\nfunction buildLabel(\n record: Record<string, unknown>,\n entityId: string,\n linkSpec: AssignmentLinkSpec | undefined,\n entitySpecs: Map<string, CustomEntitySpec>\n): string | null {\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n for (const candidate of candidates) {\n const value = readRecordValue(record, candidate)\n if (value) return value\n }\n const fullName = [readRecordValue(record, 'first_name'), readRecordValue(record, 'last_name')]\n .filter((part): part is string => Boolean(part))\n .join(' ')\n .trim()\n if (fullName.length) return fullName\n return null\n}\n\nexport async function resolveAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n opts: { queryEngine?: QueryEngine | null; tenantId: string; organizationId: string },\n): Promise<AssignmentEnrichmentMap> {\n const map: AssignmentEnrichmentMap = new Map()\n if (!assignments.length || !opts.queryEngine) return map\n const entitySpecs = await loadEntitySpecs()\n const grouped = new Map<string, Set<string>>()\n for (const assignment of assignments) {\n if (!assignment || assignment.type === LIBRARY_ENTITY_ID) continue\n const type = assignment.type?.trim()\n const id = assignment.id?.trim()\n if (!type || !id) continue\n if (!grouped.has(type)) grouped.set(type, new Set())\n grouped.get(type)!.add(id)\n }\n if (!grouped.size) return map\n\n const entityLinkSpecs = getEntityLinkSpecs()\n for (const [entityId, idsSet] of grouped.entries()) {\n const ids = filterIdsForEntity(entityId, Array.from(idsSet.values()))\n if (!ids.length) continue\n const linkSpec = entityLinkSpecs[entityId]\n const fields = new Set<string>(['id'])\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n candidates.forEach((field) => fields.add(field))\n for (const extra of linkSpec?.extraFields ?? []) {\n fields.add(extra)\n fields.add(camelToSnake(extra))\n }\n try {\n const result = await opts.queryEngine.query(entityId as any, {\n fields: Array.from(fields),\n filters: { id: ids.length === 1 ? { $eq: ids[0] } : { $in: ids } },\n tenantId: opts.tenantId,\n organizationId: opts.organizationId,\n page: { pageSize: Math.max(ids.length, 20) },\n })\n for (const record of result.items ?? []) {\n const recordId = normalizeValue((record as Record<string, unknown>).id)\n if (!recordId) continue\n const label = buildLabel(record as Record<string, unknown>, entityId, linkSpec, entitySpecs)\n const href = linkSpec?.buildHref?.(record as Record<string, unknown>) ?? null\n if (label || href) {\n map.set(`${entityId}:${recordId}`, {\n label: label ?? undefined,\n href: href ?? undefined,\n })\n }\n }\n } catch (error) {\n console.warn('[attachments] Failed to resolve assignment details for', entityId, error)\n }\n }\n return map\n}\n\nexport function applyAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n enrichments: AssignmentEnrichmentMap,\n): AttachmentAssignment[] {\n if (!enrichments.size) return assignments\n return assignments.map((assignment) => {\n if (!assignment || !assignment.type || !assignment.id) return assignment\n const key = `${assignment.type}:${assignment.id}`\n const detail = enrichments.get(key)\n if (!detail) return assignment\n const next: AttachmentAssignment = { ...assignment }\n if (!next.label && detail.label) next.label = detail.label\n if (!next.href && detail.href) next.href = detail.href\n return next\n })\n}\n"],
|
|
5
|
-
"mappings": "AAGA,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAQ3B,IAAI,wBAAmE;AAEvE,SAAS,qBAAyD;AAChE,MAAI,sBAAuB,QAAO;AAClC,QAAM,IAAI,aAAa;AACvB,
|
|
4
|
+
"sourcesContent": ["import type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { AttachmentAssignment } from './metadata'\nimport type { CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\n\ntype AssignmentLinkSpec = {\n labelFields?: string[]\n extraFields?: string[]\n buildHref?: (record: Record<string, unknown>) => string | null | undefined\n}\n\nlet _entityLinkSpecsCache: Record<string, AssignmentLinkSpec> | null = null\n\nfunction getEntityLinkSpecs(): Record<string, AssignmentLinkSpec> {\n if (_entityLinkSpecsCache) return _entityLinkSpecsCache\n const E = getEntityIds() as any\n const specs: Record<string, AssignmentLinkSpec> = {}\n\n if (E.catalog?.catalog_product) {\n specs[E.catalog.catalog_product] = {\n labelFields: ['title', 'sku', 'handle'],\n buildHref: (record) => buildSimpleHref('/backend/catalog/products', record.id),\n }\n }\n if (E.catalog?.catalog_product_variant) {\n specs[E.catalog.catalog_product_variant] = {\n labelFields: ['name', 'sku'],\n extraFields: ['product_id'],\n buildHref: (record) => {\n const productId = readRecordValue(record, 'product_id')\n if (!productId) return null\n return `/backend/catalog/products/${encodeURIComponent(productId)}/variants/${encodeURIComponent(String(record.id ?? ''))}`\n },\n }\n }\n if (E.customers?.customer_entity) {\n specs[E.customers.customer_entity] = {\n labelFields: ['display_name'],\n extraFields: ['kind'],\n buildHref: (record) => {\n const kind = String(readRecordValue(record, 'kind') || '').toLowerCase()\n if (kind === 'company') return buildSimpleHref('/backend/customers/companies', record.id)\n if (kind === 'person') return buildSimpleHref('/backend/customers/people', record.id)\n return null\n },\n }\n }\n if (E.customers?.customer_person_profile) {\n specs[E.customers.customer_person_profile] = {\n labelFields: ['preferred_name', 'display_name', 'first_name', 'last_name'],\n extraFields: ['entity_id', 'first_name', 'last_name'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/people', entityId) : null\n },\n }\n }\n if (E.customers?.customer_company_profile) {\n specs[E.customers.customer_company_profile] = {\n labelFields: ['brand_name', 'display_name', 'legal_name'],\n extraFields: ['entity_id'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/companies', entityId) : null\n },\n }\n }\n if (E.customers?.customer_deal) {\n specs[E.customers.customer_deal] = {\n labelFields: ['title'],\n buildHref: (record) => buildSimpleHref('/backend/customers/deals', record.id),\n }\n }\n if (E.sales?.sales_channel) {\n specs[E.sales.sales_channel] = {\n labelFields: ['name', 'title'],\n buildHref: (record) => buildSimpleHref('/backend/sales/channels', record.id, '/edit'),\n }\n }\n\n _entityLinkSpecsCache = specs\n return _entityLinkSpecsCache\n}\n\nconst LIBRARY_ENTITY_ID = 'attachments:library'\nconst DEFAULT_LABEL_FIELDS = [\n 'label',\n 'title',\n 'name',\n 'display_name',\n 'displayName',\n 'subject',\n 'sku',\n 'handle',\n 'order_number',\n 'quote_number',\n 'invoice_number',\n 'email',\n 'company_name',\n 'legal_name',\n 'brand_name',\n]\n\nlet entitySpecsPromise: Promise<Map<string, CustomEntitySpec>> | null = null\n\nasync function loadEntitySpecs(): Promise<Map<string, CustomEntitySpec>> {\n if (!entitySpecsPromise) {\n entitySpecsPromise = Promise.resolve()\n .then(() => {\n const map = new Map<string, CustomEntitySpec>()\n const mods = getModules()\n for (const mod of mods) {\n const specs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of specs) {\n if (spec?.id && !map.has(spec.id)) {\n map.set(spec.id, spec)\n }\n }\n }\n return map\n })\n .catch(() => new Map<string, CustomEntitySpec>())\n }\n return entitySpecsPromise\n}\n\nexport type AssignmentEnrichment = {\n label?: string\n href?: string\n}\n\nexport type AssignmentEnrichmentMap = Map<string, AssignmentEnrichment>\n\nfunction camelToSnake(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toLowerCase()\n}\n\nfunction snakeToCamel(value: string): string {\n return value.replace(/[_-](\\w)/g, (_, c: string) => c.toUpperCase())\n}\n\nfunction normalizeValue(value: unknown): string | null {\n if (value === undefined || value === null) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n return null\n}\n\nfunction readRecordValue(record: Record<string, unknown>, field: string): string | null {\n if (!field) return null\n if (record[field] !== undefined) return normalizeValue(record[field])\n const snake = camelToSnake(field)\n if (snake !== field && record[snake] !== undefined) return normalizeValue(record[snake])\n const camel = snakeToCamel(field)\n if (camel !== field && record[camel] !== undefined) return normalizeValue(record[camel])\n return null\n}\n\nfunction buildSimpleHref(base: string, idValue: unknown, suffix: string = ''): string | null {\n const id = normalizeValue(idValue)\n if (!id) return null\n return `${base}/${encodeURIComponent(id)}${suffix}`\n}\n\nfunction isUuid(value: string | null | undefined): boolean {\n return typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value.trim())\n}\n\nfunction filterIdsForEntity(entityId: string, ids: string[]): string[] {\n const E = getEntityIds() as any\n if (entityId === E.catalog?.catalog_product_variant || entityId === E.catalog?.catalog_product) {\n return ids.filter((id) => isUuid(id))\n }\n return ids\n}\n\nfunction resolveLabelCandidates(entityId: string, linkSpec: AssignmentLinkSpec | undefined, entitySpecs: Map<string, CustomEntitySpec>): string[] {\n const candidates = new Set<string>()\n const spec = entitySpecs.get(entityId)\n if (spec?.labelField) {\n candidates.add(spec.labelField)\n candidates.add(camelToSnake(spec.labelField))\n }\n for (const field of linkSpec?.labelFields ?? []) {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n }\n DEFAULT_LABEL_FIELDS.forEach((field) => {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n })\n return Array.from(candidates).filter((field) => field.length > 0)\n}\n\nfunction buildLabel(\n record: Record<string, unknown>,\n entityId: string,\n linkSpec: AssignmentLinkSpec | undefined,\n entitySpecs: Map<string, CustomEntitySpec>\n): string | null {\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n for (const candidate of candidates) {\n const value = readRecordValue(record, candidate)\n if (value) return value\n }\n const fullName = [readRecordValue(record, 'first_name'), readRecordValue(record, 'last_name')]\n .filter((part): part is string => Boolean(part))\n .join(' ')\n .trim()\n if (fullName.length) return fullName\n return null\n}\n\nexport async function resolveAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n opts: { queryEngine?: QueryEngine | null; tenantId: string; organizationId: string },\n): Promise<AssignmentEnrichmentMap> {\n const map: AssignmentEnrichmentMap = new Map()\n if (!assignments.length || !opts.queryEngine) return map\n const entitySpecs = await loadEntitySpecs()\n const grouped = new Map<string, Set<string>>()\n for (const assignment of assignments) {\n if (!assignment || assignment.type === LIBRARY_ENTITY_ID) continue\n const type = assignment.type?.trim()\n const id = assignment.id?.trim()\n if (!type || !id) continue\n if (!grouped.has(type)) grouped.set(type, new Set())\n grouped.get(type)!.add(id)\n }\n if (!grouped.size) return map\n\n const entityLinkSpecs = getEntityLinkSpecs()\n for (const [entityId, idsSet] of grouped.entries()) {\n const ids = filterIdsForEntity(entityId, Array.from(idsSet.values()))\n if (!ids.length) continue\n const linkSpec = entityLinkSpecs[entityId]\n const fields = new Set<string>(['id'])\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n candidates.forEach((field) => fields.add(field))\n for (const extra of linkSpec?.extraFields ?? []) {\n fields.add(extra)\n fields.add(camelToSnake(extra))\n }\n try {\n const result = await opts.queryEngine.query(entityId as any, {\n fields: Array.from(fields),\n filters: { id: ids.length === 1 ? { $eq: ids[0] } : { $in: ids } },\n tenantId: opts.tenantId,\n organizationId: opts.organizationId,\n page: { pageSize: Math.max(ids.length, 20) },\n })\n for (const record of result.items ?? []) {\n const recordId = normalizeValue((record as Record<string, unknown>).id)\n if (!recordId) continue\n const label = buildLabel(record as Record<string, unknown>, entityId, linkSpec, entitySpecs)\n const href = linkSpec?.buildHref?.(record as Record<string, unknown>) ?? null\n if (label || href) {\n map.set(`${entityId}:${recordId}`, {\n label: label ?? undefined,\n href: href ?? undefined,\n })\n }\n }\n } catch (error) {\n console.warn('[attachments] Failed to resolve assignment details for', entityId, error)\n }\n }\n return map\n}\n\nexport function applyAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n enrichments: AssignmentEnrichmentMap,\n): AttachmentAssignment[] {\n if (!enrichments.size) return assignments\n return assignments.map((assignment) => {\n if (!assignment || !assignment.type || !assignment.id) return assignment\n const key = `${assignment.type}:${assignment.id}`\n const detail = enrichments.get(key)\n if (!detail) return assignment\n const next: AttachmentAssignment = { ...assignment }\n if (!next.label && detail.label) next.label = detail.label\n if (!next.href && detail.href) next.href = detail.href\n return next\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -16,9 +16,9 @@ const DEFAULT_ATTACHMENT_PARTITIONS = [
|
|
|
16
16
|
isPublic: false
|
|
17
17
|
}
|
|
18
18
|
];
|
|
19
|
-
const PRODUCT_MEDIA_ENTITY_IDS =
|
|
20
|
-
E.catalog.
|
|
21
|
-
|
|
19
|
+
const PRODUCT_MEDIA_ENTITY_IDS = new Set(
|
|
20
|
+
[E.catalog?.catalog_product].filter(Boolean)
|
|
21
|
+
);
|
|
22
22
|
const FALLBACK_PARTITION = "privateAttachments";
|
|
23
23
|
function resolveDefaultPartitionCode(entityId) {
|
|
24
24
|
if (!entityId) return FALLBACK_PARTITION;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/attachments/lib/partitions.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { AttachmentPartition } from '../data/entities'\nimport { resolveDefaultAttachmentOcrEnabled } from './ocrConfig'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { E } from '#generated/entities.ids.generated'\n\nexport type AttachmentPartitionSeed = {\n code: string\n title: string\n description?: string | null\n isPublic?: boolean\n}\n\nexport const DEFAULT_ATTACHMENT_PARTITIONS: AttachmentPartitionSeed[] = [\n {\n code: 'productsMedia',\n title: 'Products media',\n description: 'Public media uploaded for catalog products.',\n isPublic: true,\n },\n {\n code: 'privateAttachments',\n title: 'Private attachments',\n description: 'Internal attachments scoped to tenants and organizations.',\n isPublic: false,\n },\n]\n\nconst PRODUCT_MEDIA_ENTITY_IDS = new Set<string>(
|
|
5
|
-
"mappings": "AACA,SAAS,2BAA2B;AACpC,SAAS,0CAA0C;AACnD,SAAS,yBAAyB;AAClC,SAAS,SAAS;AASX,MAAM,gCAA2D;AAAA,EACtE;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AACF;AAEA,MAAM,2BAA2B,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { AttachmentPartition } from '../data/entities'\nimport { resolveDefaultAttachmentOcrEnabled } from './ocrConfig'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { E } from '#generated/entities.ids.generated'\n\nexport type AttachmentPartitionSeed = {\n code: string\n title: string\n description?: string | null\n isPublic?: boolean\n}\n\nexport const DEFAULT_ATTACHMENT_PARTITIONS: AttachmentPartitionSeed[] = [\n {\n code: 'productsMedia',\n title: 'Products media',\n description: 'Public media uploaded for catalog products.',\n isPublic: true,\n },\n {\n code: 'privateAttachments',\n title: 'Private attachments',\n description: 'Internal attachments scoped to tenants and organizations.',\n isPublic: false,\n },\n]\n\nconst PRODUCT_MEDIA_ENTITY_IDS = new Set<string>(\n [(E as any).catalog?.catalog_product].filter(Boolean) as string[]\n)\n\nconst FALLBACK_PARTITION = 'privateAttachments'\n\nexport function resolveDefaultPartitionCode(entityId: string | null | undefined): string {\n if (!entityId) return FALLBACK_PARTITION\n if (PRODUCT_MEDIA_ENTITY_IDS.has(entityId)) return 'productsMedia'\n return FALLBACK_PARTITION\n}\n\nexport async function ensureDefaultPartitions(em: EntityManager): Promise<void> {\n const repo = em.getRepository(AttachmentPartition)\n const existing = await repo.findAll({ fields: ['code'] })\n const existingCodes = new Set(existing.map((entry) => entry.code))\n const pending = DEFAULT_ATTACHMENT_PARTITIONS.filter((seed) => !existingCodes.has(seed.code))\n if (!pending.length) return\n for (const seed of pending) {\n const record = repo.create({\n code: seed.code,\n title: seed.title,\n description: seed.description ?? null,\n storageDriver: 'local',\n isPublic: seed.isPublic ?? false,\n requiresOcr: resolveDefaultAttachmentOcrEnabled(),\n })\n em.persist(record)\n }\n await em.flush()\n}\n\nexport function sanitizePartitionCode(input: string): string {\n const trimmed = input.trim()\n const normalized = trimmed.replace(/[^a-zA-Z0-9_-]/g, '')\n return normalized\n}\n\nexport function isPartitionSettingsLocked(): boolean {\n const demoModeParsed = parseBooleanToken(process.env.DEMO_MODE ?? '')\n const demoModeEnabled = demoModeParsed === false ? false : true\n const onboardingEnabled = parseBooleanToken(process.env.SELF_SERVICE_ONBOARDING_ENABLED ?? '') === true\n return demoModeEnabled || onboardingEnabled\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,2BAA2B;AACpC,SAAS,0CAA0C;AACnD,SAAS,yBAAyB;AAClC,SAAS,SAAS;AASX,MAAM,gCAA2D;AAAA,EACtE;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AACF;AAEA,MAAM,2BAA2B,IAAI;AAAA,EACnC,CAAE,EAAU,SAAS,eAAe,EAAE,OAAO,OAAO;AACtD;AAEA,MAAM,qBAAqB;AAEpB,SAAS,4BAA4B,UAA6C;AACvF,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,yBAAyB,IAAI,QAAQ,EAAG,QAAO;AACnD,SAAO;AACT;AAEA,eAAsB,wBAAwB,IAAkC;AAC9E,QAAM,OAAO,GAAG,cAAc,mBAAmB;AACjD,QAAM,WAAW,MAAM,KAAK,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;AACxD,QAAM,gBAAgB,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AACjE,QAAM,UAAU,8BAA8B,OAAO,CAAC,SAAS,CAAC,cAAc,IAAI,KAAK,IAAI,CAAC;AAC5F,MAAI,CAAC,QAAQ,OAAQ;AACrB,aAAW,QAAQ,SAAS;AAC1B,UAAM,SAAS,KAAK,OAAO;AAAA,MACzB,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK,eAAe;AAAA,MACjC,eAAe;AAAA,MACf,UAAU,KAAK,YAAY;AAAA,MAC3B,aAAa,mCAAmC;AAAA,IAClD,CAAC;AACD,OAAG,QAAQ,MAAM;AAAA,EACnB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,SAAS,sBAAsB,OAAuB;AAC3D,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,aAAa,QAAQ,QAAQ,mBAAmB,EAAE;AACxD,SAAO;AACT;AAEO,SAAS,4BAAqC;AACnD,QAAM,iBAAiB,kBAAkB,QAAQ,IAAI,aAAa,EAAE;AACpE,QAAM,kBAAkB,mBAAmB,QAAQ,QAAQ;AAC3D,QAAM,oBAAoB,kBAAkB,QAAQ,IAAI,mCAAmC,EAAE,MAAM;AACnG,SAAO,mBAAmB;AAC5B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/attachments/setup.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['attachments.*', 'attachments.view', 'attachments.manage'],\n },\n}\n\nexport default setup\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO,CAAC,iBAAiB,oBAAoB,oBAAoB;AAAA,EACnE;AACF;AAEA,IAAO,gBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/audit_logs/setup.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['audit_logs.*'],\n employee: ['audit_logs.undo_self'],\n },\n}\n\nexport default setup\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO,CAAC,cAAc;AAAA,IACtB,UAAU,CAAC,sBAAsB;AAAA,EACnC;AACF;AAEA,IAAO,gBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -232,12 +232,13 @@ async function GET(req) {
|
|
|
232
232
|
}
|
|
233
233
|
const groupsWithRole = rolePreference ? applySidebarPreference(groups, rolePreference) : groups;
|
|
234
234
|
const baseForUser = adoptSidebarDefaults(groupsWithRole);
|
|
235
|
-
const
|
|
236
|
-
|
|
235
|
+
const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub;
|
|
236
|
+
const preference = effectiveUserId ? await loadSidebarPreference(em, {
|
|
237
|
+
userId: effectiveUserId,
|
|
237
238
|
tenantId: auth.tenantId ?? null,
|
|
238
239
|
organizationId: auth.orgId ?? null,
|
|
239
240
|
locale
|
|
240
|
-
});
|
|
241
|
+
}) : null;
|
|
241
242
|
const withPreference = applySidebarPreference(baseForUser, preference);
|
|
242
243
|
const payload = {
|
|
243
244
|
groups: withPreference.map((group) => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/auth/api/admin/nav.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { hasAllFeatures } from '@open-mercato/shared/security/features'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { slugifySidebarId } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { applySidebarPreference, loadFirstRoleSidebarPreference, loadSidebarPreference } from '../../services/sidebarPreferencesService'\nimport { Role } from '../../data/entities'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst sidebarNavItemSchema: z.ZodType<{ href: string; title: string; defaultTitle: string; enabled: boolean; hidden?: boolean; children?: any[] }> = z.lazy(() =>\n z.object({\n href: z.string(),\n title: z.string(),\n defaultTitle: z.string(),\n enabled: z.boolean(),\n hidden: z.boolean().optional(),\n children: z.array(sidebarNavItemSchema).optional(),\n })\n)\n\nconst adminNavResponseSchema = z.object({\n groups: z.array(\n z.object({\n id: z.string(),\n name: z.string(),\n defaultName: z.string(),\n items: z.array(sidebarNavItemSchema),\n })\n ),\n})\n\nconst adminNavErrorSchema = z.object({\n error: z.string(),\n})\n\ntype SidebarItemNode = {\n href: string\n title: string\n defaultTitle: string\n enabled: boolean\n hidden?: boolean\n children?: SidebarItemNode[]\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { translate, locale } = await resolveTranslations()\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n const cache = resolve('cache') as any\n\n // Cache key is user + tenant + organization scoped\n const cacheKey = `nav:sidebar:${locale}:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}`\n // try {\n // if (cache) {\n // const cached = await cache.get(cacheKey)\n // if (cached) return NextResponse.json(cached)\n // }\n // } catch {}\n\n // Load ACL once; we'll evaluate features locally without multiple calls\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n\n // Build nav entries from discovered backend routes\n type Entry = {\n groupId: string\n groupName: string\n groupKey?: string\n title: string\n titleKey?: string\n href: string\n enabled: boolean\n order?: number\n priority?: number\n children?: Entry[]\n }\n const entries: Entry[] = []\n\n function capitalize(s: string) { return s.charAt(0).toUpperCase() + s.slice(1) }\n function deriveTitleFromPath(p: string) {\n const seg = p.split('/').filter(Boolean).pop() || ''\n return seg ? seg.split('-').map(capitalize).join(' ') : 'Home'\n }\n\n const ctx = { auth: { roles: auth.roles || [], sub: auth.sub, tenantId: auth.tenantId, orgId: auth.orgId } }\n const modules = getModules()\n for (const m of (modules as any[])) {\n const groupDefault = capitalize(m.id)\n for (const r of (m.backendRoutes || [])) {\n const href = (r.pattern ?? r.path ?? '') as string\n if (!href || href.includes('[')) continue\n if ((r as any).navHidden) continue\n const title = (r.title as string) || deriveTitleFromPath(href)\n const titleKey = (r as any).pageTitleKey ?? (r as any).titleKey\n const groupName = (r.group as string) || groupDefault\n const groupKey = (r as any).pageGroupKey ?? (r as any).groupKey\n const groupId = typeof groupKey === 'string' && groupKey ? groupKey : slugifySidebarId(groupName)\n const visible = r.visible ? await Promise.resolve(r.visible(ctx)) : true\n if (!visible) continue\n const enabled = r.enabled ? await Promise.resolve(r.enabled(ctx)) : true\n const requiredRoles = (r.requireRoles as string[]) || []\n if (requiredRoles.length) {\n const roles = auth.roles || []\n const ok = requiredRoles.some((role) => roles.includes(role))\n if (!ok) continue\n }\n const features = (r as any).requireFeatures as string[] | undefined\n if (!acl.isSuperAdmin && !hasAllFeatures(acl.features, features)) continue\n const order = (r as any).order as number | undefined\n const priority = ((r as any).priority as number | undefined) ?? order\n entries.push({ groupId, groupName, groupKey, title, titleKey, href, enabled, order, priority })\n }\n }\n\n // Parent-child relationships within the same group by href prefix\n const roots: any[] = []\n for (const e of entries) {\n let parent: any | undefined\n for (const p of entries) {\n if (p === e) continue\n if (p.groupId !== e.groupId) continue\n if (!e.href.startsWith(p.href + '/')) continue\n if (!parent || p.href.length > parent.href.length) parent = p\n }\n if (parent) {\n ;(parent as any).children = (parent as any).children || []\n ;(parent as any).children.push(e)\n } else {\n roots.push(e)\n }\n }\n\n // Add dynamic user entities into Data designer > User Entities\n const where: any = { isActive: true, showInSidebar: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n try {\n const entities = await em.find(CustomEntity as any, where as any, { orderBy: { label: 'asc' } as any })\n const items = (entities as any[]).map((e) => ({\n entityId: e.entityId,\n label: e.label,\n href: `/backend/entities/user/${encodeURIComponent(e.entityId)}/records`\n }))\n if (items.length) {\n const dd = roots.find((it: Entry) => it.groupKey === 'entities.nav.group' && it.titleKey === 'entities.nav.userEntities')\n if (dd) {\n const existing = dd.children || []\n const dynamic = items.map((it) => ({\n groupId: dd.groupId,\n groupName: dd.groupName,\n groupKey: dd.groupKey,\n title: it.label,\n href: it.href,\n enabled: true,\n order: 1000,\n priority: 1000,\n }))\n const byHref = new Map<string, Entry>()\n for (const c of existing) if (!byHref.has(c.href)) byHref.set(c.href, c)\n for (const c of dynamic) if (!byHref.has(c.href)) byHref.set(c.href, c)\n dd.children = Array.from(byHref.values())\n }\n }\n } catch (e) {\n console.error('Error loading user entities', e)\n }\n\n // Sort roots and children\n const sortItems = (arr: any[]) => {\n arr.sort((a, b) => {\n if (a.group !== b.group) return a.group.localeCompare(b.group)\n const ap = a.priority ?? a.order ?? 10000\n const bp = b.priority ?? b.order ?? 10000\n if (ap !== bp) return ap - bp\n return String(a.title).localeCompare(String(b.title))\n })\n for (const it of arr) if (it.children?.length) sortItems(it.children)\n }\n sortItems(roots)\n\n // Group into sidebar groups\n type GroupBucket = {\n id: string\n rawName: string\n key?: string\n weight: number\n entries: Entry[]\n }\n\n const groupBuckets = new Map<string, GroupBucket>()\n for (const entry of roots) {\n const weight = entry.priority ?? entry.order ?? 10_000\n if (!groupBuckets.has(entry.groupId)) {\n groupBuckets.set(entry.groupId, {\n id: entry.groupId,\n rawName: entry.groupName,\n key: entry.groupKey as string | undefined,\n weight,\n entries: [entry],\n })\n } else {\n const bucket = groupBuckets.get(entry.groupId)!\n bucket.entries.push(entry)\n if (weight < bucket.weight) bucket.weight = weight\n if (!bucket.key && entry.groupKey) bucket.key = entry.groupKey as string\n if (!bucket.rawName && entry.groupName) bucket.rawName = entry.groupName\n }\n }\n\n const toItem = (entry: Entry): SidebarItemNode => {\n const defaultTitle = entry.titleKey ? translate(entry.titleKey, entry.title) : entry.title\n return {\n href: entry.href,\n title: defaultTitle,\n defaultTitle,\n enabled: entry.enabled,\n children: entry.children?.map((child) => toItem(child)),\n }\n }\n\n const groups = Array.from(groupBuckets.values()).map((bucket) => {\n const defaultName = bucket.key ? translate(bucket.key, bucket.rawName) : bucket.rawName\n return {\n id: bucket.id,\n key: bucket.key,\n name: defaultName,\n defaultName,\n weight: bucket.weight,\n items: bucket.entries.map((entry) => toItem(entry)),\n }\n })\n const defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'customers.storage.nav.group',\n ]\n const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = defaultGroupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n const normalized =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n group.weight = normalized\n })\n\n let rolePreference = null\n if (Array.isArray(auth.roles) && auth.roles.length) {\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roleRecords = await em.find(Role, {\n name: { $in: auth.roles },\n ...roleScope,\n } as any)\n const roleIds = roleRecords.map((role: Role) => role.id)\n if (roleIds.length) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: auth.tenantId ?? null,\n locale,\n })\n }\n }\n\n const groupsWithRole = rolePreference ? applySidebarPreference(groups, rolePreference) : groups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n\n const preference = await loadSidebarPreference(em, {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n })\n\n const withPreference = applySidebarPreference(baseForUser, preference)\n\n const payload = {\n groups: withPreference.map((group) => ({\n id: group.id,\n name: group.name,\n defaultName: group.defaultName,\n items: (group.items as SidebarItemNode[]).map((item) => ({\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n children: item.children?.map((child) => ({\n href: child.href,\n title: child.title,\n defaultTitle: child.defaultTitle,\n enabled: child.enabled,\n hidden: child.hidden,\n })),\n })),\n })),\n }\n\n try {\n if (cache) {\n const tags = [\n `rbac:user:${auth.sub}`,\n auth.tenantId ? `rbac:tenant:${auth.tenantId}` : undefined,\n `nav:entities:${auth.tenantId || 'null'}`,\n `nav:locale:${locale}`,\n `nav:sidebar:user:${auth.sub}`,\n `nav:sidebar:scope:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}:${locale}`,\n ...(Array.isArray(auth.roles) ? auth.roles.map((role: string) => `nav:sidebar:role:${role}`) : []),\n ].filter(Boolean) as string[]\n await cache.set(cacheKey, payload, { tags })\n }\n } catch {}\n\n return NextResponse.json(payload)\n}\n\nfunction adoptSidebarDefaults(groups: ReturnType<typeof applySidebarPreference>) {\n const adoptItems = <T extends { title: string; defaultTitle?: string; children?: T[] }>(items: T[]): T[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Admin sidebar navigation',\n methods: {\n GET: {\n summary: 'Resolve sidebar entries',\n description:\n 'Returns the backend navigation tree available to the authenticated administrator after applying role and personal sidebar preferences.',\n responses: [\n { status: 200, description: 'Sidebar navigation structure', schema: adminNavResponseSchema },\n { status: 401, description: 'Unauthorized', schema: adminNavErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { hasAllFeatures } from '@open-mercato/shared/security/features'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { slugifySidebarId } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { applySidebarPreference, loadFirstRoleSidebarPreference, loadSidebarPreference } from '../../services/sidebarPreferencesService'\nimport { Role } from '../../data/entities'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst sidebarNavItemSchema: z.ZodType<{ href: string; title: string; defaultTitle: string; enabled: boolean; hidden?: boolean; children?: any[] }> = z.lazy(() =>\n z.object({\n href: z.string(),\n title: z.string(),\n defaultTitle: z.string(),\n enabled: z.boolean(),\n hidden: z.boolean().optional(),\n children: z.array(sidebarNavItemSchema).optional(),\n })\n)\n\nconst adminNavResponseSchema = z.object({\n groups: z.array(\n z.object({\n id: z.string(),\n name: z.string(),\n defaultName: z.string(),\n items: z.array(sidebarNavItemSchema),\n })\n ),\n})\n\nconst adminNavErrorSchema = z.object({\n error: z.string(),\n})\n\ntype SidebarItemNode = {\n href: string\n title: string\n defaultTitle: string\n enabled: boolean\n hidden?: boolean\n children?: SidebarItemNode[]\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { translate, locale } = await resolveTranslations()\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n const cache = resolve('cache') as any\n\n // Cache key is user + tenant + organization scoped\n const cacheKey = `nav:sidebar:${locale}:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}`\n // try {\n // if (cache) {\n // const cached = await cache.get(cacheKey)\n // if (cached) return NextResponse.json(cached)\n // }\n // } catch {}\n\n // Load ACL once; we'll evaluate features locally without multiple calls\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n\n // Build nav entries from discovered backend routes\n type Entry = {\n groupId: string\n groupName: string\n groupKey?: string\n title: string\n titleKey?: string\n href: string\n enabled: boolean\n order?: number\n priority?: number\n children?: Entry[]\n }\n const entries: Entry[] = []\n\n function capitalize(s: string) { return s.charAt(0).toUpperCase() + s.slice(1) }\n function deriveTitleFromPath(p: string) {\n const seg = p.split('/').filter(Boolean).pop() || ''\n return seg ? seg.split('-').map(capitalize).join(' ') : 'Home'\n }\n\n const ctx = { auth: { roles: auth.roles || [], sub: auth.sub, tenantId: auth.tenantId, orgId: auth.orgId } }\n const modules = getModules()\n for (const m of (modules as any[])) {\n const groupDefault = capitalize(m.id)\n for (const r of (m.backendRoutes || [])) {\n const href = (r.pattern ?? r.path ?? '') as string\n if (!href || href.includes('[')) continue\n if ((r as any).navHidden) continue\n const title = (r.title as string) || deriveTitleFromPath(href)\n const titleKey = (r as any).pageTitleKey ?? (r as any).titleKey\n const groupName = (r.group as string) || groupDefault\n const groupKey = (r as any).pageGroupKey ?? (r as any).groupKey\n const groupId = typeof groupKey === 'string' && groupKey ? groupKey : slugifySidebarId(groupName)\n const visible = r.visible ? await Promise.resolve(r.visible(ctx)) : true\n if (!visible) continue\n const enabled = r.enabled ? await Promise.resolve(r.enabled(ctx)) : true\n const requiredRoles = (r.requireRoles as string[]) || []\n if (requiredRoles.length) {\n const roles = auth.roles || []\n const ok = requiredRoles.some((role) => roles.includes(role))\n if (!ok) continue\n }\n const features = (r as any).requireFeatures as string[] | undefined\n if (!acl.isSuperAdmin && !hasAllFeatures(acl.features, features)) continue\n const order = (r as any).order as number | undefined\n const priority = ((r as any).priority as number | undefined) ?? order\n entries.push({ groupId, groupName, groupKey, title, titleKey, href, enabled, order, priority })\n }\n }\n\n // Parent-child relationships within the same group by href prefix\n const roots: any[] = []\n for (const e of entries) {\n let parent: any | undefined\n for (const p of entries) {\n if (p === e) continue\n if (p.groupId !== e.groupId) continue\n if (!e.href.startsWith(p.href + '/')) continue\n if (!parent || p.href.length > parent.href.length) parent = p\n }\n if (parent) {\n ;(parent as any).children = (parent as any).children || []\n ;(parent as any).children.push(e)\n } else {\n roots.push(e)\n }\n }\n\n // Add dynamic user entities into Data designer > User Entities\n const where: any = { isActive: true, showInSidebar: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n try {\n const entities = await em.find(CustomEntity as any, where as any, { orderBy: { label: 'asc' } as any })\n const items = (entities as any[]).map((e) => ({\n entityId: e.entityId,\n label: e.label,\n href: `/backend/entities/user/${encodeURIComponent(e.entityId)}/records`\n }))\n if (items.length) {\n const dd = roots.find((it: Entry) => it.groupKey === 'entities.nav.group' && it.titleKey === 'entities.nav.userEntities')\n if (dd) {\n const existing = dd.children || []\n const dynamic = items.map((it) => ({\n groupId: dd.groupId,\n groupName: dd.groupName,\n groupKey: dd.groupKey,\n title: it.label,\n href: it.href,\n enabled: true,\n order: 1000,\n priority: 1000,\n }))\n const byHref = new Map<string, Entry>()\n for (const c of existing) if (!byHref.has(c.href)) byHref.set(c.href, c)\n for (const c of dynamic) if (!byHref.has(c.href)) byHref.set(c.href, c)\n dd.children = Array.from(byHref.values())\n }\n }\n } catch (e) {\n console.error('Error loading user entities', e)\n }\n\n // Sort roots and children\n const sortItems = (arr: any[]) => {\n arr.sort((a, b) => {\n if (a.group !== b.group) return a.group.localeCompare(b.group)\n const ap = a.priority ?? a.order ?? 10000\n const bp = b.priority ?? b.order ?? 10000\n if (ap !== bp) return ap - bp\n return String(a.title).localeCompare(String(b.title))\n })\n for (const it of arr) if (it.children?.length) sortItems(it.children)\n }\n sortItems(roots)\n\n // Group into sidebar groups\n type GroupBucket = {\n id: string\n rawName: string\n key?: string\n weight: number\n entries: Entry[]\n }\n\n const groupBuckets = new Map<string, GroupBucket>()\n for (const entry of roots) {\n const weight = entry.priority ?? entry.order ?? 10_000\n if (!groupBuckets.has(entry.groupId)) {\n groupBuckets.set(entry.groupId, {\n id: entry.groupId,\n rawName: entry.groupName,\n key: entry.groupKey as string | undefined,\n weight,\n entries: [entry],\n })\n } else {\n const bucket = groupBuckets.get(entry.groupId)!\n bucket.entries.push(entry)\n if (weight < bucket.weight) bucket.weight = weight\n if (!bucket.key && entry.groupKey) bucket.key = entry.groupKey as string\n if (!bucket.rawName && entry.groupName) bucket.rawName = entry.groupName\n }\n }\n\n const toItem = (entry: Entry): SidebarItemNode => {\n const defaultTitle = entry.titleKey ? translate(entry.titleKey, entry.title) : entry.title\n return {\n href: entry.href,\n title: defaultTitle,\n defaultTitle,\n enabled: entry.enabled,\n children: entry.children?.map((child) => toItem(child)),\n }\n }\n\n const groups = Array.from(groupBuckets.values()).map((bucket) => {\n const defaultName = bucket.key ? translate(bucket.key, bucket.rawName) : bucket.rawName\n return {\n id: bucket.id,\n key: bucket.key,\n name: defaultName,\n defaultName,\n weight: bucket.weight,\n items: bucket.entries.map((entry) => toItem(entry)),\n }\n })\n const defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'customers.storage.nav.group',\n ]\n const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = defaultGroupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n const normalized =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n group.weight = normalized\n })\n\n let rolePreference = null\n if (Array.isArray(auth.roles) && auth.roles.length) {\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roleRecords = await em.find(Role, {\n name: { $in: auth.roles },\n ...roleScope,\n } as any)\n const roleIds = roleRecords.map((role: Role) => role.id)\n if (roleIds.length) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: auth.tenantId ?? null,\n locale,\n })\n }\n }\n\n const groupsWithRole = rolePreference ? applySidebarPreference(groups, rolePreference) : groups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n\n // For API key auth, use userId (the actual user) if available; otherwise skip user preferences\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n const preference = effectiveUserId\n ? await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n })\n : null\n\n const withPreference = applySidebarPreference(baseForUser, preference)\n\n const payload = {\n groups: withPreference.map((group) => ({\n id: group.id,\n name: group.name,\n defaultName: group.defaultName,\n items: (group.items as SidebarItemNode[]).map((item) => ({\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n children: item.children?.map((child) => ({\n href: child.href,\n title: child.title,\n defaultTitle: child.defaultTitle,\n enabled: child.enabled,\n hidden: child.hidden,\n })),\n })),\n })),\n }\n\n try {\n if (cache) {\n const tags = [\n `rbac:user:${auth.sub}`,\n auth.tenantId ? `rbac:tenant:${auth.tenantId}` : undefined,\n `nav:entities:${auth.tenantId || 'null'}`,\n `nav:locale:${locale}`,\n `nav:sidebar:user:${auth.sub}`,\n `nav:sidebar:scope:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}:${locale}`,\n ...(Array.isArray(auth.roles) ? auth.roles.map((role: string) => `nav:sidebar:role:${role}`) : []),\n ].filter(Boolean) as string[]\n await cache.set(cacheKey, payload, { tags })\n }\n } catch {}\n\n return NextResponse.json(payload)\n}\n\nfunction adoptSidebarDefaults(groups: ReturnType<typeof applySidebarPreference>) {\n const adoptItems = <T extends { title: string; defaultTitle?: string; children?: T[] }>(items: T[]): T[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Admin sidebar navigation',\n methods: {\n GET: {\n summary: 'Resolve sidebar entries',\n description:\n 'Returns the backend navigation tree available to the authenticated administrator after applying role and personal sidebar preferences.',\n responses: [\n { status: 200, description: 'Sidebar navigation structure', schema: adminNavResponseSchema },\n { status: 401, description: 'Unauthorized', schema: adminNavErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -11,15 +11,33 @@ async function POST(req) {
|
|
|
11
11
|
const email = String(form.get("email") ?? "");
|
|
12
12
|
const password = String(form.get("password") ?? "");
|
|
13
13
|
const remember = parseBooleanToken(form.get("remember")?.toString()) === true;
|
|
14
|
+
const tenantIdRaw = String(form.get("tenantId") ?? form.get("tenant") ?? "").trim();
|
|
14
15
|
const requireRoleRaw = String(form.get("requireRole") ?? form.get("role") ?? "").trim();
|
|
15
16
|
const requiredRoles = requireRoleRaw ? requireRoleRaw.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
16
|
-
const parsed = userLoginSchema.pick({ email: true, password: true }).safeParse({
|
|
17
|
+
const parsed = userLoginSchema.pick({ email: true, password: true, tenantId: true }).safeParse({
|
|
18
|
+
email,
|
|
19
|
+
password,
|
|
20
|
+
tenantId: tenantIdRaw || void 0
|
|
21
|
+
});
|
|
17
22
|
if (!parsed.success) {
|
|
18
23
|
return NextResponse.json({ ok: false, error: translate("auth.login.errors.invalidCredentials", "Invalid credentials") }, { status: 400 });
|
|
19
24
|
}
|
|
20
25
|
const container = await createRequestContainer();
|
|
21
26
|
const auth = container.resolve("authService");
|
|
22
|
-
const
|
|
27
|
+
const tenantId = parsed.data.tenantId ?? null;
|
|
28
|
+
let user = null;
|
|
29
|
+
if (tenantId) {
|
|
30
|
+
user = await auth.findUserByEmailAndTenant(parsed.data.email, tenantId);
|
|
31
|
+
} else {
|
|
32
|
+
const users = await auth.findUsersByEmail(parsed.data.email);
|
|
33
|
+
if (users.length > 1) {
|
|
34
|
+
return NextResponse.json({
|
|
35
|
+
ok: false,
|
|
36
|
+
error: translate("auth.login.errors.tenantRequired", "Use the login link provided with your tenant activation to continue.")
|
|
37
|
+
}, { status: 400 });
|
|
38
|
+
}
|
|
39
|
+
user = users[0] ?? null;
|
|
40
|
+
}
|
|
23
41
|
if (!user || !user.passwordHash) {
|
|
24
42
|
return NextResponse.json({ ok: false, error: translate("auth.login.errors.invalidCredentials", "Invalid email or password") }, { status: 401 });
|
|
25
43
|
}
|
|
@@ -28,24 +46,25 @@ async function POST(req) {
|
|
|
28
46
|
return NextResponse.json({ ok: false, error: translate("auth.login.errors.invalidCredentials", "Invalid email or password") }, { status: 401 });
|
|
29
47
|
}
|
|
30
48
|
if (requiredRoles.length) {
|
|
31
|
-
const userRoleNames2 = await auth.getUserRoles(user, user.tenantId ? String(user.tenantId) : null);
|
|
49
|
+
const userRoleNames2 = await auth.getUserRoles(user, tenantId ?? (user.tenantId ? String(user.tenantId) : null));
|
|
32
50
|
const authorized = requiredRoles.some((r) => userRoleNames2.includes(r));
|
|
33
51
|
if (!authorized) {
|
|
34
52
|
return NextResponse.json({ ok: false, error: translate("auth.login.errors.permissionDenied", "Not authorized for this area") }, { status: 403 });
|
|
35
53
|
}
|
|
36
54
|
}
|
|
37
55
|
await auth.updateLastLoginAt(user);
|
|
38
|
-
const
|
|
56
|
+
const resolvedTenantId = tenantId ?? (user.tenantId ? String(user.tenantId) : null);
|
|
57
|
+
const userRoleNames = await auth.getUserRoles(user, resolvedTenantId);
|
|
39
58
|
try {
|
|
40
59
|
const eventBus = container.resolve("eventBus");
|
|
41
60
|
void eventBus.emitEvent("query_index.coverage.warmup", {
|
|
42
|
-
tenantId:
|
|
61
|
+
tenantId: resolvedTenantId
|
|
43
62
|
}).catch(() => void 0);
|
|
44
63
|
} catch {
|
|
45
64
|
}
|
|
46
65
|
const token = signJwt({
|
|
47
66
|
sub: String(user.id),
|
|
48
|
-
tenantId:
|
|
67
|
+
tenantId: resolvedTenantId,
|
|
49
68
|
orgId: user.organizationId ? String(user.organizationId) : null,
|
|
50
69
|
email: user.email,
|
|
51
70
|
roles: userRoleNames
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/auth/api/login.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { userLoginSchema } from '@open-mercato/core/modules/auth/data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { signJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EventBus } from '@open-mercato/events/types'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\n// validation comes from userLoginSchema\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n const form = await req.formData()\n const email = String(form.get('email') ?? '')\n const password = String(form.get('password') ?? '')\n const remember = parseBooleanToken(form.get('remember')?.toString()) === true\n const requireRoleRaw = (String(form.get('requireRole') ?? form.get('role') ?? '')).trim()\n const requiredRoles = requireRoleRaw ? requireRoleRaw.split(',').map((s) => s.trim()).filter(Boolean) : []\n const parsed = userLoginSchema.pick({ email: true, password: true }).safeParse({
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,uBAAuB;AAChC,SAAS,8BAA8B;AAEvC,SAAS,eAAe;AACxB,SAAS,2BAA2B;AAEpC,SAAS,yBAAyB;AAIlC,eAAsB,KAAK,KAAc;AACvC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,OAAO,MAAM,IAAI,SAAS;AAChC,QAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,KAAK,EAAE;AAC5C,QAAM,WAAW,OAAO,KAAK,IAAI,UAAU,KAAK,EAAE;AAClD,QAAM,WAAW,kBAAkB,KAAK,IAAI,UAAU,GAAG,SAAS,CAAC,MAAM;AACzE,QAAM,iBAAkB,OAAO,KAAK,IAAI,aAAa,KAAK,KAAK,IAAI,MAAM,KAAK,EAAE,EAAG,KAAK;AACxF,QAAM,gBAAgB,iBAAiB,eAAe,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IAAI,CAAC;AACzG,QAAM,SAAS,gBAAgB,KAAK,EAAE,OAAO,MAAM,UAAU,KAAK,CAAC,EAAE,UAAU,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { userLoginSchema } from '@open-mercato/core/modules/auth/data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { signJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EventBus } from '@open-mercato/events/types'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\n// validation comes from userLoginSchema\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n const form = await req.formData()\n const email = String(form.get('email') ?? '')\n const password = String(form.get('password') ?? '')\n const remember = parseBooleanToken(form.get('remember')?.toString()) === true\n const tenantIdRaw = String(form.get('tenantId') ?? form.get('tenant') ?? '').trim()\n const requireRoleRaw = (String(form.get('requireRole') ?? form.get('role') ?? '')).trim()\n const requiredRoles = requireRoleRaw ? requireRoleRaw.split(',').map((s) => s.trim()).filter(Boolean) : []\n const parsed = userLoginSchema.pick({ email: true, password: true, tenantId: true }).safeParse({\n email,\n password,\n tenantId: tenantIdRaw || undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('auth.login.errors.invalidCredentials', 'Invalid credentials') }, { status: 400 })\n }\n const container = await createRequestContainer()\n const auth = (container.resolve('authService') as AuthService)\n const tenantId = parsed.data.tenantId ?? null\n let user = null\n if (tenantId) {\n user = await auth.findUserByEmailAndTenant(parsed.data.email, tenantId)\n } else {\n const users = await auth.findUsersByEmail(parsed.data.email)\n if (users.length > 1) {\n return NextResponse.json({\n ok: false,\n error: translate('auth.login.errors.tenantRequired', 'Use the login link provided with your tenant activation to continue.'),\n }, { status: 400 })\n }\n user = users[0] ?? null\n }\n if (!user || !user.passwordHash) {\n return NextResponse.json({ ok: false, error: translate('auth.login.errors.invalidCredentials', 'Invalid email or password') }, { status: 401 })\n }\n const ok = await auth.verifyPassword(user, parsed.data.password)\n if (!ok) {\n return NextResponse.json({ ok: false, error: translate('auth.login.errors.invalidCredentials', 'Invalid email or password') }, { status: 401 })\n }\n // Optional role requirement\n if (requiredRoles.length) {\n const userRoleNames = await auth.getUserRoles(user, tenantId ?? (user.tenantId ? String(user.tenantId) : null))\n const authorized = requiredRoles.some(r => userRoleNames.includes(r))\n if (!authorized) {\n return NextResponse.json({ ok: false, error: translate('auth.login.errors.permissionDenied', 'Not authorized for this area') }, { status: 403 })\n }\n }\n await auth.updateLastLoginAt(user)\n const resolvedTenantId = tenantId ?? (user.tenantId ? String(user.tenantId) : null)\n const userRoleNames = await auth.getUserRoles(user, resolvedTenantId)\n try {\n const eventBus = (container.resolve('eventBus') as EventBus)\n void eventBus.emitEvent('query_index.coverage.warmup', {\n tenantId: resolvedTenantId,\n }).catch(() => undefined)\n } catch {\n // optional warmup\n }\n const token = signJwt({ \n sub: String(user.id), \n tenantId: resolvedTenantId, \n orgId: user.organizationId ? String(user.organizationId) : null,\n email: user.email, \n roles: userRoleNames \n })\n const res = NextResponse.json({ ok: true, token, redirect: '/backend' })\n res.cookies.set('auth_token', token, { httpOnly: true, path: '/', sameSite: 'lax', secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 8 })\n if (remember) {\n const days = Number(process.env.REMEMBER_ME_DAYS || '30')\n const expiresAt = new Date(Date.now() + days * 24 * 60 * 60 * 1000)\n const sess = await auth.createSession(user, expiresAt)\n res.cookies.set('session_token', sess.token, { httpOnly: true, path: '/', sameSite: 'lax', secure: process.env.NODE_ENV === 'production', expires: expiresAt })\n }\n return res\n}\n\nconst loginRequestSchema = userLoginSchema.extend({\n password: z.string().min(6).describe('User password'),\n remember: z.enum(['on', '1', 'true']).optional().describe('Persist the session (submit `on`, `1`, or `true`).'),\n}).describe('Login form payload')\n\nconst loginSuccessSchema = z.object({\n ok: z.literal(true),\n token: z.string().describe('JWT token issued for subsequent API calls'),\n redirect: z.string().nullable().describe('Next location the client should navigate to'),\n})\n\nconst loginErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst loginMethodDoc: OpenApiMethodDoc = {\n summary: 'Authenticate user credentials',\n description: 'Validates the submitted credentials and issues a bearer token cookie for subsequent API calls.',\n tags: ['Authentication & Accounts'],\n requestBody: {\n contentType: 'application/x-www-form-urlencoded',\n schema: loginRequestSchema,\n description: 'Form-encoded payload captured from the login form.',\n },\n responses: [\n {\n status: 200,\n description: 'Authentication succeeded',\n schema: loginSuccessSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: loginErrorSchema },\n { status: 401, description: 'Invalid credentials', schema: loginErrorSchema },\n { status: 403, description: 'User lacks required role', schema: loginErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Authenticate user credentials',\n description: 'Accepts login form submissions and manages cookie/session issuance.',\n methods: {\n POST: loginMethodDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["userRoleNames"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
|
+
import { signJwt } from "@open-mercato/shared/lib/auth/jwt";
|
|
6
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
|
+
import { User } from "@open-mercato/core/modules/auth/data/entities";
|
|
9
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
10
|
+
import { buildPasswordSchema } from "@open-mercato/shared/lib/auth/passwordPolicy";
|
|
11
|
+
const profileResponseSchema = z.object({
|
|
12
|
+
email: z.string().email()
|
|
13
|
+
});
|
|
14
|
+
const passwordSchema = buildPasswordSchema();
|
|
15
|
+
const updateSchema = z.object({
|
|
16
|
+
email: z.string().email().optional(),
|
|
17
|
+
password: passwordSchema.optional()
|
|
18
|
+
}).refine((data) => Boolean(data.email || data.password), {
|
|
19
|
+
message: "Provide an email or password.",
|
|
20
|
+
path: ["email"]
|
|
21
|
+
});
|
|
22
|
+
const profileUpdateResponseSchema = z.object({
|
|
23
|
+
ok: z.literal(true),
|
|
24
|
+
email: z.string().email()
|
|
25
|
+
});
|
|
26
|
+
const metadata = {
|
|
27
|
+
GET: { requireAuth: true },
|
|
28
|
+
PUT: { requireAuth: true }
|
|
29
|
+
};
|
|
30
|
+
function buildCommandContext(container, auth, req) {
|
|
31
|
+
return {
|
|
32
|
+
container,
|
|
33
|
+
auth,
|
|
34
|
+
organizationScope: null,
|
|
35
|
+
selectedOrganizationId: auth.orgId ?? null,
|
|
36
|
+
organizationIds: auth.orgId ? [auth.orgId] : null,
|
|
37
|
+
request: req
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function GET(req) {
|
|
41
|
+
const { translate } = await resolveTranslations();
|
|
42
|
+
const auth = await getAuthFromRequest(req);
|
|
43
|
+
if (!auth?.sub) {
|
|
44
|
+
return NextResponse.json({ error: translate("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const container = await createRequestContainer();
|
|
48
|
+
const em = container.resolve("em");
|
|
49
|
+
const user = await findOneWithDecryption(
|
|
50
|
+
em,
|
|
51
|
+
User,
|
|
52
|
+
{ id: auth.sub, deletedAt: null },
|
|
53
|
+
void 0,
|
|
54
|
+
{ tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null }
|
|
55
|
+
);
|
|
56
|
+
if (!user) {
|
|
57
|
+
return NextResponse.json({ error: translate("auth.users.form.errors.notFound", "User not found") }, { status: 404 });
|
|
58
|
+
}
|
|
59
|
+
return NextResponse.json({ email: String(user.email) });
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error("auth.profile.load failed", err);
|
|
62
|
+
return NextResponse.json({ error: translate("auth.profile.form.errors.load", "Failed to load profile.") }, { status: 400 });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function PUT(req) {
|
|
66
|
+
const { translate } = await resolveTranslations();
|
|
67
|
+
const auth = await getAuthFromRequest(req);
|
|
68
|
+
if (!auth?.sub) {
|
|
69
|
+
return NextResponse.json({ error: translate("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const body = await req.json().catch(() => ({}));
|
|
73
|
+
const parsed = updateSchema.safeParse(body);
|
|
74
|
+
if (!parsed.success) {
|
|
75
|
+
return NextResponse.json(
|
|
76
|
+
{
|
|
77
|
+
error: translate("auth.profile.form.errors.invalid", "Invalid profile update."),
|
|
78
|
+
issues: parsed.error.issues
|
|
79
|
+
},
|
|
80
|
+
{ status: 400 }
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const container = await createRequestContainer();
|
|
84
|
+
const commandBus = container.resolve("commandBus");
|
|
85
|
+
const ctx = buildCommandContext(container, auth, req);
|
|
86
|
+
const { result } = await commandBus.execute(
|
|
87
|
+
"auth.users.update",
|
|
88
|
+
{
|
|
89
|
+
input: {
|
|
90
|
+
id: auth.sub,
|
|
91
|
+
email: parsed.data.email,
|
|
92
|
+
password: parsed.data.password
|
|
93
|
+
},
|
|
94
|
+
ctx
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
const authService = container.resolve("authService");
|
|
98
|
+
const roles = await authService.getUserRoles(result, result.tenantId ? String(result.tenantId) : null);
|
|
99
|
+
const jwt = signJwt({
|
|
100
|
+
sub: String(result.id),
|
|
101
|
+
tenantId: result.tenantId ? String(result.tenantId) : null,
|
|
102
|
+
orgId: result.organizationId ? String(result.organizationId) : null,
|
|
103
|
+
email: result.email,
|
|
104
|
+
roles
|
|
105
|
+
});
|
|
106
|
+
const res = NextResponse.json({ ok: true, email: String(result.email) });
|
|
107
|
+
res.cookies.set("auth_token", jwt, {
|
|
108
|
+
httpOnly: true,
|
|
109
|
+
path: "/",
|
|
110
|
+
sameSite: "lax",
|
|
111
|
+
secure: process.env.NODE_ENV === "production",
|
|
112
|
+
maxAge: 60 * 60 * 8
|
|
113
|
+
});
|
|
114
|
+
return res;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err instanceof CrudHttpError) {
|
|
117
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
118
|
+
}
|
|
119
|
+
console.error("auth.profile.update failed", err);
|
|
120
|
+
return NextResponse.json({ error: translate("auth.profile.form.errors.save", "Failed to update profile.") }, { status: 400 });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const openApi = {
|
|
124
|
+
tag: "Authentication & Accounts",
|
|
125
|
+
summary: "Profile settings",
|
|
126
|
+
methods: {
|
|
127
|
+
GET: {
|
|
128
|
+
summary: "Get current profile",
|
|
129
|
+
description: "Returns the email address for the signed-in user.",
|
|
130
|
+
responses: [
|
|
131
|
+
{ status: 200, description: "Profile payload", schema: profileResponseSchema },
|
|
132
|
+
{ status: 401, description: "Unauthorized", schema: z.object({ error: z.string() }) },
|
|
133
|
+
{ status: 404, description: "User not found", schema: z.object({ error: z.string() }) }
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
PUT: {
|
|
137
|
+
summary: "Update current profile",
|
|
138
|
+
description: "Updates the email address or password for the signed-in user.",
|
|
139
|
+
requestBody: {
|
|
140
|
+
contentType: "application/json",
|
|
141
|
+
schema: updateSchema
|
|
142
|
+
},
|
|
143
|
+
responses: [
|
|
144
|
+
{ status: 200, description: "Profile updated", schema: profileUpdateResponseSchema },
|
|
145
|
+
{ status: 400, description: "Invalid payload", schema: z.object({ error: z.string() }) },
|
|
146
|
+
{ status: 401, description: "Unauthorized", schema: z.object({ error: z.string() }) }
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
export {
|
|
152
|
+
GET,
|
|
153
|
+
PUT,
|
|
154
|
+
metadata,
|
|
155
|
+
openApi
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=route.js.map
|