@open-mercato/core 0.4.2-canary-15c0b23a3a → 0.4.2-canary-da2b080494
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.ids.generated.js +1 -5
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +0 -2
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js +2 -3
- 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 +0 -4
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +0 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +3 -4
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/reset/confirm.js +2 -25
- package/dist/modules/auth/api/reset/confirm.js.map +2 -2
- package/dist/modules/auth/api/reset.js +0 -23
- package/dist/modules/auth/api/reset.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +9 -14
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +2 -4
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +1 -4
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +3 -3
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +3 -18
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +2 -15
- 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 +11 -25
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/users.js +2 -59
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +2 -4
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +10 -20
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +0 -1
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/services/authService.js +3 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/business_rules/api/execute/route.js +1 -7
- package/dist/modules/business_rules/api/execute/route.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +0 -4
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +0 -3
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +3 -33
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +0 -2
- 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 +0 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/configs/cli.js +0 -6
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/configs/components/CachePanel.js +4 -4
- package/dist/modules/configs/components/CachePanel.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +1 -48
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +0 -18
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +0 -3
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +0 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +0 -3
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +0 -3
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +0 -3
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +0 -31
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +0 -1
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/dashboards/cli.js +5 -44
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +11 -16
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
- package/dist/modules/dashboards/services/widgetDataService.js +3 -132
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryTable.js +0 -2
- 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/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 +1 -7
- 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/backend/sales/channels/offers/page.js +0 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +0 -2
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +0 -53
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +0 -26
- 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 +0 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +0 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +1 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +0 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- 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 +0 -79
- package/dist/modules/staff/commands/leave-requests.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +0 -5
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +0 -3
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +0 -3
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +6 -14
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/generated/entities.ids.generated.ts +1 -5
- package/generated/entity-fields-registry.ts +0 -2
- package/package.json +2 -2
- package/src/modules/api_docs/frontend/docs/api/page.tsx +2 -3
- package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
- package/src/modules/attachments/components/AttachmentLibrary.tsx +0 -4
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +0 -2
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
- package/src/modules/auth/api/admin/nav.ts +6 -10
- package/src/modules/auth/api/reset/confirm.ts +2 -25
- package/src/modules/auth/api/reset.ts +0 -23
- package/src/modules/auth/api/sidebar/preferences/route.ts +12 -21
- package/src/modules/auth/api/users/route.ts +2 -5
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +1 -4
- package/src/modules/auth/backend/roles/page.tsx +3 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +3 -22
- package/src/modules/auth/backend/users/create/page.tsx +2 -19
- package/src/modules/auth/backend/users/page.tsx +3 -3
- package/src/modules/auth/cli.ts +11 -38
- package/src/modules/auth/commands/users.ts +2 -73
- package/src/modules/auth/data/validators.ts +2 -5
- package/src/modules/auth/frontend/reset/[token]/page.tsx +11 -24
- package/src/modules/auth/i18n/de.json +1 -43
- package/src/modules/auth/i18n/en.json +1 -43
- package/src/modules/auth/i18n/es.json +1 -43
- package/src/modules/auth/i18n/pl.json +1 -43
- package/src/modules/auth/lib/setup-app.ts +0 -1
- package/src/modules/auth/services/authService.ts +4 -4
- package/src/modules/business_rules/api/execute/route.ts +1 -8
- package/src/modules/business_rules/backend/rules/page.tsx +0 -4
- package/src/modules/business_rules/backend/sets/page.tsx +0 -3
- package/src/modules/business_rules/i18n/en.json +1 -3
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +0 -51
- package/src/modules/business_rules/lib/rule-engine.ts +3 -57
- package/src/modules/catalog/components/PriceKindSettings.tsx +0 -2
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +0 -2
- package/src/modules/catalog/i18n/en.json +1 -3
- package/src/modules/configs/cli.ts +0 -6
- package/src/modules/configs/components/CachePanel.tsx +4 -4
- package/src/modules/configs/i18n/en.json +2 -12
- package/src/modules/configs/i18n/pl.json +2 -12
- package/src/modules/configs/lib/system-status.ts +1 -48
- package/src/modules/configs/lib/system-status.types.ts +0 -1
- package/src/modules/configs/lib/upgrade-actions.ts +0 -18
- package/src/modules/currencies/backend/currencies/page.tsx +0 -3
- package/src/modules/currencies/backend/exchange-rates/page.tsx +0 -2
- package/src/modules/customers/backend/customers/companies/page.tsx +0 -3
- package/src/modules/customers/backend/customers/deals/page.tsx +0 -3
- package/src/modules/customers/backend/customers/people/page.tsx +0 -3
- package/src/modules/customers/commands/deals.ts +0 -39
- package/src/modules/customers/components/CustomerTodosTable.tsx +0 -1
- package/src/modules/customers/i18n/en.json +1 -5
- package/src/modules/dashboards/cli.ts +5 -55
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +11 -22
- package/src/modules/dashboards/services/widgetDataService.ts +4 -157
- package/src/modules/dictionaries/components/DictionaryTable.tsx +0 -2
- 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 +4 -3
- package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
- package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -8
- 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/backend/sales/channels/offers/page.tsx +0 -2
- package/src/modules/sales/backend/sales/channels/page.tsx +0 -2
- package/src/modules/sales/commands/documents.ts +0 -65
- package/src/modules/sales/commands/payments.ts +0 -33
- 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 +0 -2
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +0 -2
- package/src/modules/sales/components/documents/PaymentsSection.tsx +1 -2
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +0 -2
- package/src/modules/sales/i18n/de.json +0 -20
- package/src/modules/sales/i18n/en.json +1 -25
- package/src/modules/sales/i18n/es.json +0 -20
- package/src/modules/sales/i18n/pl.json +0 -20
- 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 +0 -94
- package/src/modules/staff/i18n/de.json +0 -4
- package/src/modules/staff/i18n/en.json +1 -9
- package/src/modules/staff/i18n/es.json +0 -4
- package/src/modules/staff/i18n/pl.json +0 -4
- package/src/modules/workflows/backend/definitions/page.tsx +0 -5
- package/src/modules/workflows/backend/instances/page.tsx +1 -4
- package/src/modules/workflows/backend/tasks/page.tsx +1 -4
- package/src/modules/workflows/i18n/en.json +1 -3
- package/src/modules/workflows/lib/transition-handler.ts +6 -18
- package/dist/generated/entities/notification/index.js +0 -57
- package/dist/generated/entities/notification/index.js.map +0 -7
- package/dist/modules/auth/api/profile/route.js +0 -157
- package/dist/modules/auth/api/profile/route.js.map +0 -7
- package/dist/modules/auth/backend/auth/profile/page.js +0 -141
- package/dist/modules/auth/backend/auth/profile/page.js.map +0 -7
- package/dist/modules/auth/backend/auth/profile/page.meta.js +0 -13
- package/dist/modules/auth/backend/auth/profile/page.meta.js.map +0 -7
- package/dist/modules/auth/notifications.js +0 -112
- package/dist/modules/auth/notifications.js.map +0 -7
- package/dist/modules/business_rules/notifications.js +0 -28
- package/dist/modules/business_rules/notifications.js.map +0 -7
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +0 -37
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +0 -7
- package/dist/modules/catalog/notifications.js +0 -28
- package/dist/modules/catalog/notifications.js.map +0 -7
- package/dist/modules/catalog/subscribers/low-stock-notification.js +0 -38
- package/dist/modules/catalog/subscribers/low-stock-notification.js.map +0 -7
- package/dist/modules/customers/notifications.js +0 -48
- package/dist/modules/customers/notifications.js.map +0 -7
- package/dist/modules/dashboards/lib/role-widgets.js +0 -58
- package/dist/modules/dashboards/lib/role-widgets.js.map +0 -7
- package/dist/modules/notifications/acl.js +0 -11
- package/dist/modules/notifications/acl.js.map +0 -7
- package/dist/modules/notifications/api/[id]/action/route.js +0 -74
- package/dist/modules/notifications/api/[id]/action/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/dismiss/route.js +0 -15
- package/dist/modules/notifications/api/[id]/dismiss/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/read/route.js +0 -15
- package/dist/modules/notifications/api/[id]/read/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/restore/route.js +0 -53
- package/dist/modules/notifications/api/[id]/restore/route.js.map +0 -7
- package/dist/modules/notifications/api/batch/route.js +0 -17
- package/dist/modules/notifications/api/batch/route.js.map +0 -7
- package/dist/modules/notifications/api/feature/route.js +0 -17
- package/dist/modules/notifications/api/feature/route.js.map +0 -7
- package/dist/modules/notifications/api/mark-all-read/route.js +0 -35
- package/dist/modules/notifications/api/mark-all-read/route.js.map +0 -7
- package/dist/modules/notifications/api/openapi.js +0 -76
- package/dist/modules/notifications/api/openapi.js.map +0 -7
- package/dist/modules/notifications/api/role/route.js +0 -17
- package/dist/modules/notifications/api/role/route.js.map +0 -7
- package/dist/modules/notifications/api/route.js +0 -85
- package/dist/modules/notifications/api/route.js.map +0 -7
- package/dist/modules/notifications/api/settings/route.js +0 -155
- package/dist/modules/notifications/api/settings/route.js.map +0 -7
- package/dist/modules/notifications/api/unread-count/route.js +0 -38
- package/dist/modules/notifications/api/unread-count/route.js.map +0 -7
- package/dist/modules/notifications/backend/config/notifications/page.js +0 -10
- package/dist/modules/notifications/backend/config/notifications/page.js.map +0 -7
- package/dist/modules/notifications/backend/config/notifications/page.meta.js +0 -24
- package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +0 -7
- package/dist/modules/notifications/cli.js +0 -16
- package/dist/modules/notifications/cli.js.map +0 -7
- package/dist/modules/notifications/data/entities.js +0 -112
- package/dist/modules/notifications/data/entities.js.map +0 -7
- package/dist/modules/notifications/data/validators.js +0 -98
- package/dist/modules/notifications/data/validators.js.map +0 -7
- package/dist/modules/notifications/di.js +0 -13
- package/dist/modules/notifications/di.js.map +0 -7
- package/dist/modules/notifications/emails/NotificationEmail.js +0 -58
- package/dist/modules/notifications/emails/NotificationEmail.js.map +0 -7
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +0 -44
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +0 -7
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +0 -220
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +0 -7
- package/dist/modules/notifications/index.js +0 -14
- package/dist/modules/notifications/index.js.map +0 -7
- package/dist/modules/notifications/lib/deliveryConfig.js +0 -107
- package/dist/modules/notifications/lib/deliveryConfig.js.map +0 -7
- package/dist/modules/notifications/lib/deliveryStrategies.js +0 -14
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +0 -7
- package/dist/modules/notifications/lib/events.js +0 -12
- package/dist/modules/notifications/lib/events.js.map +0 -7
- package/dist/modules/notifications/lib/notificationBuilder.js +0 -66
- package/dist/modules/notifications/lib/notificationBuilder.js.map +0 -7
- package/dist/modules/notifications/lib/notificationFactory.js +0 -54
- package/dist/modules/notifications/lib/notificationFactory.js.map +0 -7
- package/dist/modules/notifications/lib/notificationMapper.js +0 -34
- package/dist/modules/notifications/lib/notificationMapper.js.map +0 -7
- package/dist/modules/notifications/lib/notificationRecipients.js +0 -35
- package/dist/modules/notifications/lib/notificationRecipients.js.map +0 -7
- package/dist/modules/notifications/lib/notificationService.js +0 -279
- package/dist/modules/notifications/lib/notificationService.js.map +0 -7
- package/dist/modules/notifications/lib/routeHelpers.js +0 -101
- package/dist/modules/notifications/lib/routeHelpers.js.map +0 -7
- package/dist/modules/notifications/lib/safeHref.js +0 -24
- package/dist/modules/notifications/lib/safeHref.js.map +0 -7
- package/dist/modules/notifications/migrations/Migration20260123000001.js +0 -70
- package/dist/modules/notifications/migrations/Migration20260123000001.js.map +0 -7
- package/dist/modules/notifications/migrations/Migration20260126150000.js +0 -37
- package/dist/modules/notifications/migrations/Migration20260126150000.js.map +0 -7
- package/dist/modules/notifications/subscribers/deliver-notification.js +0 -165
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +0 -7
- package/dist/modules/notifications/workers/create-notification.worker.js +0 -70
- package/dist/modules/notifications/workers/create-notification.worker.js.map +0 -7
- package/dist/modules/sales/notifications.client.js +0 -51
- package/dist/modules/sales/notifications.client.js.map +0 -7
- package/dist/modules/sales/notifications.js +0 -88
- package/dist/modules/sales/notifications.js.map +0 -7
- package/dist/modules/sales/subscribers/quote-expiring-notification.js +0 -38
- package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +0 -137
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +0 -137
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/index.js +0 -7
- package/dist/modules/sales/widgets/notifications/index.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +0 -60
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +0 -7
- package/dist/modules/staff/notifications.js +0 -75
- package/dist/modules/staff/notifications.js.map +0 -7
- package/dist/modules/workflows/notifications.js +0 -28
- package/dist/modules/workflows/notifications.js.map +0 -7
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +0 -38
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +0 -7
- package/generated/entities/notification/index.ts +0 -27
- package/src/modules/auth/api/profile/route.ts +0 -163
- package/src/modules/auth/backend/auth/profile/page.meta.ts +0 -9
- package/src/modules/auth/backend/auth/profile/page.tsx +0 -174
- package/src/modules/auth/notifications.ts +0 -109
- package/src/modules/business_rules/notifications.ts +0 -25
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +0 -50
- package/src/modules/catalog/notifications.ts +0 -25
- package/src/modules/catalog/subscribers/low-stock-notification.ts +0 -52
- package/src/modules/customers/notifications.ts +0 -44
- package/src/modules/dashboards/lib/role-widgets.ts +0 -80
- package/src/modules/notifications/__tests__/deliver-notification.test.ts +0 -195
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +0 -19
- package/src/modules/notifications/__tests__/notificationService.test.ts +0 -208
- package/src/modules/notifications/acl.ts +0 -7
- package/src/modules/notifications/api/[id]/action/route.ts +0 -75
- package/src/modules/notifications/api/[id]/dismiss/route.ts +0 -12
- package/src/modules/notifications/api/[id]/read/route.ts +0 -12
- package/src/modules/notifications/api/[id]/restore/route.ts +0 -53
- package/src/modules/notifications/api/batch/route.ts +0 -14
- package/src/modules/notifications/api/feature/route.ts +0 -14
- package/src/modules/notifications/api/mark-all-read/route.ts +0 -34
- package/src/modules/notifications/api/openapi.ts +0 -76
- package/src/modules/notifications/api/role/route.ts +0 -14
- package/src/modules/notifications/api/route.ts +0 -92
- package/src/modules/notifications/api/settings/route.ts +0 -157
- package/src/modules/notifications/api/unread-count/route.ts +0 -38
- package/src/modules/notifications/backend/config/notifications/page.meta.ts +0 -22
- package/src/modules/notifications/backend/config/notifications/page.tsx +0 -12
- package/src/modules/notifications/cli.ts +0 -18
- package/src/modules/notifications/data/entities.ts +0 -99
- package/src/modules/notifications/data/validators.ts +0 -115
- package/src/modules/notifications/di.ts +0 -11
- package/src/modules/notifications/emails/NotificationEmail.tsx +0 -98
- package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +0 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +0 -233
- package/src/modules/notifications/i18n/de.json +0 -50
- package/src/modules/notifications/i18n/en.json +0 -50
- package/src/modules/notifications/i18n/es.json +0 -50
- package/src/modules/notifications/i18n/pl.json +0 -50
- package/src/modules/notifications/index.ts +0 -12
- package/src/modules/notifications/lib/deliveryConfig.ts +0 -153
- package/src/modules/notifications/lib/deliveryStrategies.ts +0 -50
- package/src/modules/notifications/lib/events.ts +0 -48
- package/src/modules/notifications/lib/notificationBuilder.ts +0 -121
- package/src/modules/notifications/lib/notificationFactory.ts +0 -76
- package/src/modules/notifications/lib/notificationMapper.ts +0 -33
- package/src/modules/notifications/lib/notificationRecipients.ts +0 -83
- package/src/modules/notifications/lib/notificationService.ts +0 -414
- package/src/modules/notifications/lib/routeHelpers.ts +0 -151
- package/src/modules/notifications/lib/safeHref.ts +0 -29
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +0 -300
- package/src/modules/notifications/migrations/Migration20260123000001.ts +0 -73
- package/src/modules/notifications/migrations/Migration20260126150000.ts +0 -39
- package/src/modules/notifications/subscribers/deliver-notification.ts +0 -204
- package/src/modules/notifications/workers/create-notification.worker.ts +0 -122
- package/src/modules/sales/notifications.client.ts +0 -65
- package/src/modules/sales/notifications.ts +0 -82
- package/src/modules/sales/subscribers/quote-expiring-notification.ts +0 -53
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +0 -156
- package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +0 -156
- package/src/modules/sales/widgets/notifications/index.ts +0 -2
- package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +0 -81
- package/src/modules/staff/notifications.ts +0 -71
- package/src/modules/workflows/notifications.ts +0 -25
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +0 -53
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/auth/api/admin/nav.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { hasAllFeatures } from '@open-mercato/shared/security/features'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { slugifySidebarId } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { applySidebarPreference, loadFirstRoleSidebarPreference, loadSidebarPreference } from '../../services/sidebarPreferencesService'\nimport { Role } from '../../data/entities'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst sidebarNavItemSchema: z.ZodType<{ href: string; title: string; defaultTitle: string; enabled: boolean; hidden?: boolean; children?: any[] }> = z.lazy(() =>\n z.object({\n href: z.string(),\n title: z.string(),\n defaultTitle: z.string(),\n enabled: z.boolean(),\n hidden: z.boolean().optional(),\n children: z.array(sidebarNavItemSchema).optional(),\n })\n)\n\nconst adminNavResponseSchema = z.object({\n groups: z.array(\n z.object({\n id: z.string(),\n name: z.string(),\n defaultName: z.string(),\n items: z.array(sidebarNavItemSchema),\n })\n ),\n})\n\nconst adminNavErrorSchema = z.object({\n error: z.string(),\n})\n\ntype SidebarItemNode = {\n href: string\n title: string\n defaultTitle: string\n enabled: boolean\n hidden?: boolean\n children?: SidebarItemNode[]\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { translate, locale } = await resolveTranslations()\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n const cache = resolve('cache') as any\n\n // Cache key is user + tenant + organization scoped\n const cacheKey = `nav:sidebar:${locale}:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}`\n // try {\n // if (cache) {\n // const cached = await cache.get(cacheKey)\n // if (cached) return NextResponse.json(cached)\n // }\n // } catch {}\n\n // Load ACL once; we'll evaluate features locally without multiple calls\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n\n // Build nav entries from discovered backend routes\n type Entry = {\n groupId: string\n groupName: string\n groupKey?: string\n title: string\n titleKey?: string\n href: string\n enabled: boolean\n order?: number\n priority?: number\n children?: Entry[]\n }\n const entries: Entry[] = []\n\n function capitalize(s: string) { return s.charAt(0).toUpperCase() + s.slice(1) }\n function deriveTitleFromPath(p: string) {\n const seg = p.split('/').filter(Boolean).pop() || ''\n return seg ? seg.split('-').map(capitalize).join(' ') : 'Home'\n }\n\n const ctx = { auth: { roles: auth.roles || [], sub: auth.sub, tenantId: auth.tenantId, orgId: auth.orgId } }\n const modules = getModules()\n for (const m of (modules as any[])) {\n const groupDefault = capitalize(m.id)\n for (const r of (m.backendRoutes || [])) {\n const href = (r.pattern ?? r.path ?? '') as string\n if (!href || href.includes('[')) continue\n if ((r as any).navHidden) continue\n const title = (r.title as string) || deriveTitleFromPath(href)\n const titleKey = (r as any).pageTitleKey ?? (r as any).titleKey\n const groupName = (r.group as string) || groupDefault\n const groupKey = (r as any).pageGroupKey ?? (r as any).groupKey\n const groupId = typeof groupKey === 'string' && groupKey ? groupKey : slugifySidebarId(groupName)\n const visible = r.visible ? await Promise.resolve(r.visible(ctx)) : true\n if (!visible) continue\n const enabled = r.enabled ? await Promise.resolve(r.enabled(ctx)) : true\n const requiredRoles = (r.requireRoles as string[]) || []\n if (requiredRoles.length) {\n const roles = auth.roles || []\n const ok = requiredRoles.some((role) => roles.includes(role))\n if (!ok) continue\n }\n const features = (r as any).requireFeatures as string[] | undefined\n if (!acl.isSuperAdmin && !hasAllFeatures(acl.features, features)) continue\n const order = (r as any).order as number | undefined\n const priority = ((r as any).priority as number | undefined) ?? order\n entries.push({ groupId, groupName, groupKey, title, titleKey, href, enabled, order, priority })\n }\n }\n\n // Parent-child relationships within the same group by href prefix\n const roots: any[] = []\n for (const e of entries) {\n let parent: any | undefined\n for (const p of entries) {\n if (p === e) continue\n if (p.groupId !== e.groupId) continue\n if (!e.href.startsWith(p.href + '/')) continue\n if (!parent || p.href.length > parent.href.length) parent = p\n }\n if (parent) {\n ;(parent as any).children = (parent as any).children || []\n ;(parent as any).children.push(e)\n } else {\n roots.push(e)\n }\n }\n\n // Add dynamic user entities into Data designer > User Entities\n const where: any = { isActive: true, showInSidebar: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n try {\n const entities = await em.find(CustomEntity as any, where as any, { orderBy: { label: 'asc' } as any })\n const items = (entities as any[]).map((e) => ({\n entityId: e.entityId,\n label: e.label,\n href: `/backend/entities/user/${encodeURIComponent(e.entityId)}/records`\n }))\n if (items.length) {\n const dd = roots.find((it: Entry) => it.groupKey === 'entities.nav.group' && it.titleKey === 'entities.nav.userEntities')\n if (dd) {\n const existing = dd.children || []\n const dynamic = items.map((it) => ({\n groupId: dd.groupId,\n groupName: dd.groupName,\n groupKey: dd.groupKey,\n title: it.label,\n href: it.href,\n enabled: true,\n order: 1000,\n priority: 1000,\n }))\n const byHref = new Map<string, Entry>()\n for (const c of existing) if (!byHref.has(c.href)) byHref.set(c.href, c)\n for (const c of dynamic) if (!byHref.has(c.href)) byHref.set(c.href, c)\n dd.children = Array.from(byHref.values())\n }\n }\n } catch (e) {\n console.error('Error loading user entities', e)\n }\n\n // Sort roots and children\n const sortItems = (arr: any[]) => {\n arr.sort((a, b) => {\n if (a.group !== b.group) return a.group.localeCompare(b.group)\n const ap = a.priority ?? a.order ?? 10000\n const bp = b.priority ?? b.order ?? 10000\n if (ap !== bp) return ap - bp\n return String(a.title).localeCompare(String(b.title))\n })\n for (const it of arr) if (it.children?.length) sortItems(it.children)\n }\n sortItems(roots)\n\n // Group into sidebar groups\n type GroupBucket = {\n id: string\n rawName: string\n key?: string\n weight: number\n entries: Entry[]\n }\n\n const groupBuckets = new Map<string, GroupBucket>()\n for (const entry of roots) {\n const weight = entry.priority ?? entry.order ?? 10_000\n if (!groupBuckets.has(entry.groupId)) {\n groupBuckets.set(entry.groupId, {\n id: entry.groupId,\n rawName: entry.groupName,\n key: entry.groupKey as string | undefined,\n weight,\n entries: [entry],\n })\n } else {\n const bucket = groupBuckets.get(entry.groupId)!\n bucket.entries.push(entry)\n if (weight < bucket.weight) bucket.weight = weight\n if (!bucket.key && entry.groupKey) bucket.key = entry.groupKey as string\n if (!bucket.rawName && entry.groupName) bucket.rawName = entry.groupName\n }\n }\n\n const toItem = (entry: Entry): SidebarItemNode => {\n const defaultTitle = entry.titleKey ? translate(entry.titleKey, entry.title) : entry.title\n return {\n href: entry.href,\n title: defaultTitle,\n defaultTitle,\n enabled: entry.enabled,\n children: entry.children?.map((child) => toItem(child)),\n }\n }\n\n const groups = Array.from(groupBuckets.values()).map((bucket) => {\n const defaultName = bucket.key ? translate(bucket.key, bucket.rawName) : bucket.rawName\n return {\n id: bucket.id,\n key: bucket.key,\n name: defaultName,\n defaultName,\n weight: bucket.weight,\n items: bucket.entries.map((entry) => toItem(entry)),\n }\n })\n const defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'customers.storage.nav.group',\n ]\n const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = defaultGroupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n const normalized =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n group.weight = normalized\n })\n\n let rolePreference = null\n if (Array.isArray(auth.roles) && auth.roles.length) {\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roleRecords = await em.find(Role, {\n name: { $in: auth.roles },\n ...roleScope,\n } as any)\n const roleIds = roleRecords.map((role: Role) => role.id)\n if (roleIds.length) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: auth.tenantId ?? null,\n locale,\n })\n }\n }\n\n const groupsWithRole = rolePreference ? applySidebarPreference(groups, rolePreference) : groups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n\n // For API key auth, use userId (the actual user) if available; otherwise skip user preferences\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n const preference = effectiveUserId\n ? await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n })\n : null\n\n const withPreference = applySidebarPreference(baseForUser, preference)\n\n const payload = {\n groups: withPreference.map((group) => ({\n id: group.id,\n name: group.name,\n defaultName: group.defaultName,\n items: (group.items as SidebarItemNode[]).map((item) => ({\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n children: item.children?.map((child) => ({\n href: child.href,\n title: child.title,\n defaultTitle: child.defaultTitle,\n enabled: child.enabled,\n hidden: child.hidden,\n })),\n })),\n })),\n }\n\n try {\n if (cache) {\n const tags = [\n `rbac:user:${auth.sub}`,\n auth.tenantId ? `rbac:tenant:${auth.tenantId}` : undefined,\n `nav:entities:${auth.tenantId || 'null'}`,\n `nav:locale:${locale}`,\n `nav:sidebar:user:${auth.sub}`,\n `nav:sidebar:scope:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}:${locale}`,\n ...(Array.isArray(auth.roles) ? auth.roles.map((role: string) => `nav:sidebar:role:${role}`) : []),\n ].filter(Boolean) as string[]\n await cache.set(cacheKey, payload, { tags })\n }\n } catch {}\n\n return NextResponse.json(payload)\n}\n\nfunction adoptSidebarDefaults(groups: ReturnType<typeof applySidebarPreference>) {\n const adoptItems = <T extends { title: string; defaultTitle?: string; children?: T[] }>(items: T[]): T[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Admin sidebar navigation',\n methods: {\n GET: {\n summary: 'Resolve sidebar entries',\n description:\n 'Returns the backend navigation tree available to the authenticated administrator after applying role and personal sidebar preferences.',\n responses: [\n { status: 200, description: 'Sidebar navigation structure', schema: adminNavResponseSchema },\n { status: 401, description: 'Unauthorized', schema: adminNavErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,wBAAwB,gCAAgC,6BAA6B;AAC9F,SAAS,YAAY;AAEd,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,KAAK;AAC3B;AAEA,MAAM,uBAA+I,EAAE;AAAA,EAAK,MAC1J,EAAE,OAAO;AAAA,IACP,MAAM,EAAE,OAAO;AAAA,IACf,OAAO,EAAE,OAAO;AAAA,IAChB,cAAc,EAAE,OAAO;AAAA,IACvB,SAAS,EAAE,QAAQ;AAAA,IACnB,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC7B,UAAU,EAAE,MAAM,oBAAoB,EAAE,SAAS;AAAA,EACnD,CAAC;AACH;AAEA,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,QAAQ,EAAE;AAAA,IACR,EAAE,OAAO;AAAA,MACP,IAAI,EAAE,OAAO;AAAA,MACb,MAAM,EAAE,OAAO;AAAA,MACf,aAAa,EAAE,OAAO;AAAA,MACtB,OAAO,EAAE,MAAM,oBAAoB;AAAA,IACrC,CAAC;AAAA,EACH;AACF,CAAC;AAED,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,OAAO,EAAE,OAAO;AAClB,CAAC;AAWD,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAE9E,QAAM,EAAE,WAAW,OAAO,IAAI,MAAM,oBAAoB;AAExD,QAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,QAAM,KAAK,QAAQ,IAAI;AACvB,QAAM,OAAO,QAAQ,aAAa;AAClC,QAAM,QAAQ,QAAQ,OAAO;AAG7B,QAAM,WAAW,eAAe,MAAM,IAAI,KAAK,GAAG,IAAI,KAAK,YAAY,MAAM,IAAI,KAAK,SAAS,MAAM;AASrG,QAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,KAAK,EAAE,UAAU,KAAK,YAAY,MAAM,gBAAgB,KAAK,SAAS,KAAK,CAAC;AAehH,QAAM,UAAmB,CAAC;AAE1B,WAAS,WAAW,GAAW;AAAE,WAAO,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAAA,EAAE;AAC/E,WAAS,oBAAoB,GAAW;AACtC,UAAM,MAAM,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AAClD,WAAO,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI,UAAU,EAAE,KAAK,GAAG,IAAI;AAAA,EAC1D;AAEA,QAAM,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,SAAS,CAAC,GAAG,KAAK,KAAK,KAAK,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM,EAAE;AAC3G,QAAM,UAAU,WAAW;AAC3B,aAAW,KAAM,SAAmB;AAClC,UAAM,eAAe,WAAW,EAAE,EAAE;AACpC,eAAW,KAAM,EAAE,iBAAiB,CAAC,GAAI;AACvC,YAAM,OAAQ,EAAE,WAAW,EAAE,QAAQ;AACrC,UAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,EAAG;AACjC,UAAK,EAAU,UAAW;AAC1B,YAAM,QAAS,EAAE,SAAoB,oBAAoB,IAAI;AAC7D,YAAM,WAAY,EAAU,gBAAiB,EAAU;AACvD,YAAM,YAAa,EAAE,SAAoB;AACzC,YAAM,WAAY,EAAU,gBAAiB,EAAU;AACvD,YAAM,UAAU,OAAO,aAAa,YAAY,WAAW,WAAW,iBAAiB,SAAS;AAChG,YAAM,UAAU,EAAE,UAAU,MAAM,QAAQ,QAAQ,EAAE,QAAQ,GAAG,CAAC,IAAI;AACpE,UAAI,CAAC,QAAS;AACd,YAAM,UAAU,EAAE,UAAU,MAAM,QAAQ,QAAQ,EAAE,QAAQ,GAAG,CAAC,IAAI;AACpE,YAAM,gBAAiB,EAAE,gBAA6B,CAAC;AACvD,UAAI,cAAc,QAAQ;AACxB,cAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,cAAM,KAAK,cAAc,KAAK,CAAC,SAAS,MAAM,SAAS,IAAI,CAAC;AAC5D,YAAI,CAAC,GAAI;AAAA,MACX;AACA,YAAM,WAAY,EAAU;AAC5B,UAAI,CAAC,IAAI,gBAAgB,CAAC,eAAe,IAAI,UAAU,QAAQ,EAAG;AAClE,YAAM,QAAS,EAAU;AACzB,YAAM,WAAa,EAAU,YAAmC;AAChE,cAAQ,KAAK,EAAE,SAAS,WAAW,UAAU,OAAO,UAAU,MAAM,SAAS,OAAO,SAAS,CAAC;AAAA,IAChG;AAAA,EACF;AAGA,QAAM,QAAe,CAAC;AACtB,aAAW,KAAK,SAAS;AACvB,QAAI;AACJ,eAAW,KAAK,SAAS;AACvB,UAAI,MAAM,EAAG;AACb,UAAI,EAAE,YAAY,EAAE,QAAS;AAC7B,UAAI,CAAC,EAAE,KAAK,WAAW,EAAE,OAAO,GAAG,EAAG;AACtC,UAAI,CAAC,UAAU,EAAE,KAAK,SAAS,OAAO,KAAK,OAAQ,UAAS;AAAA,IAC9D;AACA,QAAI,QAAQ;AACV;AAAC,MAAC,OAAe,WAAY,OAAe,YAAY,CAAC;AACxD,MAAC,OAAe,SAAS,KAAK,CAAC;AAAA,IAClC,OAAO;AACL,YAAM,KAAK,CAAC;AAAA,IACd;AAAA,EACF;AAGA,QAAM,QAAa,EAAE,UAAU,MAAM,eAAe,KAAK;AACzD,QAAM,OAAO;AAAA,IACX,EAAE,KAAK,CAAE,EAAE,gBAAgB,KAAK,SAAS,OAAiB,GAAG,EAAE,gBAAgB,KAAK,CAAE,EAAE;AAAA,IACxF,EAAE,KAAK,CAAE,EAAE,UAAU,KAAK,YAAY,OAAiB,GAAG,EAAE,UAAU,KAAK,CAAE,EAAE;AAAA,EACjF;AACA,MAAI;AACF,UAAM,WAAW,MAAM,GAAG,KAAK,cAAqB,OAAc,EAAE,SAAS,EAAE,OAAO,MAAM,EAAS,CAAC;AACtG,UAAM,QAAS,SAAmB,IAAI,CAAC,OAAO;AAAA,MAC5C,UAAU,EAAE;AAAA,MACZ,OAAO,EAAE;AAAA,MACT,MAAM,0BAA0B,mBAAmB,EAAE,QAAQ,CAAC;AAAA,IAChE,EAAE;AACF,QAAI,MAAM,QAAQ;AAChB,YAAM,KAAK,MAAM,KAAK,CAAC,OAAc,GAAG,aAAa,wBAAwB,GAAG,aAAa,2BAA2B;AACxH,UAAI,IAAI;AACN,cAAM,WAAW,GAAG,YAAY,CAAC;AACjC,cAAM,UAAU,MAAM,IAAI,CAAC,QAAQ;AAAA,UACjC,SAAS,GAAG;AAAA,UACZ,WAAW,GAAG;AAAA,UACd,UAAU,GAAG;AAAA,UACb,OAAO,GAAG;AAAA,UACV,MAAM,GAAG;AAAA,UACT,SAAS;AAAA,UACT,OAAO;AAAA,UACP,UAAU;AAAA,QACZ,EAAE;AACF,cAAM,SAAS,oBAAI,IAAmB;AACtC,mBAAW,KAAK,SAAU,KAAI,CAAC,OAAO,IAAI,EAAE,IAAI,EAAG,QAAO,IAAI,EAAE,MAAM,CAAC;AACvE,mBAAW,KAAK,QAAS,KAAI,CAAC,OAAO,IAAI,EAAE,IAAI,EAAG,QAAO,IAAI,EAAE,MAAM,CAAC;AACtE,WAAG,WAAW,MAAM,KAAK,OAAO,OAAO,CAAC;AAAA,MAC1C;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AACV,YAAQ,MAAM,+BAA+B,CAAC;AAAA,EAChD;AAGA,QAAM,YAAY,CAAC,QAAe;AAChC,QAAI,KAAK,CAAC,GAAG,MAAM;AACjB,UAAI,EAAE,UAAU,EAAE,MAAO,QAAO,EAAE,MAAM,cAAc,EAAE,KAAK;AAC7D,YAAM,KAAK,EAAE,YAAY,EAAE,SAAS;AACpC,YAAM,KAAK,EAAE,YAAY,EAAE,SAAS;AACpC,UAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,aAAO,OAAO,EAAE,KAAK,EAAE,cAAc,OAAO,EAAE,KAAK,CAAC;AAAA,IACtD,CAAC;AACD,eAAW,MAAM,IAAK,KAAI,GAAG,UAAU,OAAQ,WAAU,GAAG,QAAQ;AAAA,EACtE;AACA,YAAU,KAAK;AAWf,QAAM,eAAe,oBAAI,IAAyB;AAClD,aAAW,SAAS,OAAO;AACzB,UAAM,SAAS,MAAM,YAAY,MAAM,SAAS;AAChD,QAAI,CAAC,aAAa,IAAI,MAAM,OAAO,GAAG;AACpC,mBAAa,IAAI,MAAM,SAAS;AAAA,QAC9B,IAAI,MAAM;AAAA,QACV,SAAS,MAAM;AAAA,QACf,KAAK,MAAM;AAAA,QACX;AAAA,QACA,SAAS,CAAC,KAAK;AAAA,MACjB,CAAC;AAAA,IACH,OAAO;AACL,YAAM,SAAS,aAAa,IAAI,MAAM,OAAO;AAC7C,aAAO,QAAQ,KAAK,KAAK;AACzB,UAAI,SAAS,OAAO,OAAQ,QAAO,SAAS;AAC5C,UAAI,CAAC,OAAO,OAAO,MAAM,SAAU,QAAO,MAAM,MAAM;AACtD,UAAI,CAAC,OAAO,WAAW,MAAM,UAAW,QAAO,UAAU,MAAM;AAAA,IACjE;AAAA,EACF;AAEA,QAAM,SAAS,CAAC,UAAkC;AAChD,UAAM,eAAe,MAAM,WAAW,UAAU,MAAM,UAAU,MAAM,KAAK,IAAI,MAAM;AACrF,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,OAAO;AAAA,MACP;AAAA,MACA,SAAS,MAAM;AAAA,MACf,UAAU,MAAM,UAAU,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC;AAAA,IACxD;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,KAAK,aAAa,OAAO,CAAC,EAAE,IAAI,CAAC,WAAW;AAC/D,UAAM,cAAc,OAAO,MAAM,UAAU,OAAO,KAAK,OAAO,OAAO,IAAI,OAAO;AAChF,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,MACX,KAAK,OAAO;AAAA,MACZ,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO,QAAQ,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC;AAAA,IACpD;AAAA,EACF,CAAC;AACD,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,kBAAkB,IAAI,IAAI,kBAAkB,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,CAAC;AACjF,SAAO,KAAK,CAAC,GAAG,MAAM;AACpB,UAAM,SAAS,gBAAgB,IAAI,EAAE,EAAE;AACvC,UAAM,SAAS,gBAAgB,IAAI,EAAE,EAAE;AACvC,QAAI,WAAW,UAAa,WAAW,QAAW;AAChD,UAAI,WAAW,OAAW,QAAO;AACjC,UAAI,WAAW,OAAW,QAAO;AACjC,UAAI,WAAW,OAAQ,QAAO,SAAS;AAAA,IACzC;AACA,QAAI,EAAE,WAAW,EAAE,OAAQ,QAAO,EAAE,SAAS,EAAE;AAC/C,WAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,EACpC,CAAC;AACD,QAAM,oBAAoB,kBAAkB;AAC5C,SAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,UAAM,OAAO,gBAAgB,IAAI,MAAM,EAAE;AACzC,UAAM,iBAAiB,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACzE,UAAM,cACH,SAAS,SAAY,OAAO,oBAAoB,SAAS,MAC1D,KAAK,IAAI,KAAK,IAAI,gBAAgB,CAAC,GAAG,MAAO;AAC/C,UAAM,SAAS;AAAA,EACjB,CAAC;AAED,MAAI,iBAAiB;AACrB,MAAI,MAAM,QAAQ,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ;AAClD,UAAM,YAAY,KAAK,WACnB,EAAE,KAAK,CAAC,EAAE,UAAU,KAAK,SAAS,GAAG,EAAE,UAAU,KAAK,CAAC,EAAE,IACzD,EAAE,UAAU,KAAK;AACrB,UAAM,cAAc,MAAM,GAAG,KAAK,MAAM;AAAA,MACtC,MAAM,EAAE,KAAK,KAAK,MAAM;AAAA,MACxB,GAAG;AAAA,IACL,CAAQ;AACR,UAAM,UAAU,YAAY,IAAI,CAAC,SAAe,KAAK,EAAE;AACvD,QAAI,QAAQ,QAAQ;AAClB,uBAAiB,MAAM,+BAA+B,IAAI;AAAA,QACxD;AAAA,QACA,UAAU,KAAK,YAAY;AAAA,QAC3B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,iBAAiB,iBAAiB,uBAAuB,QAAQ,cAAc,IAAI;AACzF,QAAM,cAAc,qBAAqB,cAAc;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { hasAllFeatures } from '@open-mercato/shared/security/features'\nimport { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'\nimport { slugifySidebarId } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { applySidebarPreference, loadFirstRoleSidebarPreference, loadSidebarPreference } from '../../services/sidebarPreferencesService'\nimport { Role } from '../../data/entities'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst sidebarNavItemSchema: z.ZodType<{ href: string; title: string; defaultTitle: string; enabled: boolean; hidden?: boolean; children?: any[] }> = z.lazy(() =>\n z.object({\n href: z.string(),\n title: z.string(),\n defaultTitle: z.string(),\n enabled: z.boolean(),\n hidden: z.boolean().optional(),\n children: z.array(sidebarNavItemSchema).optional(),\n })\n)\n\nconst adminNavResponseSchema = z.object({\n groups: z.array(\n z.object({\n id: z.string(),\n name: z.string(),\n defaultName: z.string(),\n items: z.array(sidebarNavItemSchema),\n })\n ),\n})\n\nconst adminNavErrorSchema = z.object({\n error: z.string(),\n})\n\ntype SidebarItemNode = {\n href: string\n title: string\n defaultTitle: string\n enabled: boolean\n hidden?: boolean\n children?: SidebarItemNode[]\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { translate, locale } = await resolveTranslations()\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n const cache = resolve('cache') as any\n\n // Cache key is user + tenant + organization scoped\n const cacheKey = `nav:sidebar:${locale}:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}`\n // try {\n // if (cache) {\n // const cached = await cache.get(cacheKey)\n // if (cached) return NextResponse.json(cached)\n // }\n // } catch {}\n\n // Load ACL once; we'll evaluate features locally without multiple calls\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n\n // Build nav entries from discovered backend routes\n type Entry = {\n groupId: string\n groupName: string\n groupKey?: string\n title: string\n titleKey?: string\n href: string\n enabled: boolean\n order?: number\n priority?: number\n children?: Entry[]\n }\n const entries: Entry[] = []\n\n function capitalize(s: string) { return s.charAt(0).toUpperCase() + s.slice(1) }\n function deriveTitleFromPath(p: string) {\n const seg = p.split('/').filter(Boolean).pop() || ''\n return seg ? seg.split('-').map(capitalize).join(' ') : 'Home'\n }\n\n const ctx = { auth: { roles: auth.roles || [], sub: auth.sub, tenantId: auth.tenantId, orgId: auth.orgId } }\n const modules = getModules()\n for (const m of (modules as any[])) {\n const groupDefault = capitalize(m.id)\n for (const r of (m.backendRoutes || [])) {\n const href = (r.pattern ?? r.path ?? '') as string\n if (!href || href.includes('[')) continue\n if ((r as any).navHidden) continue\n const title = (r.title as string) || deriveTitleFromPath(href)\n const titleKey = (r as any).pageTitleKey ?? (r as any).titleKey\n const groupName = (r.group as string) || groupDefault\n const groupKey = (r as any).pageGroupKey ?? (r as any).groupKey\n const groupId = typeof groupKey === 'string' && groupKey ? groupKey : slugifySidebarId(groupName)\n const visible = r.visible ? await Promise.resolve(r.visible(ctx)) : true\n if (!visible) continue\n const enabled = r.enabled ? await Promise.resolve(r.enabled(ctx)) : true\n const requiredRoles = (r.requireRoles as string[]) || []\n if (requiredRoles.length) {\n const roles = auth.roles || []\n const ok = requiredRoles.some((role) => roles.includes(role))\n if (!ok) continue\n }\n const features = (r as any).requireFeatures as string[] | undefined\n if (!acl.isSuperAdmin && !hasAllFeatures(acl.features, features)) continue\n const order = (r as any).order as number | undefined\n const priority = ((r as any).priority as number | undefined) ?? order\n entries.push({ groupId, groupName, groupKey, title, titleKey, href, enabled, order, priority })\n }\n }\n\n // Parent-child relationships within the same group by href prefix\n const roots: any[] = []\n for (const e of entries) {\n let parent: any | undefined\n for (const p of entries) {\n if (p === e) continue\n if (p.groupId !== e.groupId) continue\n if (!e.href.startsWith(p.href + '/')) continue\n if (!parent || p.href.length > parent.href.length) parent = p\n }\n if (parent) {\n ;(parent as any).children = (parent as any).children || []\n ;(parent as any).children.push(e)\n } else {\n roots.push(e)\n }\n }\n\n // Add dynamic user entities into Data designer > User Entities\n const where: any = { isActive: true, showInSidebar: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n try {\n const entities = await em.find(CustomEntity as any, where as any, { orderBy: { label: 'asc' } as any })\n const items = (entities as any[]).map((e) => ({\n entityId: e.entityId,\n label: e.label,\n href: `/backend/entities/user/${encodeURIComponent(e.entityId)}/records`\n }))\n if (items.length) {\n const dd = roots.find((it: Entry) => it.groupKey === 'entities.nav.group' && it.titleKey === 'entities.nav.userEntities')\n if (dd) {\n const existing = dd.children || []\n const dynamic = items.map((it) => ({\n groupId: dd.groupId,\n groupName: dd.groupName,\n groupKey: dd.groupKey,\n title: it.label,\n href: it.href,\n enabled: true,\n order: 1000,\n priority: 1000,\n }))\n const byHref = new Map<string, Entry>()\n for (const c of existing) if (!byHref.has(c.href)) byHref.set(c.href, c)\n for (const c of dynamic) if (!byHref.has(c.href)) byHref.set(c.href, c)\n dd.children = Array.from(byHref.values())\n }\n }\n } catch (e) {\n console.error('Error loading user entities', e)\n }\n\n // Sort roots and children\n const sortItems = (arr: any[]) => {\n arr.sort((a, b) => {\n if (a.group !== b.group) return a.group.localeCompare(b.group)\n const ap = a.priority ?? a.order ?? 10000\n const bp = b.priority ?? b.order ?? 10000\n if (ap !== bp) return ap - bp\n return String(a.title).localeCompare(String(b.title))\n })\n for (const it of arr) if (it.children?.length) sortItems(it.children)\n }\n sortItems(roots)\n\n // Group into sidebar groups\n type GroupBucket = {\n id: string\n rawName: string\n key?: string\n weight: number\n entries: Entry[]\n }\n\n const groupBuckets = new Map<string, GroupBucket>()\n for (const entry of roots) {\n const weight = entry.priority ?? entry.order ?? 10_000\n if (!groupBuckets.has(entry.groupId)) {\n groupBuckets.set(entry.groupId, {\n id: entry.groupId,\n rawName: entry.groupName,\n key: entry.groupKey as string | undefined,\n weight,\n entries: [entry],\n })\n } else {\n const bucket = groupBuckets.get(entry.groupId)!\n bucket.entries.push(entry)\n if (weight < bucket.weight) bucket.weight = weight\n if (!bucket.key && entry.groupKey) bucket.key = entry.groupKey as string\n if (!bucket.rawName && entry.groupName) bucket.rawName = entry.groupName\n }\n }\n\n const toItem = (entry: Entry): SidebarItemNode => {\n const defaultTitle = entry.titleKey ? translate(entry.titleKey, entry.title) : entry.title\n return {\n href: entry.href,\n title: defaultTitle,\n defaultTitle,\n enabled: entry.enabled,\n children: entry.children?.map((child) => toItem(child)),\n }\n }\n\n const groups = Array.from(groupBuckets.values()).map((bucket) => {\n const defaultName = bucket.key ? translate(bucket.key, bucket.rawName) : bucket.rawName\n return {\n id: bucket.id,\n key: bucket.key,\n name: defaultName,\n defaultName,\n weight: bucket.weight,\n items: bucket.entries.map((entry) => toItem(entry)),\n }\n })\n const defaultGroupOrder = [\n 'customers.nav.group',\n 'catalog.nav.group',\n 'customers~sales.nav.group',\n 'resources.nav.group',\n 'staff.nav.group',\n 'entities.nav.group',\n 'directory.nav.group',\n 'customers.storage.nav.group',\n ]\n const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]))\n groups.sort((a, b) => {\n const aIndex = groupOrderIndex.get(a.id)\n const bIndex = groupOrderIndex.get(b.id)\n if (aIndex !== undefined || bIndex !== undefined) {\n if (aIndex === undefined) return 1\n if (bIndex === undefined) return -1\n if (aIndex !== bIndex) return aIndex - bIndex\n }\n if (a.weight !== b.weight) return a.weight - b.weight\n return a.name.localeCompare(b.name)\n })\n const defaultGroupCount = defaultGroupOrder.length\n groups.forEach((group, index) => {\n const rank = groupOrderIndex.get(group.id)\n const fallbackWeight = typeof group.weight === 'number' ? group.weight : 10_000\n const normalized =\n (rank !== undefined ? rank : defaultGroupCount + index) * 1_000_000 +\n Math.min(Math.max(fallbackWeight, 0), 999_999)\n group.weight = normalized\n })\n\n let rolePreference = null\n if (Array.isArray(auth.roles) && auth.roles.length) {\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roleRecords = await em.find(Role, {\n name: { $in: auth.roles },\n ...roleScope,\n } as any)\n const roleIds = roleRecords.map((role: Role) => role.id)\n if (roleIds.length) {\n rolePreference = await loadFirstRoleSidebarPreference(em, {\n roleIds,\n tenantId: auth.tenantId ?? null,\n locale,\n })\n }\n }\n\n const groupsWithRole = rolePreference ? applySidebarPreference(groups, rolePreference) : groups\n const baseForUser = adoptSidebarDefaults(groupsWithRole)\n\n const preference = await loadSidebarPreference(em, {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n })\n\n const withPreference = applySidebarPreference(baseForUser, preference)\n\n const payload = {\n groups: withPreference.map((group) => ({\n id: group.id,\n name: group.name,\n defaultName: group.defaultName,\n items: (group.items as SidebarItemNode[]).map((item) => ({\n href: item.href,\n title: item.title,\n defaultTitle: item.defaultTitle,\n enabled: item.enabled,\n hidden: item.hidden,\n children: item.children?.map((child) => ({\n href: child.href,\n title: child.title,\n defaultTitle: child.defaultTitle,\n enabled: child.enabled,\n hidden: child.hidden,\n })),\n })),\n })),\n }\n\n try {\n if (cache) {\n const tags = [\n `rbac:user:${auth.sub}`,\n auth.tenantId ? `rbac:tenant:${auth.tenantId}` : undefined,\n `nav:entities:${auth.tenantId || 'null'}`,\n `nav:locale:${locale}`,\n `nav:sidebar:user:${auth.sub}`,\n `nav:sidebar:scope:${auth.sub}:${auth.tenantId || 'null'}:${auth.orgId || 'null'}:${locale}`,\n ...(Array.isArray(auth.roles) ? auth.roles.map((role: string) => `nav:sidebar:role:${role}`) : []),\n ].filter(Boolean) as string[]\n await cache.set(cacheKey, payload, { tags })\n }\n } catch {}\n\n return NextResponse.json(payload)\n}\n\nfunction adoptSidebarDefaults(groups: ReturnType<typeof applySidebarPreference>) {\n const adoptItems = <T extends { title: string; defaultTitle?: string; children?: T[] }>(items: T[]): T[] =>\n items.map((item) => ({\n ...item,\n defaultTitle: item.title,\n children: item.children ? adoptItems(item.children) : undefined,\n }))\n\n return groups.map((group) => ({\n ...group,\n defaultName: group.name,\n items: adoptItems(group.items),\n }))\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Admin sidebar navigation',\n methods: {\n GET: {\n summary: 'Resolve sidebar entries',\n description:\n 'Returns the backend navigation tree available to the authenticated administrator after applying role and personal sidebar preferences.',\n responses: [\n { status: 200, description: 'Sidebar navigation structure', schema: adminNavResponseSchema },\n { status: 401, description: 'Unauthorized', schema: adminNavErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { confirmPasswordResetSchema } from "@open-mercato/core/modules/auth/data/validators";
|
|
2
2
|
import { NextResponse } from "next/server";
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
-
import { buildNotificationFromType } from "@open-mercato/core/modules/notifications/lib/notificationBuilder";
|
|
5
|
-
import { resolveNotificationService } from "@open-mercato/core/modules/notifications/lib/notificationService";
|
|
6
|
-
import notificationTypes from "@open-mercato/core/modules/auth/notifications";
|
|
7
4
|
import { z } from "zod";
|
|
8
5
|
async function POST(req) {
|
|
9
6
|
const form = await req.formData();
|
|
@@ -13,28 +10,8 @@ async function POST(req) {
|
|
|
13
10
|
if (!parsed.success) return NextResponse.json({ ok: false, error: "Invalid request" }, { status: 400 });
|
|
14
11
|
const c = await createRequestContainer();
|
|
15
12
|
const auth = c.resolve("authService");
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
18
|
-
try {
|
|
19
|
-
const tenantId = user.tenantId ? String(user.tenantId) : null;
|
|
20
|
-
if (tenantId) {
|
|
21
|
-
const notificationService = resolveNotificationService(c);
|
|
22
|
-
const typeDef = notificationTypes.find((type) => type.type === "auth.password_reset.completed");
|
|
23
|
-
if (typeDef) {
|
|
24
|
-
const notificationInput = buildNotificationFromType(typeDef, {
|
|
25
|
-
recipientUserId: String(user.id),
|
|
26
|
-
sourceEntityType: "auth:user",
|
|
27
|
-
sourceEntityId: String(user.id)
|
|
28
|
-
});
|
|
29
|
-
await notificationService.create(notificationInput, {
|
|
30
|
-
tenantId,
|
|
31
|
-
organizationId: user.organizationId ? String(user.organizationId) : null
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
} catch (err) {
|
|
36
|
-
console.error("[auth.reset.confirm] Failed to create notification:", err);
|
|
37
|
-
}
|
|
13
|
+
const ok = await auth.confirmPasswordReset(parsed.data.token, parsed.data.password);
|
|
14
|
+
if (!ok) return NextResponse.json({ ok: false, error: "Invalid or expired token" }, { status: 400 });
|
|
38
15
|
return NextResponse.json({ ok: true, redirect: "/login" });
|
|
39
16
|
}
|
|
40
17
|
const metadata = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/auth/api/reset/confirm.ts"],
|
|
4
|
-
"sourcesContent": ["import { confirmPasswordResetSchema } from '@open-mercato/core/modules/auth/data/validators'\nimport { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport {
|
|
5
|
-
"mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AAEvC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { confirmPasswordResetSchema } from '@open-mercato/core/modules/auth/data/validators'\nimport { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { z } from 'zod'\n\n// validation via confirmPasswordResetSchema\n\nexport async function POST(req: Request) {\n const form = await req.formData()\n const token = String(form.get('token') ?? '')\n const password = String(form.get('password') ?? '')\n const parsed = confirmPasswordResetSchema.safeParse({ token, password })\n if (!parsed.success) return NextResponse.json({ ok: false, error: 'Invalid request' }, { status: 400 })\n const c = await createRequestContainer()\n const auth = c.resolve<AuthService>('authService')\n const ok = await auth.confirmPasswordReset(parsed.data.token, parsed.data.password)\n if (!ok) return NextResponse.json({ ok: false, error: 'Invalid or expired token' }, { status: 400 })\n return NextResponse.json({ ok: true, redirect: '/login' })\n}\n\nexport const metadata = {\n POST: {},\n}\n\nconst passwordResetConfirmResponseSchema = z.object({\n ok: z.literal(true),\n redirect: z.string(),\n})\n\nconst passwordResetErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Confirm password reset',\n methods: {\n POST: {\n summary: 'Complete password reset',\n description: 'Validates the reset token and updates the user password.',\n requestBody: {\n contentType: 'application/x-www-form-urlencoded',\n schema: confirmPasswordResetSchema,\n },\n responses: [\n { status: 200, description: 'Password reset succeeded', schema: passwordResetConfirmResponseSchema },\n { status: 400, description: 'Invalid token or payload', schema: passwordResetErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AAEvC,SAAS,SAAS;AAIlB,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,IAAI,SAAS;AAChC,QAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,KAAK,EAAE;AAC5C,QAAM,WAAW,OAAO,KAAK,IAAI,UAAU,KAAK,EAAE;AAClD,QAAM,SAAS,2BAA2B,UAAU,EAAE,OAAO,SAAS,CAAC;AACvE,MAAI,CAAC,OAAO,QAAS,QAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kBAAkB,GAAG,EAAE,QAAQ,IAAI,CAAC;AACtG,QAAM,IAAI,MAAM,uBAAuB;AACvC,QAAM,OAAO,EAAE,QAAqB,aAAa;AACjD,QAAM,KAAK,MAAM,KAAK,qBAAqB,OAAO,KAAK,OAAO,OAAO,KAAK,QAAQ;AAClF,MAAI,CAAC,GAAI,QAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AACnG,SAAO,aAAa,KAAK,EAAE,IAAI,MAAM,UAAU,SAAS,CAAC;AAC3D;AAEO,MAAM,WAAW;AAAA,EACtB,MAAM,CAAC;AACT;AAEA,MAAM,qCAAqC,EAAE,OAAO;AAAA,EAClD,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,UAAU,EAAE,OAAO;AACrB,CAAC;AAED,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,IAAI,EAAE,QAAQ,KAAK;AAAA,EACnB,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,mCAAmC;AAAA,QACnG,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,yBAAyB;AAAA,MAC3F;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,9 +4,6 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { sendEmail } from "@open-mercato/shared/lib/email/send";
|
|
5
5
|
import ResetPasswordEmail from "@open-mercato/core/modules/auth/emails/ResetPasswordEmail";
|
|
6
6
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
|
-
import { buildNotificationFromType } from "@open-mercato/core/modules/notifications/lib/notificationBuilder";
|
|
8
|
-
import { resolveNotificationService } from "@open-mercato/core/modules/notifications/lib/notificationService";
|
|
9
|
-
import notificationTypes from "@open-mercato/core/modules/auth/notifications";
|
|
10
7
|
import { z } from "zod";
|
|
11
8
|
async function POST(req) {
|
|
12
9
|
const form = await req.formData();
|
|
@@ -31,26 +28,6 @@ async function POST(req) {
|
|
|
31
28
|
hint: translate("auth.email.resetPassword.hint", "If you didn't request this, you can safely ignore this email.")
|
|
32
29
|
};
|
|
33
30
|
await sendEmail({ to: user.email, subject, react: ResetPasswordEmail({ resetUrl, copy }) });
|
|
34
|
-
try {
|
|
35
|
-
const tenantId = user.tenantId ? String(user.tenantId) : null;
|
|
36
|
-
if (tenantId) {
|
|
37
|
-
const notificationService = resolveNotificationService(c);
|
|
38
|
-
const typeDef = notificationTypes.find((type) => type.type === "auth.password_reset.requested");
|
|
39
|
-
if (typeDef) {
|
|
40
|
-
const notificationInput = buildNotificationFromType(typeDef, {
|
|
41
|
-
recipientUserId: String(user.id),
|
|
42
|
-
sourceEntityType: "auth:user",
|
|
43
|
-
sourceEntityId: String(user.id)
|
|
44
|
-
});
|
|
45
|
-
await notificationService.create(notificationInput, {
|
|
46
|
-
tenantId,
|
|
47
|
-
organizationId: user.organizationId ? String(user.organizationId) : null
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} catch (err) {
|
|
52
|
-
console.error("[auth.reset] Failed to create notification:", err);
|
|
53
|
-
}
|
|
54
31
|
return NextResponse.json({ ok: true });
|
|
55
32
|
}
|
|
56
33
|
const metadata = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/auth/api/reset.ts"],
|
|
4
|
-
"sourcesContent": ["import { requestPasswordResetSchema } from '@open-mercato/core/modules/auth/data/validators'\nimport { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { sendEmail } from '@open-mercato/shared/lib/email/send'\nimport ResetPasswordEmail from '@open-mercato/core/modules/auth/emails/ResetPasswordEmail'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {
|
|
5
|
-
"mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AAEvC,SAAS,iBAAiB;AAC1B,OAAO,wBAAwB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,
|
|
4
|
+
"sourcesContent": ["import { requestPasswordResetSchema } from '@open-mercato/core/modules/auth/data/validators'\nimport { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { sendEmail } from '@open-mercato/shared/lib/email/send'\nimport ResetPasswordEmail from '@open-mercato/core/modules/auth/emails/ResetPasswordEmail'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { z } from 'zod'\n\n// validation via requestPasswordResetSchema\n\nexport async function POST(req: Request) {\n const form = await req.formData()\n const email = String(form.get('email') ?? '')\n const parsed = requestPasswordResetSchema.safeParse({ email })\n if (!parsed.success) return NextResponse.json({ ok: true }) // do not reveal\n const c = await createRequestContainer()\n const auth = c.resolve<AuthService>('authService')\n const resReq = await auth.requestPasswordReset(parsed.data.email)\n if (!resReq) return NextResponse.json({ ok: true })\n const { user, token } = resReq\n const url = new URL(req.url)\n const base = process.env.APP_URL || `${url.protocol}//${url.host}`\n const resetUrl = `${base}/reset/${token}`\n\n const { translate } = await resolveTranslations()\n const subject = translate('auth.email.resetPassword.subject', 'Reset your password')\n const copy = {\n preview: translate('auth.email.resetPassword.preview', 'Reset your password'),\n title: translate('auth.email.resetPassword.title', 'Reset your password'),\n body: translate('auth.email.resetPassword.body', 'Click the link below to set a new password. This link will expire in 60 minutes.'),\n cta: translate('auth.email.resetPassword.cta', 'Set a new password'),\n hint: translate('auth.email.resetPassword.hint', \"If you didn't request this, you can safely ignore this email.\"),\n }\n\n await sendEmail({ to: user.email, subject, react: ResetPasswordEmail({ resetUrl, copy }) })\n return NextResponse.json({ ok: true })\n}\n\nexport const metadata = {\n POST: {},\n}\n\nconst passwordResetRequestSchema = z.object({\n email: z.string().email(),\n})\n\nconst passwordResetResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Request password reset',\n methods: {\n POST: {\n summary: 'Send reset email',\n description: 'Requests a password reset email for the given account. The endpoint always returns `ok: true` to avoid leaking account existence.',\n requestBody: {\n contentType: 'application/x-www-form-urlencoded',\n schema: passwordResetRequestSchema,\n },\n responses: [\n { status: 200, description: 'Reset email dispatched (or ignored for unknown accounts)', schema: passwordResetResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AAEvC,SAAS,iBAAiB;AAC1B,OAAO,wBAAwB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,SAAS;AAIlB,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,IAAI,SAAS;AAChC,QAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,KAAK,EAAE;AAC5C,QAAM,SAAS,2BAA2B,UAAU,EAAE,MAAM,CAAC;AAC7D,MAAI,CAAC,OAAO,QAAS,QAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AAC1D,QAAM,IAAI,MAAM,uBAAuB;AACvC,QAAM,OAAO,EAAE,QAAqB,aAAa;AACjD,QAAM,SAAS,MAAM,KAAK,qBAAqB,OAAO,KAAK,KAAK;AAChE,MAAI,CAAC,OAAQ,QAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AAClD,QAAM,EAAE,MAAM,MAAM,IAAI;AACxB,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,OAAO,QAAQ,IAAI,WAAW,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI;AAChE,QAAM,WAAW,GAAG,IAAI,UAAU,KAAK;AAEvC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,UAAU,UAAU,oCAAoC,qBAAqB;AACnF,QAAM,OAAO;AAAA,IACX,SAAS,UAAU,oCAAoC,qBAAqB;AAAA,IAC5E,OAAO,UAAU,kCAAkC,qBAAqB;AAAA,IACxE,MAAM,UAAU,iCAAiC,kFAAkF;AAAA,IACnI,KAAK,UAAU,gCAAgC,oBAAoB;AAAA,IACnE,MAAM,UAAU,iCAAiC,+DAA+D;AAAA,EAClH;AAEA,QAAM,UAAU,EAAE,IAAI,KAAK,OAAO,SAAS,OAAO,mBAAmB,EAAE,UAAU,KAAK,CAAC,EAAE,CAAC;AAC1F,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;AAEO,MAAM,WAAW;AAAA,EACtB,MAAM,CAAC;AACT;AAEA,MAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,OAAO,EAAE,OAAO,EAAE,MAAM;AAC1B,CAAC;AAED,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAC3C,IAAI,EAAE,QAAQ,IAAI;AACpB,CAAC;AAEM,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,4DAA4D,QAAQ,4BAA4B;AAAA,MAC9H;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -53,13 +53,12 @@ async function GET(req) {
|
|
|
53
53
|
["auth.sidebar.manage"],
|
|
54
54
|
{ tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null }
|
|
55
55
|
) ?? false;
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
userId: effectiveUserId,
|
|
56
|
+
const settings = await loadSidebarPreference(em, {
|
|
57
|
+
userId: auth.sub,
|
|
59
58
|
tenantId: auth.tenantId ?? null,
|
|
60
59
|
organizationId: auth.orgId ?? null,
|
|
61
60
|
locale
|
|
62
|
-
})
|
|
61
|
+
});
|
|
63
62
|
let rolesPayload = [];
|
|
64
63
|
if (canApplyToRoles) {
|
|
65
64
|
const roleScope = auth.tenantId ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] } : { tenantId: null };
|
|
@@ -78,11 +77,11 @@ async function GET(req) {
|
|
|
78
77
|
return NextResponse.json({
|
|
79
78
|
locale,
|
|
80
79
|
settings: {
|
|
81
|
-
version: settings
|
|
82
|
-
groupOrder: settings
|
|
83
|
-
groupLabels: settings
|
|
84
|
-
itemLabels: settings
|
|
85
|
-
hiddenItems: settings
|
|
80
|
+
version: settings.version ?? SIDEBAR_PREFERENCES_VERSION,
|
|
81
|
+
groupOrder: settings.groupOrder ?? [],
|
|
82
|
+
groupLabels: settings.groupLabels ?? {},
|
|
83
|
+
itemLabels: settings.itemLabels ?? {},
|
|
84
|
+
hiddenItems: settings.hiddenItems ?? []
|
|
86
85
|
},
|
|
87
86
|
canApplyToRoles,
|
|
88
87
|
roles: rolesPayload
|
|
@@ -91,10 +90,6 @@ async function GET(req) {
|
|
|
91
90
|
async function PUT(req) {
|
|
92
91
|
const auth = await getAuthFromRequest(req);
|
|
93
92
|
if (!auth) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
94
|
-
const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub;
|
|
95
|
-
if (!effectiveUserId) {
|
|
96
|
-
return NextResponse.json({ error: "Cannot save preferences: no user associated with this API key" }, { status: 403 });
|
|
97
|
-
}
|
|
98
93
|
let parsedBody;
|
|
99
94
|
try {
|
|
100
95
|
parsedBody = await req.json();
|
|
@@ -161,7 +156,7 @@ async function PUT(req) {
|
|
|
161
156
|
return NextResponse.json({ error: "Forbidden", requiredFeatures: ["auth.sidebar.manage"] }, { status: 403 });
|
|
162
157
|
}
|
|
163
158
|
const settings = await saveSidebarPreference(em, {
|
|
164
|
-
userId:
|
|
159
|
+
userId: auth.sub,
|
|
165
160
|
tenantId: auth.tenantId ?? null,
|
|
166
161
|
organizationId: auth.orgId ?? null,
|
|
167
162
|
locale
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/auth/api/sidebar/preferences/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { sidebarPreferencesInputSchema } from '../../../data/validators'\nimport {\n loadRoleSidebarPreferences,\n loadSidebarPreference,\n saveRoleSidebarPreference,\n saveSidebarPreference,\n} from '../../../services/sidebarPreferencesService'\nimport { SIDEBAR_PREFERENCES_VERSION } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { Role, RoleSidebarPreference } from '../../../data/entities'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\n\nexport const metadata = {\n GET: { requireAuth: true },\n PUT: { requireAuth: true },\n}\n\nconst sidebarSettingsSchema = z.object({\n version: z.number().int().positive(),\n groupOrder: z.array(z.string()),\n groupLabels: z.record(z.string(), z.string()),\n itemLabels: z.record(z.string(), z.string()),\n hiddenItems: z.array(z.string()),\n})\n\nconst sidebarRoleEntrySchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n hasPreference: z.boolean(),\n})\n\nconst sidebarPreferencesResponseSchema = z.object({\n locale: z.string(),\n settings: sidebarSettingsSchema,\n canApplyToRoles: z.boolean(),\n roles: z.array(sidebarRoleEntrySchema),\n})\n\nconst sidebarPreferencesUpdateResponseSchema = sidebarPreferencesResponseSchema.extend({\n appliedRoles: z.array(z.string().uuid()),\n clearedRoles: z.array(z.string().uuid()),\n})\n\nconst sidebarErrorSchema = z.object({\n error: z.string(),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { locale } = await resolveTranslations()\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['auth.sidebar.manage'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n // For API key auth, use userId (the actual user) if available\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n const settings = effectiveUserId\n ? await loadSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n })\n : null\n\n let rolesPayload: Array<{ id: string; name: string; hasPreference: boolean }> = []\n if (canApplyToRoles) {\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roles = await em.find(Role, roleScope as any, { orderBy: { name: 'asc' } })\n const rolePrefs = await loadRoleSidebarPreferences(em, {\n roleIds: roles.map((r: Role) => r.id),\n tenantId: auth.tenantId ?? null,\n locale,\n })\n rolesPayload = roles.map((role: Role) => ({\n id: role.id,\n name: role.name,\n hasPreference: rolePrefs.has(role.id),\n }))\n }\n\n return NextResponse.json({\n locale,\n settings: {\n version: settings?.version ?? SIDEBAR_PREFERENCES_VERSION,\n groupOrder: settings?.groupOrder ?? [],\n groupLabels: settings?.groupLabels ?? {},\n itemLabels: settings?.itemLabels ?? {},\n hiddenItems: settings?.hiddenItems ?? [],\n },\n canApplyToRoles,\n roles: rolesPayload,\n })\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n // For API key auth, use userId (the actual user) if available\n const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub\n if (!effectiveUserId) {\n return NextResponse.json({ error: 'Cannot save preferences: no user associated with this API key' }, { status: 403 })\n }\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n\n const parsed = sidebarPreferencesInputSchema.safeParse(parsedBody)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const sanitizeRecord = (record?: Record<string, string>) => {\n if (!record) return {}\n const result: Record<string, string> = {}\n for (const [key, value] of Object.entries(record)) {\n const trimmedKey = key.trim()\n const trimmedValue = value.trim()\n if (!trimmedKey || !trimmedValue) continue\n result[trimmedKey] = trimmedValue\n }\n return result\n }\n\n const groupOrderSource = parsed.data.groupOrder ?? []\n const seen = new Set<string>()\n const groupOrder: string[] = []\n for (const id of groupOrderSource) {\n const trimmed = id.trim()\n if (!trimmed || seen.has(trimmed)) continue\n seen.add(trimmed)\n groupOrder.push(trimmed)\n }\n\n const payload = {\n version: parsed.data.version ?? SIDEBAR_PREFERENCES_VERSION,\n groupOrder,\n groupLabels: sanitizeRecord(parsed.data.groupLabels),\n itemLabels: sanitizeRecord(parsed.data.itemLabels),\n hiddenItems: (() => {\n const source = parsed.data.hiddenItems ?? []\n const seenHidden = new Set<string>()\n const values: string[] = []\n for (const href of source) {\n const trimmed = href.trim()\n if (!trimmed || seenHidden.has(trimmed)) continue\n seenHidden.add(trimmed)\n values.push(trimmed)\n }\n return values\n })(),\n }\n\n const { locale } = await resolveTranslations()\n const container = await createRequestContainer()\n const em = container.resolve('em') as any\n const rbac = container.resolve('rbacService') as any\n const cache = container.resolve('cache') as { deleteByTags?: (tags: string[]) => Promise<unknown> } | undefined\n\n const applyToRolesSource = parsed.data.applyToRoles ?? []\n const applyToRoles = Array.from(new Set(applyToRolesSource.map((id) => id.trim()).filter((id) => id.length > 0)))\n const clearRoleIdsSource = parsed.data.clearRoleIds ?? []\n const clearRoleIds = Array.from(new Set(clearRoleIdsSource.map((id) => id.trim()).filter((id) => id.length > 0)))\n\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['auth.sidebar.manage'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n if ((applyToRoles.length > 0 || clearRoleIds.length > 0) && !canApplyToRoles) {\n return NextResponse.json({ error: 'Forbidden', requiredFeatures: ['auth.sidebar.manage'] }, { status: 403 })\n }\n\n const settings = await saveSidebarPreference(em, {\n userId: effectiveUserId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n }, payload)\n\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const availableRoles = canApplyToRoles\n ? await em.find(Role, roleScope as any, { orderBy: { name: 'asc' } })\n : []\n const roleMap = new Map<string, Role>(availableRoles.map((role: Role) => [String(role.id), role]))\n\n let updatedRoleIds: string[] = []\n if (applyToRoles.length > 0) {\n const missing = applyToRoles.filter((id) => !roleMap.has(id))\n if (missing.length) {\n return NextResponse.json({ error: 'Invalid roles', missing }, { status: 400 })\n }\n for (const roleId of applyToRoles) {\n const role = roleMap.get(roleId)!\n await saveRoleSidebarPreference(em, {\n roleId: role.id,\n tenantId: auth.tenantId ?? null,\n locale,\n }, payload)\n updatedRoleIds.push(role.id)\n }\n }\n\n const filteredClearRoleIds = clearRoleIds.filter((id) => !updatedRoleIds.includes(id) && !applyToRoles.includes(id))\n\n if (filteredClearRoleIds.length > 0) {\n await em.nativeDelete(RoleSidebarPreference, {\n role: { $in: filteredClearRoleIds },\n locale,\n tenantId: auth.tenantId ?? null,\n })\n if (cache?.deleteByTags) {\n try {\n await cache.deleteByTags(filteredClearRoleIds.map((roleId) => `nav:sidebar:role:${roleId}`))\n } catch {}\n }\n }\n\n if (cache?.deleteByTags) {\n const tags = [\n `nav:sidebar:user:${auth.sub}`,\n `nav:sidebar:scope:${auth.sub}:${auth.tenantId ?? 'null'}:${auth.orgId ?? 'null'}:${locale}`,\n ...updatedRoleIds.map((roleId) => `nav:sidebar:role:${roleId}`),\n ]\n try {\n await cache.deleteByTags(tags)\n } catch {}\n }\n\n let rolesPayload: Array<{ id: string; name: string; hasPreference: boolean }> = []\n if (canApplyToRoles) {\n const rolePrefs = await loadRoleSidebarPreferences(em, {\n roleIds: availableRoles.map((role: Role) => role.id),\n tenantId: auth.tenantId ?? null,\n locale,\n })\n rolesPayload = availableRoles.map((role: Role) => ({\n id: role.id,\n name: role.name,\n hasPreference: rolePrefs.has(role.id),\n }))\n }\n\n return NextResponse.json({\n locale,\n settings,\n canApplyToRoles,\n roles: rolesPayload,\n appliedRoles: updatedRoleIds,\n clearedRoles: filteredClearRoleIds,\n })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Sidebar preferences',\n methods: {\n GET: {\n summary: 'Get sidebar preferences',\n description: 'Returns personal sidebar customization and any role-level preferences the user can manage.',\n responses: [\n { status: 200, description: 'Current sidebar configuration', schema: sidebarPreferencesResponseSchema },\n { status: 401, description: 'Unauthorized', schema: sidebarErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update sidebar preferences',\n description: 'Updates personal sidebar configuration and, optionally, applies the same settings to selected roles.',\n requestBody: {\n contentType: 'application/json',\n schema: sidebarPreferencesInputSchema,\n },\n responses: [\n { status: 200, description: 'Preferences saved', schema: sidebarPreferencesUpdateResponseSchema },\n { status: 400, description: 'Invalid payload', schema: sidebarErrorSchema },\n { status: 401, description: 'Unauthorized', schema: sidebarErrorSchema },\n { status: 403, description: 'Missing features for role-wide updates', schema: sidebarErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,qCAAqC;AAC9C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAC5C,SAAS,MAAM,6BAA6B;AAE5C,SAAS,SAAS;AAEX,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,KAAK;AAAA,EACzB,KAAK,EAAE,aAAa,KAAK;AAC3B;AAEA,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACnC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC9B,aAAa,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC;AAAA,EAC5C,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC;AAAA,EAC3C,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC;AACjC,CAAC;AAED,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,EACf,eAAe,EAAE,QAAQ;AAC3B,CAAC;AAED,MAAM,mCAAmC,EAAE,OAAO;AAAA,EAChD,QAAQ,EAAE,OAAO;AAAA,EACjB,UAAU;AAAA,EACV,iBAAiB,EAAE,QAAQ;AAAA,EAC3B,OAAO,EAAE,MAAM,sBAAsB;AACvC,CAAC;AAED,MAAM,yCAAyC,iCAAiC,OAAO;AAAA,EACrF,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,EACvC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AACzC,CAAC;AAED,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAE9E,QAAM,EAAE,OAAO,IAAI,MAAM,oBAAoB;AAC7C,QAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,QAAM,KAAK,QAAQ,IAAI;AACvB,QAAM,OAAO,QAAQ,aAAa;AAElC,QAAM,kBAAkB,MAAM,KAAK;AAAA,IACjC,KAAK;AAAA,IACL,CAAC,qBAAqB;AAAA,IACtB,EAAE,UAAU,KAAK,YAAY,MAAM,gBAAgB,KAAK,SAAS,KAAK;AAAA,EACxE,KAAK;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { sidebarPreferencesInputSchema } from '../../../data/validators'\nimport {\n loadRoleSidebarPreferences,\n loadSidebarPreference,\n saveRoleSidebarPreference,\n saveSidebarPreference,\n} from '../../../services/sidebarPreferencesService'\nimport { SIDEBAR_PREFERENCES_VERSION } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { Role, RoleSidebarPreference } from '../../../data/entities'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\n\nexport const metadata = {\n GET: { requireAuth: true },\n PUT: { requireAuth: true },\n}\n\nconst sidebarSettingsSchema = z.object({\n version: z.number().int().positive(),\n groupOrder: z.array(z.string()),\n groupLabels: z.record(z.string(), z.string()),\n itemLabels: z.record(z.string(), z.string()),\n hiddenItems: z.array(z.string()),\n})\n\nconst sidebarRoleEntrySchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n hasPreference: z.boolean(),\n})\n\nconst sidebarPreferencesResponseSchema = z.object({\n locale: z.string(),\n settings: sidebarSettingsSchema,\n canApplyToRoles: z.boolean(),\n roles: z.array(sidebarRoleEntrySchema),\n})\n\nconst sidebarPreferencesUpdateResponseSchema = sidebarPreferencesResponseSchema.extend({\n appliedRoles: z.array(z.string().uuid()),\n clearedRoles: z.array(z.string().uuid()),\n})\n\nconst sidebarErrorSchema = z.object({\n error: z.string(),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { locale } = await resolveTranslations()\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['auth.sidebar.manage'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n const settings = await loadSidebarPreference(em, {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n })\n\n let rolesPayload: Array<{ id: string; name: string; hasPreference: boolean }> = []\n if (canApplyToRoles) {\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roles = await em.find(Role, roleScope as any, { orderBy: { name: 'asc' } })\n const rolePrefs = await loadRoleSidebarPreferences(em, {\n roleIds: roles.map((r: Role) => r.id),\n tenantId: auth.tenantId ?? null,\n locale,\n })\n rolesPayload = roles.map((role: Role) => ({\n id: role.id,\n name: role.name,\n hasPreference: rolePrefs.has(role.id),\n }))\n }\n\n return NextResponse.json({\n locale,\n settings: {\n version: settings.version ?? SIDEBAR_PREFERENCES_VERSION,\n groupOrder: settings.groupOrder ?? [],\n groupLabels: settings.groupLabels ?? {},\n itemLabels: settings.itemLabels ?? {},\n hiddenItems: settings.hiddenItems ?? [],\n },\n canApplyToRoles,\n roles: rolesPayload,\n })\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n\n const parsed = sidebarPreferencesInputSchema.safeParse(parsedBody)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const sanitizeRecord = (record?: Record<string, string>) => {\n if (!record) return {}\n const result: Record<string, string> = {}\n for (const [key, value] of Object.entries(record)) {\n const trimmedKey = key.trim()\n const trimmedValue = value.trim()\n if (!trimmedKey || !trimmedValue) continue\n result[trimmedKey] = trimmedValue\n }\n return result\n }\n\n const groupOrderSource = parsed.data.groupOrder ?? []\n const seen = new Set<string>()\n const groupOrder: string[] = []\n for (const id of groupOrderSource) {\n const trimmed = id.trim()\n if (!trimmed || seen.has(trimmed)) continue\n seen.add(trimmed)\n groupOrder.push(trimmed)\n }\n\n const payload = {\n version: parsed.data.version ?? SIDEBAR_PREFERENCES_VERSION,\n groupOrder,\n groupLabels: sanitizeRecord(parsed.data.groupLabels),\n itemLabels: sanitizeRecord(parsed.data.itemLabels),\n hiddenItems: (() => {\n const source = parsed.data.hiddenItems ?? []\n const seenHidden = new Set<string>()\n const values: string[] = []\n for (const href of source) {\n const trimmed = href.trim()\n if (!trimmed || seenHidden.has(trimmed)) continue\n seenHidden.add(trimmed)\n values.push(trimmed)\n }\n return values\n })(),\n }\n\n const { locale } = await resolveTranslations()\n const container = await createRequestContainer()\n const em = container.resolve('em') as any\n const rbac = container.resolve('rbacService') as any\n const cache = container.resolve('cache') as { deleteByTags?: (tags: string[]) => Promise<unknown> } | undefined\n\n const applyToRolesSource = parsed.data.applyToRoles ?? []\n const applyToRoles = Array.from(new Set(applyToRolesSource.map((id) => id.trim()).filter((id) => id.length > 0)))\n const clearRoleIdsSource = parsed.data.clearRoleIds ?? []\n const clearRoleIds = Array.from(new Set(clearRoleIdsSource.map((id) => id.trim()).filter((id) => id.length > 0)))\n\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['auth.sidebar.manage'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n if ((applyToRoles.length > 0 || clearRoleIds.length > 0) && !canApplyToRoles) {\n return NextResponse.json({ error: 'Forbidden', requiredFeatures: ['auth.sidebar.manage'] }, { status: 403 })\n }\n\n const settings = await saveSidebarPreference(em, {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n locale,\n }, payload)\n\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const availableRoles = canApplyToRoles\n ? await em.find(Role, roleScope as any, { orderBy: { name: 'asc' } })\n : []\n const roleMap = new Map<string, Role>(availableRoles.map((role: Role) => [String(role.id), role]))\n\n let updatedRoleIds: string[] = []\n if (applyToRoles.length > 0) {\n const missing = applyToRoles.filter((id) => !roleMap.has(id))\n if (missing.length) {\n return NextResponse.json({ error: 'Invalid roles', missing }, { status: 400 })\n }\n for (const roleId of applyToRoles) {\n const role = roleMap.get(roleId)!\n await saveRoleSidebarPreference(em, {\n roleId: role.id,\n tenantId: auth.tenantId ?? null,\n locale,\n }, payload)\n updatedRoleIds.push(role.id)\n }\n }\n\n const filteredClearRoleIds = clearRoleIds.filter((id) => !updatedRoleIds.includes(id) && !applyToRoles.includes(id))\n\n if (filteredClearRoleIds.length > 0) {\n await em.nativeDelete(RoleSidebarPreference, {\n role: { $in: filteredClearRoleIds },\n locale,\n tenantId: auth.tenantId ?? null,\n })\n if (cache?.deleteByTags) {\n try {\n await cache.deleteByTags(filteredClearRoleIds.map((roleId) => `nav:sidebar:role:${roleId}`))\n } catch {}\n }\n }\n\n if (cache?.deleteByTags) {\n const tags = [\n `nav:sidebar:user:${auth.sub}`,\n `nav:sidebar:scope:${auth.sub}:${auth.tenantId ?? 'null'}:${auth.orgId ?? 'null'}:${locale}`,\n ...updatedRoleIds.map((roleId) => `nav:sidebar:role:${roleId}`),\n ]\n try {\n await cache.deleteByTags(tags)\n } catch {}\n }\n\n let rolesPayload: Array<{ id: string; name: string; hasPreference: boolean }> = []\n if (canApplyToRoles) {\n const rolePrefs = await loadRoleSidebarPreferences(em, {\n roleIds: availableRoles.map((role: Role) => role.id),\n tenantId: auth.tenantId ?? null,\n locale,\n })\n rolesPayload = availableRoles.map((role: Role) => ({\n id: role.id,\n name: role.name,\n hasPreference: rolePrefs.has(role.id),\n }))\n }\n\n return NextResponse.json({\n locale,\n settings,\n canApplyToRoles,\n roles: rolesPayload,\n appliedRoles: updatedRoleIds,\n clearedRoles: filteredClearRoleIds,\n })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Sidebar preferences',\n methods: {\n GET: {\n summary: 'Get sidebar preferences',\n description: 'Returns personal sidebar customization and any role-level preferences the user can manage.',\n responses: [\n { status: 200, description: 'Current sidebar configuration', schema: sidebarPreferencesResponseSchema },\n { status: 401, description: 'Unauthorized', schema: sidebarErrorSchema },\n ],\n },\n PUT: {\n summary: 'Update sidebar preferences',\n description: 'Updates personal sidebar configuration and, optionally, applies the same settings to selected roles.',\n requestBody: {\n contentType: 'application/json',\n schema: sidebarPreferencesInputSchema,\n },\n responses: [\n { status: 200, description: 'Preferences saved', schema: sidebarPreferencesUpdateResponseSchema },\n { status: 400, description: 'Invalid payload', schema: sidebarErrorSchema },\n { status: 401, description: 'Unauthorized', schema: sidebarErrorSchema },\n { status: 403, description: 'Missing features for role-wide updates', schema: sidebarErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -11,7 +11,6 @@ import { loadCustomFieldValues } from "@open-mercato/shared/lib/crud/custom-fiel
|
|
|
11
11
|
import { userCrudEvents, userCrudIndexer } from "@open-mercato/core/modules/auth/commands/users";
|
|
12
12
|
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
13
13
|
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
|
|
14
|
-
import { buildPasswordSchema } from "@open-mercato/shared/lib/auth/passwordPolicy";
|
|
15
14
|
const querySchema = z.object({
|
|
16
15
|
id: z.string().uuid().optional(),
|
|
17
16
|
page: z.coerce.number().min(1).default(1),
|
|
@@ -21,17 +20,16 @@ const querySchema = z.object({
|
|
|
21
20
|
roleIds: z.array(z.string().uuid()).optional()
|
|
22
21
|
}).passthrough();
|
|
23
22
|
const rawBodySchema = z.object({}).passthrough();
|
|
24
|
-
const passwordSchema = buildPasswordSchema();
|
|
25
23
|
const userCreateSchema = z.object({
|
|
26
24
|
email: z.string().email(),
|
|
27
|
-
password:
|
|
25
|
+
password: z.string().min(6),
|
|
28
26
|
organizationId: z.string().uuid(),
|
|
29
27
|
roles: z.array(z.string()).optional()
|
|
30
28
|
});
|
|
31
29
|
const userUpdateSchema = z.object({
|
|
32
30
|
id: z.string().uuid(),
|
|
33
31
|
email: z.string().email().optional(),
|
|
34
|
-
password:
|
|
32
|
+
password: z.string().min(6).optional(),
|
|
35
33
|
organizationId: z.string().uuid().optional(),
|
|
36
34
|
roles: z.array(z.string()).optional()
|
|
37
35
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/auth/api/users/route.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\nconst querySchema = z.object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n organizationId: z.string().uuid().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n}).passthrough()\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst passwordSchema = buildPasswordSchema()\n\nconst userCreateSchema = z.object({\n email: z.string().email(),\n password: passwordSchema,\n organizationId: z.string().uuid(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userUpdateSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email().optional(),\n password: passwordSchema.optional(),\n organizationId: z.string().uuid().optional(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userListItemSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email(),\n organizationId: z.string().uuid().nullable(),\n organizationName: z.string().nullable(),\n tenantId: z.string().uuid().nullable(),\n tenantName: z.string().nullable(),\n roles: z.array(z.string()),\n})\n\nconst userListResponseSchema = z.object({\n items: z.array(userListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n isSuperAdmin: z.boolean().optional(),\n})\n\nconst okResponseSchema = z.object({ ok: z.literal(true) })\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\ntype CrudInput = Record<string, unknown>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.users.list'] },\n POST: { requireAuth: true, requireFeatures: ['auth.users.create'] },\n PUT: { requireAuth: true, requireFeatures: ['auth.users.edit'] },\n DELETE: { requireAuth: true, requireFeatures: ['auth.users.delete'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: User,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: userCrudEvents,\n indexer: userCrudIndexer,\n actions: {\n create: {\n commandId: 'auth.users.create',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles)\n }\n return parsed\n },\n response: ({ result }) => ({ id: String(result.id) }),\n status: 201,\n },\n update: {\n commandId: 'auth.users.update',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'auth.users.delete',\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const url = new URL(req.url)\n const rawRoleIds = url.searchParams.getAll('roleId').filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n organizationId: url.searchParams.get('organizationId') || undefined,\n roleIds: rawRoleIds.length ? rawRoleIds : undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n let isSuperAdmin = false\n try {\n if (auth.sub) {\n const rbacService = container.resolve('rbacService') as any\n const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n isSuperAdmin = !!acl?.isSuperAdmin\n }\n } catch (err) {\n console.error('users: failed to resolve rbac', err)\n }\n const { id, page, pageSize, search, organizationId, roleIds } = parsed.data\n const where: any = { deletedAt: null }\n if (!isSuperAdmin) {\n if (!auth.tenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n where.tenantId = auth.tenantId\n }\n if (organizationId) where.organizationId = organizationId\n if (search) where.email = { $ilike: `%${escapeLikePattern(search)}%` } as any\n let idFilter: Set<string> | null = id ? new Set([id]) : null\n if (Array.isArray(roleIds) && roleIds.length > 0) {\n const uniqueRoleIds = Array.from(new Set(roleIds))\n const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds as any } } as any)\n const roleUserIds = new Set<string>()\n for (const link of linksForRoles) {\n const uid = String((link as any).user?.id || (link as any).user || '')\n if (uid) roleUserIds.add(uid)\n }\n if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n if (idFilter) {\n for (const uid of Array.from(idFilter)) {\n if (!roleUserIds.has(uid)) idFilter.delete(uid)\n }\n } else {\n idFilter = roleUserIds\n }\n if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n }\n if (idFilter && idFilter.size) {\n where.id = { $in: Array.from(idFilter) as any }\n } else if (id) {\n where.id = id\n }\n const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize })\n const userIds = rows.map((u: any) => u.id)\n const links = userIds.length\n ? await findWithDecryption(\n em,\n UserRole,\n { user: { $in: userIds as any } } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n : []\n const roleMap: Record<string, string[]> = {}\n for (const l of links) {\n const uid = String((l as any).user?.id || (l as any).user)\n const rname = String((l as any).role?.name || '')\n if (!roleMap[uid]) roleMap[uid] = []\n if (rname) roleMap[uid].push(rname)\n }\n const orgIds = rows\n .map((u: any) => (u.organizationId ? String(u.organizationId) : null))\n .filter((id): id is string => !!id)\n const uniqueOrgIds = Array.from(new Set(orgIds))\n let orgMap: Record<string, string> = {}\n if (uniqueOrgIds.length) {\n const organizations = await em.find(\n Organization,\n { id: { $in: uniqueOrgIds as any }, deletedAt: null },\n )\n orgMap = organizations.reduce<Record<string, string>>((acc, org) => {\n const orgId = org?.id ? String(org.id) : null\n if (!orgId) return acc\n const rawName = (org as any)?.name\n const orgName = typeof rawName === 'string' && rawName.length > 0 ? rawName : orgId\n acc[orgId] = orgName\n return acc\n }, {})\n }\n const tenantIds = rows\n .map((u: any) => (u.tenantId ? String(u.tenantId) : null))\n .filter((id): id is string => !!id)\n const uniqueTenantIds = Array.from(new Set(tenantIds))\n let tenantMap: Record<string, string> = {}\n if (uniqueTenantIds.length) {\n const tenants = await em.find(\n Tenant,\n { id: { $in: uniqueTenantIds as any }, deletedAt: null },\n )\n tenantMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tenantId = tenant?.id ? String(tenant.id) : null\n if (!tenantId) return acc\n const rawName = (tenant as any)?.name\n const tenantName = typeof rawName === 'string' && rawName.length > 0 ? rawName : tenantId\n acc[tenantId] = tenantName\n return acc\n }, {})\n }\n const tenantByUser: Record<string, string | null> = {}\n const organizationByUser: Record<string, string | null> = {}\n for (const u of rows) {\n const uid = String(u.id)\n tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null\n organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null\n }\n const cfByUser = userIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.auth.user,\n recordIds: userIds.map(String),\n tenantIdByRecord: tenantByUser,\n organizationIdByRecord: organizationByUser,\n tenantFallbacks: auth.tenantId ? [auth.tenantId] : [],\n })\n : {}\n\n const items = rows.map((u: any) => {\n const uid = String(u.id)\n const orgId = u.organizationId ? String(u.organizationId) : null\n return {\n id: uid,\n email: String(u.email),\n organizationId: orgId,\n organizationName: orgId ? orgMap[orgId] ?? orgId : null,\n tenantId: u.tenantId ? String(u.tenantId) : null,\n tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,\n roles: roleMap[uid] || [],\n ...(cfByUser[uid] || {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'auth.user',\n organizationId: null,\n tenantId: auth.tenantId ?? null,\n query: parsed.data,\n accessType: id ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total: count, totalPages, isSuperAdmin })\n}\n\nexport const POST = async (req: Request) => {\n const body = await req.clone().json().catch(() => ({}))\n await assertCanAssignRoles(req, body?.roles)\n return crud.POST(req)\n}\n\nexport const PUT = async (req: Request) => {\n const body = await req.clone().json().catch(() => ({}))\n await assertCanAssignRoles(req, body?.roles)\n return crud.PUT(req)\n}\n\nexport const DELETE = crud.DELETE\n\nasync function assertCanAssignRoles(req: Request, roles: unknown) {\n if (!Array.isArray(roles)) return\n const normalized = roles\n .map((role) => (typeof role === 'string' ? role.trim().toLowerCase() : null))\n .filter((role): role is string => !!role)\n if (!normalized.includes('superadmin')) return\n const auth = await getAuthFromRequest(req)\n if (!auth) throw new Error('Unauthorized')\n const container = await createRequestContainer()\n const rbac = container.resolve('rbacService') as RbacService\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n if (!acl?.isSuperAdmin) {\n throw forbidden('Only super administrators can assign the superadmin role.')\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'User management',\n methods: {\n GET: {\n summary: 'List users',\n description:\n 'Returns users for the current tenant. Super administrators may scope the response via organization or role filters.',\n query: querySchema,\n responses: [\n { status: 200, description: 'User collection', schema: userListResponseSchema },\n ],\n },\n POST: {\n summary: 'Create user',\n description: 'Creates a new confirmed user within the specified organization and optional roles.',\n requestBody: {\n contentType: 'application/json',\n schema: userCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'User created',\n schema: z.object({ id: z.string().uuid() }),\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or duplicate email', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update user',\n description: 'Updates profile fields, organization assignment, credentials, or role memberships.',\n requestBody: {\n contentType: 'application/json',\n schema: userUpdateSchema,\n },\n responses: [\n { status: 200, description: 'User updated', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete user',\n description: 'Deletes a user by identifier. Undo support is provided via the command bus.',\n query: z.object({ id: z.string().uuid().describe('User identifier') }),\n responses: [\n { status: 200, description: 'User deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'User cannot be deleted', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\n\nconst querySchema = z.object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n organizationId: z.string().uuid().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n}).passthrough()\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst userCreateSchema = z.object({\n email: z.string().email(),\n password: z.string().min(6),\n organizationId: z.string().uuid(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userUpdateSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email().optional(),\n password: z.string().min(6).optional(),\n organizationId: z.string().uuid().optional(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userListItemSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email(),\n organizationId: z.string().uuid().nullable(),\n organizationName: z.string().nullable(),\n tenantId: z.string().uuid().nullable(),\n tenantName: z.string().nullable(),\n roles: z.array(z.string()),\n})\n\nconst userListResponseSchema = z.object({\n items: z.array(userListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n isSuperAdmin: z.boolean().optional(),\n})\n\nconst okResponseSchema = z.object({ ok: z.literal(true) })\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\ntype CrudInput = Record<string, unknown>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.users.list'] },\n POST: { requireAuth: true, requireFeatures: ['auth.users.create'] },\n PUT: { requireAuth: true, requireFeatures: ['auth.users.edit'] },\n DELETE: { requireAuth: true, requireFeatures: ['auth.users.delete'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: User,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: userCrudEvents,\n indexer: userCrudIndexer,\n actions: {\n create: {\n commandId: 'auth.users.create',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles)\n }\n return parsed\n },\n response: ({ result }) => ({ id: String(result.id) }),\n status: 201,\n },\n update: {\n commandId: 'auth.users.update',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'auth.users.delete',\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const url = new URL(req.url)\n const rawRoleIds = url.searchParams.getAll('roleId').filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n organizationId: url.searchParams.get('organizationId') || undefined,\n roleIds: rawRoleIds.length ? rawRoleIds : undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n let isSuperAdmin = false\n try {\n if (auth.sub) {\n const rbacService = container.resolve('rbacService') as any\n const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n isSuperAdmin = !!acl?.isSuperAdmin\n }\n } catch (err) {\n console.error('users: failed to resolve rbac', err)\n }\n const { id, page, pageSize, search, organizationId, roleIds } = parsed.data\n const where: any = { deletedAt: null }\n if (!isSuperAdmin) {\n if (!auth.tenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n where.tenantId = auth.tenantId\n }\n if (organizationId) where.organizationId = organizationId\n if (search) where.email = { $ilike: `%${escapeLikePattern(search)}%` } as any\n let idFilter: Set<string> | null = id ? new Set([id]) : null\n if (Array.isArray(roleIds) && roleIds.length > 0) {\n const uniqueRoleIds = Array.from(new Set(roleIds))\n const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds as any } } as any)\n const roleUserIds = new Set<string>()\n for (const link of linksForRoles) {\n const uid = String((link as any).user?.id || (link as any).user || '')\n if (uid) roleUserIds.add(uid)\n }\n if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n if (idFilter) {\n for (const uid of Array.from(idFilter)) {\n if (!roleUserIds.has(uid)) idFilter.delete(uid)\n }\n } else {\n idFilter = roleUserIds\n }\n if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n }\n if (idFilter && idFilter.size) {\n where.id = { $in: Array.from(idFilter) as any }\n } else if (id) {\n where.id = id\n }\n const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize })\n const userIds = rows.map((u: any) => u.id)\n const links = userIds.length\n ? await findWithDecryption(\n em,\n UserRole,\n { user: { $in: userIds as any } } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n : []\n const roleMap: Record<string, string[]> = {}\n for (const l of links) {\n const uid = String((l as any).user?.id || (l as any).user)\n const rname = String((l as any).role?.name || '')\n if (!roleMap[uid]) roleMap[uid] = []\n if (rname) roleMap[uid].push(rname)\n }\n const orgIds = rows\n .map((u: any) => (u.organizationId ? String(u.organizationId) : null))\n .filter((id): id is string => !!id)\n const uniqueOrgIds = Array.from(new Set(orgIds))\n let orgMap: Record<string, string> = {}\n if (uniqueOrgIds.length) {\n const organizations = await em.find(\n Organization,\n { id: { $in: uniqueOrgIds as any }, deletedAt: null },\n )\n orgMap = organizations.reduce<Record<string, string>>((acc, org) => {\n const orgId = org?.id ? String(org.id) : null\n if (!orgId) return acc\n const rawName = (org as any)?.name\n const orgName = typeof rawName === 'string' && rawName.length > 0 ? rawName : orgId\n acc[orgId] = orgName\n return acc\n }, {})\n }\n const tenantIds = rows\n .map((u: any) => (u.tenantId ? String(u.tenantId) : null))\n .filter((id): id is string => !!id)\n const uniqueTenantIds = Array.from(new Set(tenantIds))\n let tenantMap: Record<string, string> = {}\n if (uniqueTenantIds.length) {\n const tenants = await em.find(\n Tenant,\n { id: { $in: uniqueTenantIds as any }, deletedAt: null },\n )\n tenantMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tenantId = tenant?.id ? String(tenant.id) : null\n if (!tenantId) return acc\n const rawName = (tenant as any)?.name\n const tenantName = typeof rawName === 'string' && rawName.length > 0 ? rawName : tenantId\n acc[tenantId] = tenantName\n return acc\n }, {})\n }\n const tenantByUser: Record<string, string | null> = {}\n const organizationByUser: Record<string, string | null> = {}\n for (const u of rows) {\n const uid = String(u.id)\n tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null\n organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null\n }\n const cfByUser = userIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.auth.user,\n recordIds: userIds.map(String),\n tenantIdByRecord: tenantByUser,\n organizationIdByRecord: organizationByUser,\n tenantFallbacks: auth.tenantId ? [auth.tenantId] : [],\n })\n : {}\n\n const items = rows.map((u: any) => {\n const uid = String(u.id)\n const orgId = u.organizationId ? String(u.organizationId) : null\n return {\n id: uid,\n email: String(u.email),\n organizationId: orgId,\n organizationName: orgId ? orgMap[orgId] ?? orgId : null,\n tenantId: u.tenantId ? String(u.tenantId) : null,\n tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,\n roles: roleMap[uid] || [],\n ...(cfByUser[uid] || {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'auth.user',\n organizationId: null,\n tenantId: auth.tenantId ?? null,\n query: parsed.data,\n accessType: id ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total: count, totalPages, isSuperAdmin })\n}\n\nexport const POST = async (req: Request) => {\n const body = await req.clone().json().catch(() => ({}))\n await assertCanAssignRoles(req, body?.roles)\n return crud.POST(req)\n}\n\nexport const PUT = async (req: Request) => {\n const body = await req.clone().json().catch(() => ({}))\n await assertCanAssignRoles(req, body?.roles)\n return crud.PUT(req)\n}\n\nexport const DELETE = crud.DELETE\n\nasync function assertCanAssignRoles(req: Request, roles: unknown) {\n if (!Array.isArray(roles)) return\n const normalized = roles\n .map((role) => (typeof role === 'string' ? role.trim().toLowerCase() : null))\n .filter((role): role is string => !!role)\n if (!normalized.includes('superadmin')) return\n const auth = await getAuthFromRequest(req)\n if (!auth) throw new Error('Unauthorized')\n const container = await createRequestContainer()\n const rbac = container.resolve('rbacService') as RbacService\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n if (!acl?.isSuperAdmin) {\n throw forbidden('Only super administrators can assign the superadmin role.')\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'User management',\n methods: {\n GET: {\n summary: 'List users',\n description:\n 'Returns users for the current tenant. Super administrators may scope the response via organization or role filters.',\n query: querySchema,\n responses: [\n { status: 200, description: 'User collection', schema: userListResponseSchema },\n ],\n },\n POST: {\n summary: 'Create user',\n description: 'Creates a new confirmed user within the specified organization and optional roles.',\n requestBody: {\n contentType: 'application/json',\n schema: userCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'User created',\n schema: z.object({ id: z.string().uuid() }),\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or duplicate email', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update user',\n description: 'Updates profile fields, organization assignment, credentials, or role memberships.',\n requestBody: {\n contentType: 'application/json',\n schema: userUpdateSchema,\n },\n responses: [\n { status: 200, description: 'User updated', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete user',\n description: 'Deletes a user by identifier. Undo support is provided via the command bus.',\n query: z.object({ id: z.string().uuid().describe('User identifier') }),\n responses: [\n { status: 200, description: 'User deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'User cannot be deleted', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["id"]
|
|
7
7
|
}
|
|
@@ -19,7 +19,6 @@ function EditRolePage({ params }) {
|
|
|
19
19
|
const [aclData, setAclData] = React.useState({ isSuperAdmin: false, features: [], organizations: null });
|
|
20
20
|
const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false);
|
|
21
21
|
const [selectedTenantId, setSelectedTenantId] = React.useState(null);
|
|
22
|
-
const widgetEditorRef = React.useRef(null);
|
|
23
22
|
React.useEffect(() => {
|
|
24
23
|
if (!id) return;
|
|
25
24
|
const roleId = id;
|
|
@@ -128,8 +127,7 @@ function EditRolePage({ params }) {
|
|
|
128
127
|
{
|
|
129
128
|
kind: "role",
|
|
130
129
|
targetId: String(id),
|
|
131
|
-
tenantId: selectedTenantId ?? (initial?.tenantId ?? null)
|
|
132
|
-
ref: widgetEditorRef
|
|
130
|
+
tenantId: selectedTenantId ?? (initial?.tenantId ?? null)
|
|
133
131
|
}
|
|
134
132
|
) : null
|
|
135
133
|
}
|
|
@@ -165,7 +163,6 @@ function EditRolePage({ params }) {
|
|
|
165
163
|
await updateCrud("auth/roles/acl", { roleId: id, tenantId: effectiveTenantId, ...aclData }, {
|
|
166
164
|
errorMessage: t("auth.roles.form.errors.aclUpdate", "Failed to update role access control")
|
|
167
165
|
});
|
|
168
|
-
await widgetEditorRef.current?.save();
|
|
169
166
|
try {
|
|
170
167
|
window.dispatchEvent(new Event("om:refresh-sidebar"));
|
|
171
168
|
} catch {
|