@open-mercato/core 0.4.2-canary-10c7a8bf2a → 0.4.2-canary-c84cff7ed5
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 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- 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/attachments/components/AttachmentLibrary.js +4 -0
- 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/auth/api/admin/nav.js +4 -3
- package/dist/modules/auth/api/admin/nav.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/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 +14 -2
- 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 +13 -0
- 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 +4 -2
- package/dist/modules/auth/data/validators.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 +3 -0
- 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 +3 -3
- package/dist/modules/auth/services/authService.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/notifications.js +28 -0
- package/dist/modules/business_rules/notifications.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/analytics.js +27 -0
- package/dist/modules/catalog/analytics.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/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/lib/upgrade-actions.js +18 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- 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/customers/analytics.js +50 -0
- package/dist/modules/customers/analytics.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/dashboards/acl.js +2 -1
- package/dist/modules/dashboards/acl.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +187 -0
- package/dist/modules/dashboards/api/widgets/data/route.js.map +7 -0
- package/dist/modules/dashboards/cli.js +173 -1
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/di.js +11 -0
- package/dist/modules/dashboards/di.js.map +7 -0
- package/dist/modules/dashboards/lib/aggregations.js +162 -0
- package/dist/modules/dashboards/lib/aggregations.js.map +7 -0
- package/dist/modules/dashboards/lib/formatters.js +34 -0
- package/dist/modules/dashboards/lib/formatters.js.map +7 -0
- 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/seed/analytics.js +383 -0
- package/dist/modules/dashboards/seed/analytics.js.map +7 -0
- package/dist/modules/dashboards/services/analyticsRegistry.js +52 -0
- package/dist/modules/dashboards/services/analyticsRegistry.js.map +7 -0
- package/dist/modules/dashboards/services/widgetDataService.js +207 -0
- package/dist/modules/dashboards/services/widgetDataService.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +128 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +126 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +151 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +126 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js +16 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +123 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js +18 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +128 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js +21 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +211 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js +19 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +131 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js +19 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +153 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/config.js +22 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/config.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +180 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +7 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +25 -0
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js.map +7 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- 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/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/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/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 +94 -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 +219 -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 +105 -0
- package/dist/modules/notifications/lib/deliveryConfig.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/subscribers/deliver-notification.js +139 -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/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/query_index/components/QueryIndexesTable.js +7 -1
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- 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/sales/analytics.js +67 -0
- package/dist/modules/sales/analytics.js.map +7 -0
- 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/commands/documents.js +53 -0
- 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/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/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/workflows/backend/definitions/page.js +5 -0
- package/dist/modules/workflows/backend/definitions/page.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/page.js +3 -0
- package/dist/modules/workflows/backend/tasks/page.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/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 -1
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +2 -2
- 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/attachments/components/AttachmentLibrary.tsx +4 -0
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
- package/src/modules/auth/api/admin/nav.ts +10 -6
- 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/page.tsx +3 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +18 -2
- 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 +14 -0
- package/src/modules/auth/commands/users.ts +73 -2
- package/src/modules/auth/data/validators.ts +5 -2
- package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
- package/src/modules/auth/i18n/de.json +43 -1
- package/src/modules/auth/i18n/en.json +43 -1
- package/src/modules/auth/i18n/es.json +43 -1
- package/src/modules/auth/i18n/pl.json +43 -1
- package/src/modules/auth/lib/setup-app.ts +3 -0
- package/src/modules/auth/notifications.ts +109 -0
- package/src/modules/auth/services/authService.ts +4 -4
- 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/i18n/en.json +3 -1
- package/src/modules/business_rules/notifications.ts +25 -0
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
- package/src/modules/catalog/analytics.ts +24 -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/subscribers/low-stock-notification.ts +52 -0
- package/src/modules/configs/cli.ts +6 -0
- package/src/modules/configs/lib/upgrade-actions.ts +18 -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/customers/analytics.ts +47 -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/dashboards/acl.ts +1 -0
- package/src/modules/dashboards/api/widgets/data/route.ts +221 -0
- package/src/modules/dashboards/cli.ts +204 -1
- package/src/modules/dashboards/di.ts +9 -0
- package/src/modules/dashboards/i18n/de.json +115 -1
- package/src/modules/dashboards/i18n/en.json +115 -1
- package/src/modules/dashboards/i18n/es.json +115 -1
- package/src/modules/dashboards/i18n/pl.json +115 -1
- package/src/modules/dashboards/lib/__tests__/aggregations.test.ts +327 -0
- package/src/modules/dashboards/lib/__tests__/formatters.test.ts +128 -0
- package/src/modules/dashboards/lib/aggregations.ts +225 -0
- package/src/modules/dashboards/lib/formatters.ts +36 -0
- package/src/modules/dashboards/lib/role-widgets.ts +80 -0
- package/src/modules/dashboards/seed/analytics.ts +405 -0
- package/src/modules/dashboards/services/analyticsRegistry.ts +79 -0
- package/src/modules/dashboards/services/widgetDataService.ts +329 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +135 -0
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +133 -0
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +154 -0
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +133 -0
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +17 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +137 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/config.ts +20 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +135 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/config.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +220 -0
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/config.ts +21 -0
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +131 -0
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/top-customers/config.ts +21 -0
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +161 -0
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +24 -0
- package/src/modules/dashboards/widgets/dashboard/top-products/config.ts +27 -0
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +181 -0
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +24 -0
- package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -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/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/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
- package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
- 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 +110 -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 +231 -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 +145 -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 +300 -0
- package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
- package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
- package/src/modules/notifications/subscribers/deliver-notification.ts +175 -0
- package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
- package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
- 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/sales/analytics.ts +64 -0
- 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/commands/documents.ts +65 -0
- 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/notifications.client.ts +65 -0
- package/src/modules/sales/notifications.ts +82 -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/workflows/backend/definitions/page.tsx +5 -0
- package/src/modules/workflows/backend/instances/page.tsx +4 -1
- package/src/modules/workflows/backend/tasks/page.tsx +4 -1
- package/src/modules/workflows/i18n/en.json +3 -1
- package/src/modules/workflows/notifications.ts +25 -0
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/auth/backend/users/create/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { E } from '#generated/entities.ids.generated'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup, type CrudFieldOption } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { fetchRoleOptions } from '@open-mercato/core/modules/auth/backend/users/roleOptions'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype CreateUserFormValues = {\n email: string\n password: string\n tenantId: string | null\n organizationId: string | null\n roles: string[]\n} & Record<string, unknown>\n\ntype UserListResponse = {\n isSuperAdmin?: boolean\n}\n\ntype WidgetCatalogResponse = {\n items?: Array<{ id?: string | null; title?: string | null; description?: string | null }>\n}\n\ntype TenantAwareOrganizationSelectProps = {\n fieldId: string\n value: string | null\n setValue: (value: string | null) => void\n tenantId: string | null\n includeInactiveIds?: Iterable<string | null | undefined>\n}\n\nfunction TenantAwareOrganizationSelectInput({\n fieldId,\n value,\n setValue,\n tenantId,\n includeInactiveIds,\n}: TenantAwareOrganizationSelectProps) {\n const prevTenantRef = React.useRef<string | null>(tenantId)\n const hydratedRef = React.useRef(false)\n const handleChange = React.useCallback((next: string | null) => {\n setValue(next ?? null)\n }, [setValue])\n\n React.useEffect(() => {\n if (!hydratedRef.current) {\n hydratedRef.current = true\n prevTenantRef.current = tenantId\n return\n }\n if (prevTenantRef.current !== tenantId) {\n prevTenantRef.current = tenantId\n setValue(null)\n }\n }, [tenantId, setValue])\n\n return (\n <OrganizationSelect\n id={fieldId}\n value={value}\n onChange={handleChange}\n required\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n tenantId={tenantId}\n includeInactiveIds={includeInactiveIds}\n />\n )\n}\n\nexport default function CreateUserPage() {\n const t = useT()\n const [widgetCatalog, setWidgetCatalog] = React.useState<Array<{ id: string; title: string; description: string | null }>>([])\n const [widgetLoading, setWidgetLoading] = React.useState(true)\n const [widgetError, setWidgetError] = React.useState<string | null>(null)\n const [widgetMode, setWidgetMode] = React.useState<'inherit' | 'override'>('inherit')\n const [selectedWidgets, setSelectedWidgets] = React.useState<string[]>([])\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function loadCatalog() {\n setWidgetLoading(true)\n setWidgetError(null)\n try {\n const { ok, result } = await apiCall<WidgetCatalogResponse>('/api/dashboards/widgets/catalog')\n if (!ok) throw new Error('request_failed')\n if (!cancelled) {\n const rawItems: unknown[] = Array.isArray(result?.items) ? result?.items ?? [] : []\n const normalized = rawItems\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const idValue = entry.id\n const titleValue = entry.title\n const descriptionValue = entry.description\n const id = typeof idValue === 'string' ? idValue : null\n if (!id || !id.length) return null\n const title = typeof titleValue === 'string' && titleValue.length > 0 ? titleValue : id\n const description = typeof descriptionValue === 'string' && descriptionValue.length > 0 ? descriptionValue : null\n return { id, title, description }\n })\n .filter((item): item is { id: string; title: string; description: string | null } => item !== null)\n setWidgetCatalog(normalized)\n }\n } catch (err) {\n console.error('Failed to load dashboard widget catalog', err)\n if (!cancelled) {\n setWidgetError(t(\n 'auth.users.widgets.errors.load',\n 'Unable to load dashboard widgets. You can configure them later from the user page.',\n ))\n }\n } finally {\n if (!cancelled) setWidgetLoading(false)\n }\n }\n loadCatalog()\n return () => { cancelled = true }\n }, [t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadActor() {\n try {\n const { ok, result } = await apiCall<UserListResponse>('/api/auth/users?page=1&pageSize=1')\n if (!cancelled && ok) setActorIsSuperAdmin(Boolean(result?.isSuperAdmin))\n } catch (err) {\n console.error('Failed to resolve actor super admin flag', err)\n }\n }\n loadActor()\n return () => { cancelled = true }\n }, [])\n\n const toggleWidget = React.useCallback((id: string) => {\n setSelectedWidgets((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const loadRoleOptions = React.useCallback(async (query?: string): Promise<CrudFieldOption[]> => {\n if (actorIsSuperAdmin) {\n if (!selectedTenantId) return []\n return fetchRoleOptions(query, { tenantId: selectedTenantId })\n }\n return fetchRoleOptions(query)\n }, [actorIsSuperAdmin, selectedTenantId])\n\n const fields: CrudField[] = React.useMemo(() => {\n const items: CrudField[] = [\n { id: 'email', label: t('auth.users.form.field.email', 'Email'), type: 'text', required: true },\n { id: 'password', label: t('auth.users.form.field.password', 'Password'), type: 'text', required: true },\n ]\n if (actorIsSuperAdmin) {\n items.push({\n id: 'tenantId',\n label: t('auth.users.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => {\n const normalizedValue = typeof value === 'string'\n ? value\n : (typeof selectedTenantId === 'string' ? selectedTenantId : null)\n return (\n <TenantSelect\n id=\"tenantId\"\n value={normalizedValue}\n onChange={(next) => {\n const resolved = next ?? null\n setValue(resolved)\n setSelectedTenantId(resolved)\n }}\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n required\n />\n )\n },\n })\n }\n items.push({\n id: 'organizationId',\n label: t('auth.users.form.field.organization', 'Organization'),\n type: 'custom',\n component: ({ id, value, setValue }) => {\n const normalizedValue = typeof value === 'string' ? value : null\n return (\n <TenantAwareOrganizationSelectInput\n fieldId={id}\n value={normalizedValue}\n setValue={(next) => setValue(next ?? null)}\n tenantId={selectedTenantId}\n />\n )\n },\n })\n items.push({ id: 'roles', label: t('auth.users.form.field.roles', 'Roles'), type: 'tags', loadOptions: loadRoleOptions })\n return items\n }, [actorIsSuperAdmin, loadRoleOptions, selectedTenantId, t])\n\n const detailFieldIds = React.useMemo(() => {\n const base: string[] = ['email', 'password', 'organizationId', 'roles']\n if (actorIsSuperAdmin) base.splice(2, 0, 'tenantId')\n return base\n }, [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => [\n { id: 'details', title: t('auth.users.form.group.details', 'Details'), column: 1, fields: detailFieldIds },\n {\n id: 'acl',\n title: t('auth.users.form.group.access', 'Access'),\n column: 1,\n component: () => (\n <div className=\"text-sm text-muted-foreground\">\n {t('auth.users.form.aclHint', 'ACL can be edited after creating the user.')}\n </div>\n ),\n },\n { id: 'custom', title: t('auth.users.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n {\n id: 'dashboardWidgets',\n title: t('auth.users.form.group.widgets', 'Dashboard Widgets'),\n column: 2,\n component: () => (\n <DashboardWidgetSelector\n catalog={widgetCatalog}\n loading={widgetLoading}\n error={widgetError}\n mode={widgetMode}\n onModeChange={setWidgetMode}\n selected={selectedWidgets}\n onToggle={toggleWidget}\n />\n ),\n },\n ], [detailFieldIds, t, widgetCatalog, widgetError, widgetLoading, widgetMode, selectedWidgets, toggleWidget])\n\n const initialValues = React.useMemo<Partial<CreateUserFormValues>>(\n () => ({\n email: '',\n password: '',\n tenantId: null,\n organizationId: null,\n roles: [],\n }),\n [],\n )\n\n return (\n <Page>\n <PageBody>\n <CrudForm<CreateUserFormValues>\n title={t('auth.users.form.title.create', 'Create User')}\n backHref=\"/backend/users\"\n fields={fields}\n groups={groups}\n entityId={E.auth.user}\n initialValues={initialValues}\n submitLabel={t('auth.users.form.action.create', 'Create')}\n cancelHref=\"/backend/users\"\n successRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.created', 'User created'))}&type=success`}\n onSubmit={async (values) => {\n const customFields = collectCustomFieldValues(values)\n const payload: Record<string, unknown> = {\n email: values.email,\n password: values.password,\n organizationId: values.organizationId ? values.organizationId : null,\n roles: Array.isArray(values.roles) ? values.roles : [],\n ...(Object.keys(customFields).length ? { customFields } : {}),\n }\n if (actorIsSuperAdmin) {\n const rawTenant = typeof values.tenantId === 'string' ? values.tenantId.trim() : null\n payload.tenantId = rawTenant && rawTenant.length ? rawTenant : null\n }\n const { result: created } = await createCrud<{ id?: string }>('auth/users', payload)\n const newUserId = typeof created?.id === 'string' ? created.id : null\n\n if (widgetMode === 'override' && newUserId) {\n await updateCrud('dashboards/users/widgets', {\n userId: newUserId,\n mode: 'override',\n widgetIds: selectedWidgets,\n organizationId: values.organizationId ? values.organizationId : null,\n tenantId: actorIsSuperAdmin\n ? (typeof values.tenantId === 'string' && values.tenantId.length ? values.tenantId : null)\n : null,\n }, {\n errorMessage: t('auth.users.form.errors.widgetsAssign', 'Failed to assign dashboard widgets to the new user'),\n })\n }\n }}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction DashboardWidgetSelector({\n catalog,\n loading,\n error,\n mode,\n onModeChange,\n selected,\n onToggle,\n}: {\n catalog: Array<{ id: string; title: string; description: string | null }>\n loading: boolean\n error: string | null\n mode: 'inherit' | 'override'\n onModeChange: (mode: 'inherit' | 'override') => void\n selected: string[]\n onToggle: (id: string) => void\n}) {\n const t = useT()\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('auth.users.widgets.loading', 'Loading widgets\u2026')}\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n {!error && (\n <>\n <div className=\"flex items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n value=\"inherit\"\n checked={mode === 'inherit'}\n onChange={() => onModeChange('inherit')}\n />\n {t('auth.users.widgets.mode.inherit', 'Inherit from roles')}\n </label>\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n value=\"override\"\n checked={mode === 'override'}\n onChange={() => onModeChange('override')}\n />\n {t('auth.users.widgets.mode.override', 'Override for this user')}\n </label>\n </div>\n {mode === 'override' && (\n <div className=\"space-y-2\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => onToggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{widget.title}</div>\n {widget.description ? <div className=\"text-xs text-muted-foreground\">{widget.description}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n {mode === 'inherit' && (\n <div className=\"rounded-md border bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n {t('auth.users.widgets.mode.hint', 'New users inherit widgets from their assigned roles. Override to pick a custom set.')}\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { E } from '#generated/entities.ids.generated'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup, type CrudFieldOption } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { fetchRoleOptions } from '@open-mercato/core/modules/auth/backend/users/roleOptions'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\ntype CreateUserFormValues = {\n email: string\n password: string\n tenantId: string | null\n organizationId: string | null\n roles: string[]\n} & Record<string, unknown>\n\ntype UserListResponse = {\n isSuperAdmin?: boolean\n}\n\ntype WidgetCatalogResponse = {\n items?: Array<{ id?: string | null; title?: string | null; description?: string | null }>\n}\n\ntype TenantAwareOrganizationSelectProps = {\n fieldId: string\n value: string | null\n setValue: (value: string | null) => void\n tenantId: string | null\n includeInactiveIds?: Iterable<string | null | undefined>\n}\n\nfunction TenantAwareOrganizationSelectInput({\n fieldId,\n value,\n setValue,\n tenantId,\n includeInactiveIds,\n}: TenantAwareOrganizationSelectProps) {\n const prevTenantRef = React.useRef<string | null>(tenantId)\n const hydratedRef = React.useRef(false)\n const handleChange = React.useCallback((next: string | null) => {\n setValue(next ?? null)\n }, [setValue])\n\n React.useEffect(() => {\n if (!hydratedRef.current) {\n hydratedRef.current = true\n prevTenantRef.current = tenantId\n return\n }\n if (prevTenantRef.current !== tenantId) {\n prevTenantRef.current = tenantId\n setValue(null)\n }\n }, [tenantId, setValue])\n\n return (\n <OrganizationSelect\n id={fieldId}\n value={value}\n onChange={handleChange}\n required\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n tenantId={tenantId}\n includeInactiveIds={includeInactiveIds}\n />\n )\n}\n\nexport default function CreateUserPage() {\n const t = useT()\n const [widgetCatalog, setWidgetCatalog] = React.useState<Array<{ id: string; title: string; description: string | null }>>([])\n const [widgetLoading, setWidgetLoading] = React.useState(true)\n const [widgetError, setWidgetError] = React.useState<string | null>(null)\n const [widgetMode, setWidgetMode] = React.useState<'inherit' | 'override'>('inherit')\n const [selectedWidgets, setSelectedWidgets] = React.useState<string[]>([])\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])\n const passwordRequirements = React.useMemo(\n () => formatPasswordRequirements(passwordPolicy, t),\n [passwordPolicy, t],\n )\n const passwordDescription = React.useMemo(() => (\n passwordRequirements\n ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })\n : undefined\n ), [passwordRequirements, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadCatalog() {\n setWidgetLoading(true)\n setWidgetError(null)\n try {\n const { ok, result } = await apiCall<WidgetCatalogResponse>('/api/dashboards/widgets/catalog')\n if (!ok) throw new Error('request_failed')\n if (!cancelled) {\n const rawItems: unknown[] = Array.isArray(result?.items) ? result?.items ?? [] : []\n const normalized = rawItems\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const idValue = entry.id\n const titleValue = entry.title\n const descriptionValue = entry.description\n const id = typeof idValue === 'string' ? idValue : null\n if (!id || !id.length) return null\n const title = typeof titleValue === 'string' && titleValue.length > 0 ? titleValue : id\n const description = typeof descriptionValue === 'string' && descriptionValue.length > 0 ? descriptionValue : null\n return { id, title, description }\n })\n .filter((item): item is { id: string; title: string; description: string | null } => item !== null)\n setWidgetCatalog(normalized)\n }\n } catch (err) {\n console.error('Failed to load dashboard widget catalog', err)\n if (!cancelled) {\n setWidgetError(t(\n 'auth.users.widgets.errors.load',\n 'Unable to load dashboard widgets. You can configure them later from the user page.',\n ))\n }\n } finally {\n if (!cancelled) setWidgetLoading(false)\n }\n }\n loadCatalog()\n return () => { cancelled = true }\n }, [t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadActor() {\n try {\n const { ok, result } = await apiCall<UserListResponse>('/api/auth/users?page=1&pageSize=1')\n if (!cancelled && ok) setActorIsSuperAdmin(Boolean(result?.isSuperAdmin))\n } catch (err) {\n console.error('Failed to resolve actor super admin flag', err)\n }\n }\n loadActor()\n return () => { cancelled = true }\n }, [])\n\n const toggleWidget = React.useCallback((id: string) => {\n setSelectedWidgets((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const loadRoleOptions = React.useCallback(async (query?: string): Promise<CrudFieldOption[]> => {\n if (actorIsSuperAdmin) {\n if (!selectedTenantId) return []\n return fetchRoleOptions(query, { tenantId: selectedTenantId })\n }\n return fetchRoleOptions(query)\n }, [actorIsSuperAdmin, selectedTenantId])\n\n const fields: CrudField[] = React.useMemo(() => {\n const items: CrudField[] = [\n { id: 'email', label: t('auth.users.form.field.email', 'Email'), type: 'text', required: true },\n {\n id: 'password',\n label: t('auth.users.form.field.password', 'Password'),\n type: 'text',\n required: true,\n description: passwordDescription,\n },\n ]\n if (actorIsSuperAdmin) {\n items.push({\n id: 'tenantId',\n label: t('auth.users.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => {\n const normalizedValue = typeof value === 'string'\n ? value\n : (typeof selectedTenantId === 'string' ? selectedTenantId : null)\n return (\n <TenantSelect\n id=\"tenantId\"\n value={normalizedValue}\n onChange={(next) => {\n const resolved = next ?? null\n setValue(resolved)\n setSelectedTenantId(resolved)\n }}\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n required\n />\n )\n },\n })\n }\n items.push({\n id: 'organizationId',\n label: t('auth.users.form.field.organization', 'Organization'),\n type: 'custom',\n component: ({ id, value, setValue }) => {\n const normalizedValue = typeof value === 'string' ? value : null\n return (\n <TenantAwareOrganizationSelectInput\n fieldId={id}\n value={normalizedValue}\n setValue={(next) => setValue(next ?? null)}\n tenantId={selectedTenantId}\n />\n )\n },\n })\n items.push({ id: 'roles', label: t('auth.users.form.field.roles', 'Roles'), type: 'tags', loadOptions: loadRoleOptions })\n return items\n }, [actorIsSuperAdmin, loadRoleOptions, passwordDescription, selectedTenantId, t])\n\n const detailFieldIds = React.useMemo(() => {\n const base: string[] = ['email', 'password', 'organizationId', 'roles']\n if (actorIsSuperAdmin) base.splice(2, 0, 'tenantId')\n return base\n }, [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => [\n { id: 'details', title: t('auth.users.form.group.details', 'Details'), column: 1, fields: detailFieldIds },\n {\n id: 'acl',\n title: t('auth.users.form.group.access', 'Access'),\n column: 1,\n component: () => (\n <div className=\"text-sm text-muted-foreground\">\n {t('auth.users.form.aclHint', 'ACL can be edited after creating the user.')}\n </div>\n ),\n },\n { id: 'custom', title: t('auth.users.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n {\n id: 'dashboardWidgets',\n title: t('auth.users.form.group.widgets', 'Dashboard Widgets'),\n column: 2,\n component: () => (\n <DashboardWidgetSelector\n catalog={widgetCatalog}\n loading={widgetLoading}\n error={widgetError}\n mode={widgetMode}\n onModeChange={setWidgetMode}\n selected={selectedWidgets}\n onToggle={toggleWidget}\n />\n ),\n },\n ], [detailFieldIds, t, widgetCatalog, widgetError, widgetLoading, widgetMode, selectedWidgets, toggleWidget])\n\n const initialValues = React.useMemo<Partial<CreateUserFormValues>>(\n () => ({\n email: '',\n password: '',\n tenantId: null,\n organizationId: null,\n roles: [],\n }),\n [],\n )\n\n return (\n <Page>\n <PageBody>\n <CrudForm<CreateUserFormValues>\n title={t('auth.users.form.title.create', 'Create User')}\n backHref=\"/backend/users\"\n fields={fields}\n groups={groups}\n entityId={E.auth.user}\n initialValues={initialValues}\n submitLabel={t('auth.users.form.action.create', 'Create')}\n cancelHref=\"/backend/users\"\n successRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.created', 'User created'))}&type=success`}\n onSubmit={async (values) => {\n const customFields = collectCustomFieldValues(values)\n const payload: Record<string, unknown> = {\n email: values.email,\n password: values.password,\n organizationId: values.organizationId ? values.organizationId : null,\n roles: Array.isArray(values.roles) ? values.roles : [],\n ...(Object.keys(customFields).length ? { customFields } : {}),\n }\n if (actorIsSuperAdmin) {\n const rawTenant = typeof values.tenantId === 'string' ? values.tenantId.trim() : null\n payload.tenantId = rawTenant && rawTenant.length ? rawTenant : null\n }\n const { result: created } = await createCrud<{ id?: string }>('auth/users', payload)\n const newUserId = typeof created?.id === 'string' ? created.id : null\n\n if (widgetMode === 'override' && newUserId) {\n await updateCrud('dashboards/users/widgets', {\n userId: newUserId,\n mode: 'override',\n widgetIds: selectedWidgets,\n organizationId: values.organizationId ? values.organizationId : null,\n tenantId: actorIsSuperAdmin\n ? (typeof values.tenantId === 'string' && values.tenantId.length ? values.tenantId : null)\n : null,\n }, {\n errorMessage: t('auth.users.form.errors.widgetsAssign', 'Failed to assign dashboard widgets to the new user'),\n })\n }\n }}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction DashboardWidgetSelector({\n catalog,\n loading,\n error,\n mode,\n onModeChange,\n selected,\n onToggle,\n}: {\n catalog: Array<{ id: string; title: string; description: string | null }>\n loading: boolean\n error: string | null\n mode: 'inherit' | 'override'\n onModeChange: (mode: 'inherit' | 'override') => void\n selected: string[]\n onToggle: (id: string) => void\n}) {\n const t = useT()\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('auth.users.widgets.loading', 'Loading widgets\u2026')}\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n {!error && (\n <>\n <div className=\"flex items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n value=\"inherit\"\n checked={mode === 'inherit'}\n onChange={() => onModeChange('inherit')}\n />\n {t('auth.users.widgets.mode.inherit', 'Inherit from roles')}\n </label>\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n value=\"override\"\n checked={mode === 'override'}\n onChange={() => onModeChange('override')}\n />\n {t('auth.users.widgets.mode.override', 'Override for this user')}\n </label>\n </div>\n {mode === 'override' && (\n <div className=\"space-y-2\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => onToggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{widget.title}</div>\n {widget.description ? <div className=\"text-xs text-muted-foreground\">{widget.description}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n {mode === 'inherit' && (\n <div className=\"rounded-md border bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n {t('auth.users.widgets.mode.hint', 'New users inherit widgets from their assigned roles. Override to pick a custom set.')}\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -334,9 +334,9 @@ function UsersListPage() {
|
|
|
334
334
|
onSortingChange: setSorting,
|
|
335
335
|
perspective: { tableId: "auth.users.list" },
|
|
336
336
|
rowActions: (row) => /* @__PURE__ */ jsx(RowActions, { items: [
|
|
337
|
-
{ label: t("common.edit", "Edit"), href: `/backend/users/${row.id}/edit` },
|
|
338
|
-
{ label: t("auth.users.list.actions.showRoles", "Show roles"), href: `/backend/roles?userId=${encodeURIComponent(row.id)}` },
|
|
339
|
-
{ label: t("common.delete", "Delete"), destructive: true, onSelect: () => {
|
|
337
|
+
{ id: "edit", label: t("common.edit", "Edit"), href: `/backend/users/${row.id}/edit` },
|
|
338
|
+
{ id: "show-roles", label: t("auth.users.list.actions.showRoles", "Show roles"), href: `/backend/roles?userId=${encodeURIComponent(row.id)}` },
|
|
339
|
+
{ id: "delete", label: t("common.delete", "Delete"), destructive: true, onSelect: () => {
|
|
340
340
|
void handleDelete(row);
|
|
341
341
|
} }
|
|
342
342
|
] }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/auth/backend/users/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { buildOrganizationTreeOptions, formatOrganizationTreeLabel, type OrganizationTreeNode, type OrganizationTreeOption } from '@open-mercato/core/modules/directory/lib/tree'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype Row = {\n id: string\n email: string\n organizationId: string | null\n organizationName?: string | null\n tenantId: string | null\n tenantName?: string | null\n roles: string[]\n}\n\ntype FilterOption = { value: string; label: string }\n\nasync function fetchOrganizationFilterOptions(): Promise<FilterOption[]> {\n const search = new URLSearchParams()\n search.set('view', 'tree')\n search.set('status', 'all')\n try {\n const call = await apiCall<{ items?: OrganizationTreeNode[] }>(\n `/api/directory/organizations?${search.toString()}`,\n )\n if (!call.ok) return []\n const nodes = Array.isArray(call.result?.items) ? (call.result!.items as OrganizationTreeNode[]) : []\n const flattened: OrganizationTreeOption[] = buildOrganizationTreeOptions(nodes)\n return flattened\n .filter((opt) => typeof opt.value === 'string' && opt.value.length > 0)\n .map((opt) => {\n const baseLabel = opt.name && opt.name.length > 0 ? opt.name : opt.value\n const depth = typeof opt.depth === 'number' ? opt.depth : 0\n const label = `${formatOrganizationTreeLabel(baseLabel, depth)}${opt.isActive === false ? ' (inactive)' : ''}`\n return { value: opt.value, label }\n })\n } catch {\n return []\n }\n}\n\nasync function fetchRoleFilterOptions(query?: string): Promise<FilterOption[]> {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', '50')\n if (query && query.trim()) search.set('search', query.trim())\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/auth/roles?${search.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items : []\n return items\n .map((item: any): FilterOption | null => {\n const id = typeof item?.id === 'string' ? item.id : null\n const name = typeof item?.name === 'string' ? item.name : null\n if (!id || !name) return null\n return { value: id, label: name }\n })\n .filter((opt: FilterOption | null): opt is FilterOption => opt !== null)\n } catch {\n return []\n }\n}\n\nasync function fetchRoleOptionsByIds(ids: string[]): Promise<FilterOption[]> {\n const unique = Array.from(new Set(ids.filter((id) => typeof id === 'string' && id.trim() !== '')))\n if (unique.length === 0) return []\n const results = await Promise.all(unique.map(async (id) => {\n try {\n const search = new URLSearchParams()\n search.set('id', id)\n search.set('page', '1')\n search.set('pageSize', '1')\n const call = await apiCall<{ items?: unknown[] }>(`/api/auth/roles?${search.toString()}`)\n if (!call.ok) return null\n const data = call.result\n const items = Array.isArray(data?.items) ? (data?.items as unknown[]) : []\n const match = items.find((item) => {\n if (!item || typeof item !== 'object') return false\n const entry = item as Record<string, unknown>\n return typeof entry.id === 'string' && entry.id === id\n })\n const record = match && typeof match === 'object' ? (match as Record<string, unknown>) : null\n const name = typeof record?.name === 'string' ? record.name : null\n if (!name) return null\n return { value: id, label: name }\n } catch {\n return null\n }\n }))\n return results.filter((opt: FilterOption | null): opt is FilterOption => opt !== null)\n}\n\nfunction mergeOptions(existing: FilterOption[], next: FilterOption[]): FilterOption[] {\n if (!next.length) return existing\n const map = new Map<string, FilterOption>()\n for (const opt of existing) map.set(opt.value, opt)\n for (const opt of next) map.set(opt.value, opt)\n return Array.from(map.values()).sort((a, b) => a.label.localeCompare(b.label))\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\nexport default function UsersListPage() {\n const searchParams = useSearchParams()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n const t = useT()\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'email', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [organizationOptions, setOrganizationOptions] = React.useState<FilterOption[]>([])\n const [roleOptions, setRoleOptions] = React.useState<FilterOption[]>([])\n const [roleLabelToId, setRoleLabelToId] = React.useState<Record<string, string>>({})\n const [roleIdToLabel, setRoleIdToLabel] = React.useState<Record<string, string>>({})\n const [roleFilterDirty, setRoleFilterDirty] = React.useState(false)\n\n const roleIdsFromUrl = React.useMemo(() => {\n if (!searchParams) return [] as string[]\n const raw = searchParams.getAll('roleId')\n return raw.filter((id): id is string => typeof id === 'string' && id.trim() !== '')\n }, [searchParams])\n\n const roleIdsFromUrlKey = React.useMemo(() => roleIdsFromUrl.join('|'), [roleIdsFromUrl])\n const organizationId = typeof filterValues.organizationId === 'string' && filterValues.organizationId ? filterValues.organizationId : undefined\n\n const applyRoleOptions = React.useCallback((opts: FilterOption[]) => {\n if (!opts.length) return\n setRoleOptions((prev) => mergeOptions(prev, opts))\n setRoleLabelToId((prev) => {\n if (!opts.length) return prev\n const next = { ...prev }\n for (const opt of opts) next[opt.label] = opt.value\n return next\n })\n setRoleIdToLabel((prev) => {\n if (!opts.length) return prev\n const next = { ...prev }\n for (const opt of opts) next[opt.value] = opt.label\n return next\n })\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n fetchOrganizationFilterOptions().then((opts) => {\n if (!cancelled) setOrganizationOptions(opts)\n })\n return () => { cancelled = true }\n }, [scopeVersion])\n\n React.useEffect(() => {\n let cancelled = false\n fetchRoleFilterOptions().then((opts) => {\n if (!cancelled) applyRoleOptions(opts)\n })\n return () => { cancelled = true }\n }, [applyRoleOptions])\n\n React.useEffect(() => {\n if (roleFilterDirty) return\n if (roleIdsFromUrl.length === 0) {\n setFilterValues((prev) => {\n if (!Array.isArray(prev.roles) || prev.roles.length === 0) return prev\n const next = { ...prev }\n delete (next as any).roles\n return next\n })\n return\n }\n const missing = roleIdsFromUrl.filter((id) => !roleIdToLabel[id])\n if (missing.length === 0) {\n const labels = roleIdsFromUrl\n .map((id) => roleIdToLabel[id])\n .filter((label): label is string => typeof label === 'string' && label.length > 0)\n setFilterValues((prev) => {\n const current = Array.isArray(prev.roles) ? prev.roles as string[] : []\n if (arraysEqual(current, labels)) return prev\n return { ...prev, roles: labels }\n })\n return\n }\n let cancelled = false\n ;(async () => {\n const fetched = await fetchRoleOptionsByIds(missing)\n if (cancelled) return\n if (fetched.length) applyRoleOptions(fetched)\n const labelMap = new Map<string, string>()\n for (const opt of fetched) labelMap.set(opt.value, opt.label)\n for (const id of roleIdsFromUrl) {\n if (roleIdToLabel[id]) labelMap.set(id, roleIdToLabel[id])\n }\n const labels = roleIdsFromUrl\n .map((id) => labelMap.get(id))\n .filter((label): label is string => typeof label === 'string' && label.length > 0)\n if (labels.length) {\n setFilterValues((prev) => {\n const current = Array.isArray(prev.roles) ? prev.roles as string[] : []\n if (arraysEqual(current, labels)) return prev\n return { ...prev, roles: labels }\n })\n }\n })()\n return () => { cancelled = true }\n }, [roleFilterDirty, roleIdsFromUrlKey, roleIdsFromUrl, roleIdToLabel, applyRoleOptions])\n\n React.useEffect(() => {\n setRoleFilterDirty(false)\n }, [roleIdsFromUrlKey])\n\n const loadRoleOptions = React.useCallback(async (query?: string) => {\n const opts = await fetchRoleFilterOptions(query)\n if (opts.length) applyRoleOptions(opts)\n return opts\n }, [applyRoleOptions])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'organizationId',\n label: 'Organization',\n type: 'select',\n options: organizationOptions,\n },\n {\n id: 'roles',\n label: 'Roles',\n type: 'tags',\n placeholder: 'Filter by roles',\n options: roleOptions,\n loadOptions: loadRoleOptions,\n },\n ], [organizationOptions, roleOptions, loadRoleOptions])\n\n const roleIdsFromFilter = React.useMemo(() => {\n const raw = Array.isArray(filterValues.roles) ? filterValues.roles as string[] : []\n return raw\n .map((label) => roleLabelToId[label])\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n }, [filterValues.roles, roleLabelToId])\n\n const effectiveRoleIds = React.useMemo(() => {\n if (roleFilterDirty) return roleIdsFromFilter\n if (roleIdsFromFilter.length > 0) return roleIdsFromFilter\n return roleIdsFromUrl\n }, [roleFilterDirty, roleIdsFromFilter, roleIdsFromUrl])\n\n const normalizedRoleIds = React.useMemo(() => {\n if (!effectiveRoleIds.length) return [] as string[]\n const unique = Array.from(new Set(effectiveRoleIds))\n unique.sort()\n return unique\n }, [effectiveRoleIds])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n const org = typeof values.organizationId === 'string' ? values.organizationId.trim() : ''\n if (org) next.organizationId = org\n const rawRoles = Array.isArray(values.roles) ? (values.roles as string[]) : []\n const sanitizedRoles = rawRoles.map((role) => role.trim()).filter((role) => role.length > 0)\n if (sanitizedRoles.length) next.roles = sanitizedRoles\n setFilterValues(next)\n setRoleFilterDirty(true)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setRoleFilterDirty(true)\n setPage(1)\n }, [])\n\n const params = React.useMemo(() => {\n const p = new URLSearchParams()\n p.set('page', String(page))\n p.set('pageSize', '50')\n if (search) p.set('search', search)\n if (organizationId) p.set('organizationId', organizationId)\n if (normalizedRoleIds.length) {\n for (const id of normalizedRoleIds) p.append('roleId', id)\n }\n return p.toString()\n }, [page, search, organizationId, normalizedRoleIds])\n\n const { data: usersData, isLoading } = useQuery({\n queryKey: ['users', params, scopeVersion],\n queryFn: async () => {\n const call = await apiCall<{ items: Row[]; total: number; totalPages: number; isSuperAdmin?: boolean }>(\n `/api/auth/users?${params}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('auth.users.list.error.load', 'Failed to load users'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const rows = usersData?.items || []\n const total = usersData?.total || 0\n const totalPages = usersData?.totalPages || 1\n const isSuperAdmin = !!usersData?.isSuperAdmin\n const rowsWithOrgNames: Row[] = React.useMemo(() => rows.map(row => ({\n ...row,\n organizationName: row.organizationName ?? (row.organizationId ?? undefined),\n tenantName: row.tenantName ?? (row.tenantId ?? undefined),\n })), [rows])\n const showTenantColumn = React.useMemo(\n () => isSuperAdmin && rowsWithOrgNames.some((row) => row.tenantName),\n [isSuperAdmin, rowsWithOrgNames],\n )\n const columns = React.useMemo<ColumnDef<Row>[]>(() => {\n const base: ColumnDef<Row>[] = [\n { accessorKey: 'email', header: 'Email' },\n { accessorKey: 'organizationName', header: 'Organization' },\n { accessorKey: 'roles', header: 'Roles', cell: ({ row }) => (row.original.roles || []).join(', ') },\n ]\n if (showTenantColumn) {\n base.splice(1, 0, { accessorKey: 'tenantName', header: 'Tenant' })\n }\n return base\n }, [showTenantColumn])\n\n const handleDelete = React.useCallback(async (row: Row) => {\n if (!window.confirm(t('auth.users.list.confirmDelete', 'Delete user \"{email}\"?', { email: row.email }))) return\n const deleteErrorMessage = t('auth.users.list.error.delete', 'Failed to delete user')\n try {\n const call = await apiCall(`/api/auth/users?id=${encodeURIComponent(row.id)}`, { method: 'DELETE' })\n if (!call.ok) {\n await raiseCrudError(call.response, deleteErrorMessage)\n }\n flash(t('auth.users.flash.deleted', 'User deleted'), 'success')\n await queryClient.invalidateQueries({ queryKey: ['users'] })\n } catch (error) {\n const message = error instanceof Error ? error.message : deleteErrorMessage\n flash(message, 'error')\n }\n }, [queryClient, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('auth.users.list.title', 'Users')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/users/create\">{t('common.create', 'Create')}</Link>\n </Button>\n )}\n columns={columns}\n data={rowsWithOrgNames}\n searchValue={search}\n onSearchChange={handleSearchChange}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n perspective={{ tableId: 'auth.users.list' }}\n rowActions={(row) => (\n <RowActions items={[\n { label: t('common.edit', 'Edit'), href: `/backend/users/${row.id}/edit` },\n { label: t('auth.users.list.actions.showRoles', 'Show roles'), href: `/backend/roles?userId=${encodeURIComponent(row.id)}` },\n { label: t('common.delete', 'Delete'), destructive: true, onSelect: () => { void handleDelete(row) } },\n ]} />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { buildOrganizationTreeOptions, formatOrganizationTreeLabel, type OrganizationTreeNode, type OrganizationTreeOption } from '@open-mercato/core/modules/directory/lib/tree'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype Row = {\n id: string\n email: string\n organizationId: string | null\n organizationName?: string | null\n tenantId: string | null\n tenantName?: string | null\n roles: string[]\n}\n\ntype FilterOption = { value: string; label: string }\n\nasync function fetchOrganizationFilterOptions(): Promise<FilterOption[]> {\n const search = new URLSearchParams()\n search.set('view', 'tree')\n search.set('status', 'all')\n try {\n const call = await apiCall<{ items?: OrganizationTreeNode[] }>(\n `/api/directory/organizations?${search.toString()}`,\n )\n if (!call.ok) return []\n const nodes = Array.isArray(call.result?.items) ? (call.result!.items as OrganizationTreeNode[]) : []\n const flattened: OrganizationTreeOption[] = buildOrganizationTreeOptions(nodes)\n return flattened\n .filter((opt) => typeof opt.value === 'string' && opt.value.length > 0)\n .map((opt) => {\n const baseLabel = opt.name && opt.name.length > 0 ? opt.name : opt.value\n const depth = typeof opt.depth === 'number' ? opt.depth : 0\n const label = `${formatOrganizationTreeLabel(baseLabel, depth)}${opt.isActive === false ? ' (inactive)' : ''}`\n return { value: opt.value, label }\n })\n } catch {\n return []\n }\n}\n\nasync function fetchRoleFilterOptions(query?: string): Promise<FilterOption[]> {\n const search = new URLSearchParams()\n search.set('page', '1')\n search.set('pageSize', '50')\n if (query && query.trim()) search.set('search', query.trim())\n try {\n const call = await apiCall<{ items?: unknown[] }>(`/api/auth/roles?${search.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items : []\n return items\n .map((item: any): FilterOption | null => {\n const id = typeof item?.id === 'string' ? item.id : null\n const name = typeof item?.name === 'string' ? item.name : null\n if (!id || !name) return null\n return { value: id, label: name }\n })\n .filter((opt: FilterOption | null): opt is FilterOption => opt !== null)\n } catch {\n return []\n }\n}\n\nasync function fetchRoleOptionsByIds(ids: string[]): Promise<FilterOption[]> {\n const unique = Array.from(new Set(ids.filter((id) => typeof id === 'string' && id.trim() !== '')))\n if (unique.length === 0) return []\n const results = await Promise.all(unique.map(async (id) => {\n try {\n const search = new URLSearchParams()\n search.set('id', id)\n search.set('page', '1')\n search.set('pageSize', '1')\n const call = await apiCall<{ items?: unknown[] }>(`/api/auth/roles?${search.toString()}`)\n if (!call.ok) return null\n const data = call.result\n const items = Array.isArray(data?.items) ? (data?.items as unknown[]) : []\n const match = items.find((item) => {\n if (!item || typeof item !== 'object') return false\n const entry = item as Record<string, unknown>\n return typeof entry.id === 'string' && entry.id === id\n })\n const record = match && typeof match === 'object' ? (match as Record<string, unknown>) : null\n const name = typeof record?.name === 'string' ? record.name : null\n if (!name) return null\n return { value: id, label: name }\n } catch {\n return null\n }\n }))\n return results.filter((opt: FilterOption | null): opt is FilterOption => opt !== null)\n}\n\nfunction mergeOptions(existing: FilterOption[], next: FilterOption[]): FilterOption[] {\n if (!next.length) return existing\n const map = new Map<string, FilterOption>()\n for (const opt of existing) map.set(opt.value, opt)\n for (const opt of next) map.set(opt.value, opt)\n return Array.from(map.values()).sort((a, b) => a.label.localeCompare(b.label))\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\nexport default function UsersListPage() {\n const searchParams = useSearchParams()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n const t = useT()\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'email', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [organizationOptions, setOrganizationOptions] = React.useState<FilterOption[]>([])\n const [roleOptions, setRoleOptions] = React.useState<FilterOption[]>([])\n const [roleLabelToId, setRoleLabelToId] = React.useState<Record<string, string>>({})\n const [roleIdToLabel, setRoleIdToLabel] = React.useState<Record<string, string>>({})\n const [roleFilterDirty, setRoleFilterDirty] = React.useState(false)\n\n const roleIdsFromUrl = React.useMemo(() => {\n if (!searchParams) return [] as string[]\n const raw = searchParams.getAll('roleId')\n return raw.filter((id): id is string => typeof id === 'string' && id.trim() !== '')\n }, [searchParams])\n\n const roleIdsFromUrlKey = React.useMemo(() => roleIdsFromUrl.join('|'), [roleIdsFromUrl])\n const organizationId = typeof filterValues.organizationId === 'string' && filterValues.organizationId ? filterValues.organizationId : undefined\n\n const applyRoleOptions = React.useCallback((opts: FilterOption[]) => {\n if (!opts.length) return\n setRoleOptions((prev) => mergeOptions(prev, opts))\n setRoleLabelToId((prev) => {\n if (!opts.length) return prev\n const next = { ...prev }\n for (const opt of opts) next[opt.label] = opt.value\n return next\n })\n setRoleIdToLabel((prev) => {\n if (!opts.length) return prev\n const next = { ...prev }\n for (const opt of opts) next[opt.value] = opt.label\n return next\n })\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n fetchOrganizationFilterOptions().then((opts) => {\n if (!cancelled) setOrganizationOptions(opts)\n })\n return () => { cancelled = true }\n }, [scopeVersion])\n\n React.useEffect(() => {\n let cancelled = false\n fetchRoleFilterOptions().then((opts) => {\n if (!cancelled) applyRoleOptions(opts)\n })\n return () => { cancelled = true }\n }, [applyRoleOptions])\n\n React.useEffect(() => {\n if (roleFilterDirty) return\n if (roleIdsFromUrl.length === 0) {\n setFilterValues((prev) => {\n if (!Array.isArray(prev.roles) || prev.roles.length === 0) return prev\n const next = { ...prev }\n delete (next as any).roles\n return next\n })\n return\n }\n const missing = roleIdsFromUrl.filter((id) => !roleIdToLabel[id])\n if (missing.length === 0) {\n const labels = roleIdsFromUrl\n .map((id) => roleIdToLabel[id])\n .filter((label): label is string => typeof label === 'string' && label.length > 0)\n setFilterValues((prev) => {\n const current = Array.isArray(prev.roles) ? prev.roles as string[] : []\n if (arraysEqual(current, labels)) return prev\n return { ...prev, roles: labels }\n })\n return\n }\n let cancelled = false\n ;(async () => {\n const fetched = await fetchRoleOptionsByIds(missing)\n if (cancelled) return\n if (fetched.length) applyRoleOptions(fetched)\n const labelMap = new Map<string, string>()\n for (const opt of fetched) labelMap.set(opt.value, opt.label)\n for (const id of roleIdsFromUrl) {\n if (roleIdToLabel[id]) labelMap.set(id, roleIdToLabel[id])\n }\n const labels = roleIdsFromUrl\n .map((id) => labelMap.get(id))\n .filter((label): label is string => typeof label === 'string' && label.length > 0)\n if (labels.length) {\n setFilterValues((prev) => {\n const current = Array.isArray(prev.roles) ? prev.roles as string[] : []\n if (arraysEqual(current, labels)) return prev\n return { ...prev, roles: labels }\n })\n }\n })()\n return () => { cancelled = true }\n }, [roleFilterDirty, roleIdsFromUrlKey, roleIdsFromUrl, roleIdToLabel, applyRoleOptions])\n\n React.useEffect(() => {\n setRoleFilterDirty(false)\n }, [roleIdsFromUrlKey])\n\n const loadRoleOptions = React.useCallback(async (query?: string) => {\n const opts = await fetchRoleFilterOptions(query)\n if (opts.length) applyRoleOptions(opts)\n return opts\n }, [applyRoleOptions])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n {\n id: 'organizationId',\n label: 'Organization',\n type: 'select',\n options: organizationOptions,\n },\n {\n id: 'roles',\n label: 'Roles',\n type: 'tags',\n placeholder: 'Filter by roles',\n options: roleOptions,\n loadOptions: loadRoleOptions,\n },\n ], [organizationOptions, roleOptions, loadRoleOptions])\n\n const roleIdsFromFilter = React.useMemo(() => {\n const raw = Array.isArray(filterValues.roles) ? filterValues.roles as string[] : []\n return raw\n .map((label) => roleLabelToId[label])\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n }, [filterValues.roles, roleLabelToId])\n\n const effectiveRoleIds = React.useMemo(() => {\n if (roleFilterDirty) return roleIdsFromFilter\n if (roleIdsFromFilter.length > 0) return roleIdsFromFilter\n return roleIdsFromUrl\n }, [roleFilterDirty, roleIdsFromFilter, roleIdsFromUrl])\n\n const normalizedRoleIds = React.useMemo(() => {\n if (!effectiveRoleIds.length) return [] as string[]\n const unique = Array.from(new Set(effectiveRoleIds))\n unique.sort()\n return unique\n }, [effectiveRoleIds])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n const org = typeof values.organizationId === 'string' ? values.organizationId.trim() : ''\n if (org) next.organizationId = org\n const rawRoles = Array.isArray(values.roles) ? (values.roles as string[]) : []\n const sanitizedRoles = rawRoles.map((role) => role.trim()).filter((role) => role.length > 0)\n if (sanitizedRoles.length) next.roles = sanitizedRoles\n setFilterValues(next)\n setRoleFilterDirty(true)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setRoleFilterDirty(true)\n setPage(1)\n }, [])\n\n const params = React.useMemo(() => {\n const p = new URLSearchParams()\n p.set('page', String(page))\n p.set('pageSize', '50')\n if (search) p.set('search', search)\n if (organizationId) p.set('organizationId', organizationId)\n if (normalizedRoleIds.length) {\n for (const id of normalizedRoleIds) p.append('roleId', id)\n }\n return p.toString()\n }, [page, search, organizationId, normalizedRoleIds])\n\n const { data: usersData, isLoading } = useQuery({\n queryKey: ['users', params, scopeVersion],\n queryFn: async () => {\n const call = await apiCall<{ items: Row[]; total: number; totalPages: number; isSuperAdmin?: boolean }>(\n `/api/auth/users?${params}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('auth.users.list.error.load', 'Failed to load users'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const rows = usersData?.items || []\n const total = usersData?.total || 0\n const totalPages = usersData?.totalPages || 1\n const isSuperAdmin = !!usersData?.isSuperAdmin\n const rowsWithOrgNames: Row[] = React.useMemo(() => rows.map(row => ({\n ...row,\n organizationName: row.organizationName ?? (row.organizationId ?? undefined),\n tenantName: row.tenantName ?? (row.tenantId ?? undefined),\n })), [rows])\n const showTenantColumn = React.useMemo(\n () => isSuperAdmin && rowsWithOrgNames.some((row) => row.tenantName),\n [isSuperAdmin, rowsWithOrgNames],\n )\n const columns = React.useMemo<ColumnDef<Row>[]>(() => {\n const base: ColumnDef<Row>[] = [\n { accessorKey: 'email', header: 'Email' },\n { accessorKey: 'organizationName', header: 'Organization' },\n { accessorKey: 'roles', header: 'Roles', cell: ({ row }) => (row.original.roles || []).join(', ') },\n ]\n if (showTenantColumn) {\n base.splice(1, 0, { accessorKey: 'tenantName', header: 'Tenant' })\n }\n return base\n }, [showTenantColumn])\n\n const handleDelete = React.useCallback(async (row: Row) => {\n if (!window.confirm(t('auth.users.list.confirmDelete', 'Delete user \"{email}\"?', { email: row.email }))) return\n const deleteErrorMessage = t('auth.users.list.error.delete', 'Failed to delete user')\n try {\n const call = await apiCall(`/api/auth/users?id=${encodeURIComponent(row.id)}`, { method: 'DELETE' })\n if (!call.ok) {\n await raiseCrudError(call.response, deleteErrorMessage)\n }\n flash(t('auth.users.flash.deleted', 'User deleted'), 'success')\n await queryClient.invalidateQueries({ queryKey: ['users'] })\n } catch (error) {\n const message = error instanceof Error ? error.message : deleteErrorMessage\n flash(message, 'error')\n }\n }, [queryClient, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('auth.users.list.title', 'Users')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/users/create\">{t('common.create', 'Create')}</Link>\n </Button>\n )}\n columns={columns}\n data={rowsWithOrgNames}\n searchValue={search}\n onSearchChange={handleSearchChange}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n perspective={{ tableId: 'auth.users.list' }}\n rowActions={(row) => (\n <RowActions items={[\n { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/users/${row.id}/edit` },\n { id: 'show-roles', label: t('auth.users.list.actions.showRoles', 'Show roles'), href: `/backend/roles?userId=${encodeURIComponent(row.id)}` },\n { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => { void handleDelete(row) } },\n ]} />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/modules/auth/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { TenantDataEncryptionService } from "@open-mercato/shared/lib/encryption
|
|
|
13
13
|
import { decryptWithAesGcm } from "@open-mercato/shared/lib/encryption/aes";
|
|
14
14
|
import { env } from "process";
|
|
15
15
|
import crypto from "node:crypto";
|
|
16
|
+
import { formatPasswordRequirements, getPasswordPolicy, validatePassword } from "@open-mercato/shared/lib/auth/passwordPolicy";
|
|
16
17
|
const addUser = {
|
|
17
18
|
command: "add-user",
|
|
18
19
|
async run(rest) {
|
|
@@ -30,6 +31,7 @@ const addUser = {
|
|
|
30
31
|
console.error("Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]");
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
34
|
+
if (!ensurePasswordPolicy(password)) return;
|
|
33
35
|
const { resolve } = await createRequestContainer();
|
|
34
36
|
const em = resolve("em");
|
|
35
37
|
const org = await findOneWithDecryption(
|
|
@@ -95,6 +97,15 @@ function hashSecret(value) {
|
|
|
95
97
|
if (!value) return null;
|
|
96
98
|
return crypto.createHash("sha256").update(normalizeKeyInput(value)).digest("hex").slice(0, 12);
|
|
97
99
|
}
|
|
100
|
+
function ensurePasswordPolicy(password) {
|
|
101
|
+
const policy = getPasswordPolicy();
|
|
102
|
+
const result = validatePassword(password, policy);
|
|
103
|
+
if (result.ok) return true;
|
|
104
|
+
const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback);
|
|
105
|
+
const suffix = requirements ? `: ${requirements}` : "";
|
|
106
|
+
console.error(`Password does not meet the requirements${suffix}.`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
98
109
|
async function withEncryptionDebugDisabled(fn) {
|
|
99
110
|
const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG;
|
|
100
111
|
process.env.TENANT_DATA_ENCRYPTION_DEBUG = "no";
|
|
@@ -375,6 +386,7 @@ const setupApp = {
|
|
|
375
386
|
console.error("Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee]");
|
|
376
387
|
return;
|
|
377
388
|
}
|
|
389
|
+
if (!ensurePasswordPolicy(password)) return;
|
|
378
390
|
const { resolve } = await createRequestContainer();
|
|
379
391
|
const em = resolve("em");
|
|
380
392
|
const roleNames = rolesCsv ? rolesCsv.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
@@ -532,6 +544,7 @@ const setPassword = {
|
|
|
532
544
|
console.error("Usage: mercato auth set-password --email <email> --password <newPassword>");
|
|
533
545
|
return;
|
|
534
546
|
}
|
|
547
|
+
if (!ensurePasswordPolicy(password)) return;
|
|
535
548
|
const { resolve } = await createRequestContainer();
|
|
536
549
|
const em = resolve("em");
|
|
537
550
|
const emailHash = computeEmailHash(email);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/auth/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { hash } from 'bcryptjs'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { ensureRoles, setupInitialTenant } from './lib/setup-app'\nimport { normalizeTenantId } from './lib/tenantAccess'\nimport { computeEmailHash } from './lib/emailHash'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { env } from 'process'\nimport type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport crypto from 'node:crypto'\n\nconst addUser: ModuleCli = {\n command: 'add-user',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const email = args.email\n const password = args.password\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org)\n const rolesCsv = (args.roles ?? '').trim()\n if (!email || !password || !organizationId) {\n console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const org =\n (await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )) ?? null\n if (!org) throw new Error('Organization not found')\n const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null\n const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null\n const u = em.create(User, {\n email,\n emailHash: computeEmailHash(email),\n passwordHash: await hash(password, 10),\n isConfirmed: true,\n organizationId: org.id,\n tenantId: org.tenant.id,\n })\n await em.persistAndFlush(u)\n if (rolesCsv) {\n const names = rolesCsv.split(',').map(s => s.trim()).filter(Boolean)\n for (const name of names) {\n let role = await em.findOne(Role, { name, tenantId: normalizedTenantId })\n if (!role && normalizedTenantId !== null) {\n role = await em.findOne(Role, { name, tenantId: null })\n }\n if (!role) {\n role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: new Date() })\n await em.persistAndFlush(role)\n } else if (normalizedTenantId !== null && role.tenantId !== normalizedTenantId) {\n role.tenantId = normalizedTenantId\n await em.persistAndFlush(role)\n }\n const link = em.create(UserRole, { user: u, role })\n await em.persistAndFlush(link)\n }\n }\n console.log('User created with id', u.id)\n },\n}\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/^['\"]|['\"]$/g, '')\n}\n\nfunction hashSecret(value: string | null | undefined): string | null {\n if (!value) return null\n return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)\n}\n\nasync function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {\n const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'\n try {\n return await fn()\n } finally {\n if (previous === undefined) {\n delete process.env.TENANT_DATA_ENCRYPTION_DEBUG\n } else {\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous\n }\n }\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nconst seedRoles: ModuleCli = {\n command: 'seed-roles',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n if (!key) continue\n const value = rest[i + 1]\n if (value) args[key] = value\n }\n const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n if (tenantId) {\n await ensureRoles(em, { tenantId })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', tenantId)\n return\n }\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to seed.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (!id) continue\n await ensureRoles(em, { tenantId: id })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', id)\n }\n },\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenantId as string) ?? (args.tenant as string) ?? (args.tenant_id as string) ?? null\n const organizationId = (args.organizationId as string) ?? (args.orgId as string) ?? (args.org as string) ?? null\n const oldKey = (args['old-key'] as string) ?? (args.oldKey as string) ?? null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantId) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n })\n console.log('[rotate-encryption-key] key fingerprints', {\n oldKey: hashSecret(oldKey),\n currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY),\n })\n }\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n const conn: any = (em as any).getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n const meta = (em as any)?.getMetadata?.()?.get?.(User)\n const tableName = meta?.tableName || 'users'\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n const printedDek = new Set<string>()\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (scopeTenantId: string, scopeOrganizationId: string): Promise<number> => {\n if (debug && !printedDek.has(scopeTenantId)) {\n printedDek.add(scopeTenantId)\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),\n encryptionService.getDek(scopeTenantId),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n tenantId: scopeTenantId,\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n }\n const rawRows = await conn.execute(\n `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,\n [scopeTenantId, scopeOrganizationId],\n )\n const rows = Array.isArray(rawRows) ? rawRows : []\n const pending = rotate\n ? rows\n : rows.filter((row: any) => !isEncryptedPayload(row?.email))\n if (!pending.length) return 0\n console.log(\n `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? ' (dry-run)' : ''}.`\n )\n if (dryRun) return 0\n const ids = pending.map((row: any) => String(row.id))\n const users = rotate\n ? await em.find(\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n )\n : await findWithDecryption(\n em,\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n {},\n { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService },\n )\n const usersById = new Map(users.map((user) => [String(user.id), user]))\n let updated = 0\n for (const row of pending) {\n const user = usersById.get(String(row.id))\n if (!user) continue\n const rawEmail = typeof row.email === 'string' ? row.email : String(row.email ?? '')\n if (!rawEmail) continue\n if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {\n continue\n }\n let plainEmail = rawEmail\n if (rotate && isEncryptedPayload(rawEmail) && oldKms) {\n if (debug) {\n console.log('[rotate-encryption-key] decrypting', {\n userId: row.id,\n tenantId: scopeTenantId,\n organizationId: scopeOrganizationId,\n })\n }\n let oldDek = oldDekCache.get(scopeTenantId) ?? null\n if (!oldDekCache.has(scopeTenantId)) {\n oldDek = await oldKms.getTenantDek(scopeTenantId)\n oldDekCache.set(scopeTenantId, oldDek)\n }\n const maybeEmail = decryptWithOldKey(rawEmail, oldDek)\n if (typeof maybeEmail !== 'string' || isEncryptedPayload(maybeEmail)) continue\n plainEmail = maybeEmail\n }\n if (!plainEmail) continue\n const encrypted = await encryptionService.encryptEntityPayload(\n 'auth:user',\n { email: plainEmail },\n scopeTenantId,\n scopeOrganizationId,\n )\n const nextEmail = encrypted.email as string | undefined\n if (nextEmail && nextEmail !== user.email) {\n user.email = nextEmail as any\n user.emailHash = (encrypted as any).emailHash ?? computeEmailHash(plainEmail)\n em.persist(user)\n updated += 1\n }\n }\n if (updated > 0) {\n await em.flush()\n }\n return updated\n }\n\n if (tenantId && organizationId) {\n const updated = await processScope(String(tenantId), String(organizationId))\n if (!updated) {\n console.log('All auth user emails already encrypted for the selected scope.')\n } else {\n console.log(`Encrypted ${updated} auth user email(s).`)\n }\n return\n }\n\n const organizations = await em.find(Organization, {})\n if (!organizations.length) {\n console.log('No organizations found; nothing to encrypt.')\n return\n }\n let total = 0\n for (const org of organizations) {\n const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null\n const scopeOrganizationId = org.id ? String(org.id) : null\n if (!scopeTenantId || !scopeOrganizationId) continue\n total += await processScope(scopeTenantId, scopeOrganizationId)\n }\n if (total > 0) {\n console.log(`Encrypted ${total} auth user email(s) across all organizations.`)\n } else {\n console.log('All auth user emails already encrypted across all organizations.')\n }\n },\n}\n\n// will be exported at the bottom with all commands\n\nconst addOrganization: ModuleCli = {\n command: 'add-org',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const name = args.name || args.orgName\n if (!name) {\n console.error('Usage: mercato auth add-org --name <organization name>')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n // Create tenant implicitly for simplicity\n const tenant = em.create(Tenant, { name: `${name} Tenant` })\n await em.persistAndFlush(tenant)\n const org = em.create(Organization, { name, tenant })\n await em.persistAndFlush(org)\n await rebuildHierarchyForTenant(em, String(tenant.id))\n console.log('Organization created with id', org.id, 'in tenant', tenant.id)\n },\n}\n\nconst setupApp: ModuleCli = {\n command: 'setup',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const orgName = args.orgName || args.name\n const email = args.email\n const password = args.password\n const rolesCsv = (args.roles ?? 'superadmin,admin,employee').trim()\n if (!orgName || !email || !password) {\n console.error('Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee]')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const roleNames = rolesCsv\n ? rolesCsv.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined\n\n try {\n const result = await setupInitialTenant(em, {\n orgName,\n roleNames,\n primaryUser: { email, password, confirm: true },\n includeDerivedUsers: true,\n })\n\n if (result.reusedExistingUser) {\n console.log('\u26A0\uFE0F Existing initial user detected during setup.')\n console.log(`\u26A0\uFE0F Email: ${email}`)\n console.log('\u26A0\uFE0F Updated roles if missing and reused tenant/organization.')\n }\n\n if(env.NODE_ENV !== 'test') { \n for (const snapshot of result.users) {\n if (snapshot.created) {\n if (snapshot.user.email === email && password) {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email, 'password:', password)\n } else {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email)\n }\n } else {\n console.log(`Updated user ${snapshot.user.email}`)\n }\n }\n }\n\n if(env.NODE_ENV !== 'test') console.log('\u2705 Setup complete:', { tenantId: result.tenantId, organizationId: result.organizationId })\n } catch (err) {\n if (err instanceof Error && err.message === 'USER_EXISTS') {\n console.error('Setup aborted: user already exists with the provided email.')\n return\n }\n throw err\n }\n },\n}\n\nconst listOrganizations: ModuleCli = {\n command: 'list-orgs',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const orgs = await findWithDecryption(\n em,\n Organization,\n {},\n { populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n \n if (orgs.length === 0) {\n console.log('No organizations found')\n return\n }\n \n console.log(`Found ${orgs.length} organization(s):`)\n console.log('')\n console.log('ID | Name | Tenant ID | Created')\n console.log('-------------------------------------|-------------------------|-------------------------------------|-------------------')\n \n for (const org of orgs) {\n const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'\n const id = org.id || 'N/A'\n const tenantId = org.tenant?.id || 'N/A'\n const name = (org.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`)\n }\n },\n}\n\nconst listTenants: ModuleCli = {\n command: 'list-tenants',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const tenants = await em.find(Tenant, {})\n \n if (tenants.length === 0) {\n console.log('No tenants found')\n return\n }\n \n console.log(`Found ${tenants.length} tenant(s):`)\n console.log('')\n console.log('ID | Name | Created')\n console.log('-------------------------------------|-------------------------|-------------------')\n \n for (const tenant of tenants) {\n const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : 'N/A'\n const id = tenant.id || 'N/A'\n const name = (tenant.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${created}`)\n }\n },\n}\n\nconst listUsers: ModuleCli = {\n command: 'list-users',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n \n // Build query with optional filters\n const where: any = {}\n if (args.organizationId || args.orgId || args.org) {\n where.organizationId = args.organizationId || args.orgId || args.org\n }\n if (args.tenantId || args.tenant) {\n where.tenantId = args.tenantId || args.tenant\n }\n \n const users = await em.find(User, where)\n \n if (users.length === 0) {\n console.log('No users found')\n return\n }\n \n console.log(`Found ${users.length} user(s):`)\n console.log('')\n console.log('ID | Email | Name | Organization ID | Tenant ID | Roles')\n console.log('-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------')\n \n for (const user of users) {\n // Get user roles separately\n const userRoles = await findWithDecryption(\n em,\n UserRole,\n { user: user.id },\n { populate: ['role'] },\n { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null },\n )\n const roles = userRoles.map((ur: any) => ur.role?.name).filter(Boolean).join(', ') || 'None'\n \n // Get organization and tenant names if IDs exist\n let orgName = 'N/A'\n let tenantName = 'N/A'\n \n if (user.organizationId) {\n const org = await em.findOne(Organization, { id: user.organizationId })\n orgName = org?.name?.substring(0, 19) + '...' || user.organizationId.substring(0, 8) + '...'\n }\n \n if (user.tenantId) {\n const tenant = await em.findOne(Tenant, { id: user.tenantId })\n tenantName = tenant?.name?.substring(0, 19) + '...' || user.tenantId.substring(0, 8) + '...'\n }\n \n const id = user.id || 'N/A'\n const email = (user.email || 'N/A').padEnd(23)\n const name = (user.name || 'Unnamed').padEnd(23)\n \n console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`)\n }\n },\n}\n\nconst setPassword: ModuleCli = {\n command: 'set-password',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const email = args.email\n const password = args.password\n \n if (!email || !password) {\n console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')\n return\n }\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const emailHash = computeEmailHash(email)\n const user = await em.findOne(User, { $or: [{ email }, { emailHash }] })\n \n if (!user) {\n console.error(`User with email \"${email}\" not found`)\n return\n }\n \n user.passwordHash = await hash(password, 10)\n await em.persistAndFlush(user)\n \n console.log(`\u2705 Password updated successfully for user: ${email}`)\n },\n}\n\n// Export the full CLI list\nexport default [addUser, seedRoles, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword]\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { hash } from 'bcryptjs'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { ensureRoles, setupInitialTenant } from './lib/setup-app'\nimport { normalizeTenantId } from './lib/tenantAccess'\nimport { computeEmailHash } from './lib/emailHash'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { env } from 'process'\nimport type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport crypto from 'node:crypto'\nimport { formatPasswordRequirements, getPasswordPolicy, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\nconst addUser: ModuleCli = {\n command: 'add-user',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const email = args.email\n const password = args.password\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org)\n const rolesCsv = (args.roles ?? '').trim()\n if (!email || !password || !organizationId) {\n console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const org =\n (await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )) ?? null\n if (!org) throw new Error('Organization not found')\n const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null\n const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null\n const u = em.create(User, {\n email,\n emailHash: computeEmailHash(email),\n passwordHash: await hash(password, 10),\n isConfirmed: true,\n organizationId: org.id,\n tenantId: org.tenant.id,\n })\n await em.persistAndFlush(u)\n if (rolesCsv) {\n const names = rolesCsv.split(',').map(s => s.trim()).filter(Boolean)\n for (const name of names) {\n let role = await em.findOne(Role, { name, tenantId: normalizedTenantId })\n if (!role && normalizedTenantId !== null) {\n role = await em.findOne(Role, { name, tenantId: null })\n }\n if (!role) {\n role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: new Date() })\n await em.persistAndFlush(role)\n } else if (normalizedTenantId !== null && role.tenantId !== normalizedTenantId) {\n role.tenantId = normalizedTenantId\n await em.persistAndFlush(role)\n }\n const link = em.create(UserRole, { user: u, role })\n await em.persistAndFlush(link)\n }\n }\n console.log('User created with id', u.id)\n },\n}\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/^['\"]|['\"]$/g, '')\n}\n\nfunction hashSecret(value: string | null | undefined): string | null {\n if (!value) return null\n return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)\n}\n\nfunction ensurePasswordPolicy(password: string): boolean {\n const policy = getPasswordPolicy()\n const result = validatePassword(password, policy)\n if (result.ok) return true\n const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback)\n const suffix = requirements ? `: ${requirements}` : ''\n console.error(`Password does not meet the requirements${suffix}.`)\n return false\n}\n\nasync function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {\n const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'\n try {\n return await fn()\n } finally {\n if (previous === undefined) {\n delete process.env.TENANT_DATA_ENCRYPTION_DEBUG\n } else {\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous\n }\n }\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nconst seedRoles: ModuleCli = {\n command: 'seed-roles',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n if (!key) continue\n const value = rest[i + 1]\n if (value) args[key] = value\n }\n const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n if (tenantId) {\n await ensureRoles(em, { tenantId })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', tenantId)\n return\n }\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to seed.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (!id) continue\n await ensureRoles(em, { tenantId: id })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', id)\n }\n },\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenantId as string) ?? (args.tenant as string) ?? (args.tenant_id as string) ?? null\n const organizationId = (args.organizationId as string) ?? (args.orgId as string) ?? (args.org as string) ?? null\n const oldKey = (args['old-key'] as string) ?? (args.oldKey as string) ?? null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantId) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n })\n console.log('[rotate-encryption-key] key fingerprints', {\n oldKey: hashSecret(oldKey),\n currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY),\n })\n }\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n const conn: any = (em as any).getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n const meta = (em as any)?.getMetadata?.()?.get?.(User)\n const tableName = meta?.tableName || 'users'\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n const printedDek = new Set<string>()\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (scopeTenantId: string, scopeOrganizationId: string): Promise<number> => {\n if (debug && !printedDek.has(scopeTenantId)) {\n printedDek.add(scopeTenantId)\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),\n encryptionService.getDek(scopeTenantId),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n tenantId: scopeTenantId,\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n }\n const rawRows = await conn.execute(\n `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,\n [scopeTenantId, scopeOrganizationId],\n )\n const rows = Array.isArray(rawRows) ? rawRows : []\n const pending = rotate\n ? rows\n : rows.filter((row: any) => !isEncryptedPayload(row?.email))\n if (!pending.length) return 0\n console.log(\n `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? ' (dry-run)' : ''}.`\n )\n if (dryRun) return 0\n const ids = pending.map((row: any) => String(row.id))\n const users = rotate\n ? await em.find(\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n )\n : await findWithDecryption(\n em,\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n {},\n { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService },\n )\n const usersById = new Map(users.map((user) => [String(user.id), user]))\n let updated = 0\n for (const row of pending) {\n const user = usersById.get(String(row.id))\n if (!user) continue\n const rawEmail = typeof row.email === 'string' ? row.email : String(row.email ?? '')\n if (!rawEmail) continue\n if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {\n continue\n }\n let plainEmail = rawEmail\n if (rotate && isEncryptedPayload(rawEmail) && oldKms) {\n if (debug) {\n console.log('[rotate-encryption-key] decrypting', {\n userId: row.id,\n tenantId: scopeTenantId,\n organizationId: scopeOrganizationId,\n })\n }\n let oldDek = oldDekCache.get(scopeTenantId) ?? null\n if (!oldDekCache.has(scopeTenantId)) {\n oldDek = await oldKms.getTenantDek(scopeTenantId)\n oldDekCache.set(scopeTenantId, oldDek)\n }\n const maybeEmail = decryptWithOldKey(rawEmail, oldDek)\n if (typeof maybeEmail !== 'string' || isEncryptedPayload(maybeEmail)) continue\n plainEmail = maybeEmail\n }\n if (!plainEmail) continue\n const encrypted = await encryptionService.encryptEntityPayload(\n 'auth:user',\n { email: plainEmail },\n scopeTenantId,\n scopeOrganizationId,\n )\n const nextEmail = encrypted.email as string | undefined\n if (nextEmail && nextEmail !== user.email) {\n user.email = nextEmail as any\n user.emailHash = (encrypted as any).emailHash ?? computeEmailHash(plainEmail)\n em.persist(user)\n updated += 1\n }\n }\n if (updated > 0) {\n await em.flush()\n }\n return updated\n }\n\n if (tenantId && organizationId) {\n const updated = await processScope(String(tenantId), String(organizationId))\n if (!updated) {\n console.log('All auth user emails already encrypted for the selected scope.')\n } else {\n console.log(`Encrypted ${updated} auth user email(s).`)\n }\n return\n }\n\n const organizations = await em.find(Organization, {})\n if (!organizations.length) {\n console.log('No organizations found; nothing to encrypt.')\n return\n }\n let total = 0\n for (const org of organizations) {\n const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null\n const scopeOrganizationId = org.id ? String(org.id) : null\n if (!scopeTenantId || !scopeOrganizationId) continue\n total += await processScope(scopeTenantId, scopeOrganizationId)\n }\n if (total > 0) {\n console.log(`Encrypted ${total} auth user email(s) across all organizations.`)\n } else {\n console.log('All auth user emails already encrypted across all organizations.')\n }\n },\n}\n\n// will be exported at the bottom with all commands\n\nconst addOrganization: ModuleCli = {\n command: 'add-org',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const name = args.name || args.orgName\n if (!name) {\n console.error('Usage: mercato auth add-org --name <organization name>')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n // Create tenant implicitly for simplicity\n const tenant = em.create(Tenant, { name: `${name} Tenant` })\n await em.persistAndFlush(tenant)\n const org = em.create(Organization, { name, tenant })\n await em.persistAndFlush(org)\n await rebuildHierarchyForTenant(em, String(tenant.id))\n console.log('Organization created with id', org.id, 'in tenant', tenant.id)\n },\n}\n\nconst setupApp: ModuleCli = {\n command: 'setup',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const orgName = args.orgName || args.name\n const email = args.email\n const password = args.password\n const rolesCsv = (args.roles ?? 'superadmin,admin,employee').trim()\n if (!orgName || !email || !password) {\n console.error('Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const roleNames = rolesCsv\n ? rolesCsv.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined\n\n try {\n const result = await setupInitialTenant(em, {\n orgName,\n roleNames,\n primaryUser: { email, password, confirm: true },\n includeDerivedUsers: true,\n })\n\n if (result.reusedExistingUser) {\n console.log('\u26A0\uFE0F Existing initial user detected during setup.')\n console.log(`\u26A0\uFE0F Email: ${email}`)\n console.log('\u26A0\uFE0F Updated roles if missing and reused tenant/organization.')\n }\n\n if(env.NODE_ENV !== 'test') { \n for (const snapshot of result.users) {\n if (snapshot.created) {\n if (snapshot.user.email === email && password) {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email, 'password:', password)\n } else {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email)\n }\n } else {\n console.log(`Updated user ${snapshot.user.email}`)\n }\n }\n }\n\n if(env.NODE_ENV !== 'test') console.log('\u2705 Setup complete:', { tenantId: result.tenantId, organizationId: result.organizationId })\n } catch (err) {\n if (err instanceof Error && err.message === 'USER_EXISTS') {\n console.error('Setup aborted: user already exists with the provided email.')\n return\n }\n throw err\n }\n },\n}\n\nconst listOrganizations: ModuleCli = {\n command: 'list-orgs',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const orgs = await findWithDecryption(\n em,\n Organization,\n {},\n { populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n \n if (orgs.length === 0) {\n console.log('No organizations found')\n return\n }\n \n console.log(`Found ${orgs.length} organization(s):`)\n console.log('')\n console.log('ID | Name | Tenant ID | Created')\n console.log('-------------------------------------|-------------------------|-------------------------------------|-------------------')\n \n for (const org of orgs) {\n const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'\n const id = org.id || 'N/A'\n const tenantId = org.tenant?.id || 'N/A'\n const name = (org.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`)\n }\n },\n}\n\nconst listTenants: ModuleCli = {\n command: 'list-tenants',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const tenants = await em.find(Tenant, {})\n \n if (tenants.length === 0) {\n console.log('No tenants found')\n return\n }\n \n console.log(`Found ${tenants.length} tenant(s):`)\n console.log('')\n console.log('ID | Name | Created')\n console.log('-------------------------------------|-------------------------|-------------------')\n \n for (const tenant of tenants) {\n const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : 'N/A'\n const id = tenant.id || 'N/A'\n const name = (tenant.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${created}`)\n }\n },\n}\n\nconst listUsers: ModuleCli = {\n command: 'list-users',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n \n // Build query with optional filters\n const where: any = {}\n if (args.organizationId || args.orgId || args.org) {\n where.organizationId = args.organizationId || args.orgId || args.org\n }\n if (args.tenantId || args.tenant) {\n where.tenantId = args.tenantId || args.tenant\n }\n \n const users = await em.find(User, where)\n \n if (users.length === 0) {\n console.log('No users found')\n return\n }\n \n console.log(`Found ${users.length} user(s):`)\n console.log('')\n console.log('ID | Email | Name | Organization ID | Tenant ID | Roles')\n console.log('-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------')\n \n for (const user of users) {\n // Get user roles separately\n const userRoles = await findWithDecryption(\n em,\n UserRole,\n { user: user.id },\n { populate: ['role'] },\n { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null },\n )\n const roles = userRoles.map((ur: any) => ur.role?.name).filter(Boolean).join(', ') || 'None'\n \n // Get organization and tenant names if IDs exist\n let orgName = 'N/A'\n let tenantName = 'N/A'\n \n if (user.organizationId) {\n const org = await em.findOne(Organization, { id: user.organizationId })\n orgName = org?.name?.substring(0, 19) + '...' || user.organizationId.substring(0, 8) + '...'\n }\n \n if (user.tenantId) {\n const tenant = await em.findOne(Tenant, { id: user.tenantId })\n tenantName = tenant?.name?.substring(0, 19) + '...' || user.tenantId.substring(0, 8) + '...'\n }\n \n const id = user.id || 'N/A'\n const email = (user.email || 'N/A').padEnd(23)\n const name = (user.name || 'Unnamed').padEnd(23)\n \n console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`)\n }\n },\n}\n\nconst setPassword: ModuleCli = {\n command: 'set-password',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const email = args.email\n const password = args.password\n \n if (!email || !password) {\n console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const emailHash = computeEmailHash(email)\n const user = await em.findOne(User, { $or: [{ email }, { emailHash }] })\n \n if (!user) {\n console.error(`User with email \"${email}\" not found`)\n return\n }\n \n user.passwordHash = await hash(password, 10)\n await em.persistAndFlush(user)\n \n console.log(`\u2705 Password updated successfully for user: ${email}`)\n },\n}\n\n// Export the full CLI list\nexport default [addUser, seedRoles, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|