@open-mercato/core 0.6.5-develop.5337.1.534b781eac → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +1 -1
- package/dist/bootstrap.js +46 -6
- package/dist/bootstrap.js.map +2 -2
- package/dist/generated/entities/organization/index.js +2 -0
- package/dist/generated/entities/organization/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/crmFixtures.js +4 -0
- package/dist/helpers/integration/crmFixtures.js.map +2 -2
- package/dist/modules/attachments/api/library/route.js +2 -2
- package/dist/modules/attachments/api/library/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +2 -0
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentContentPreview.js +9 -5
- package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
- package/dist/modules/attachments/lib/access.js +18 -0
- package/dist/modules/attachments/lib/access.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +3 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/audit_logs/data/entities.js +2 -1
- package/dist/modules/audit_logs/data/entities.js.map +2 -2
- package/dist/modules/audit_logs/migrations/Migration20260611104500.js +13 -0
- package/dist/modules/audit_logs/migrations/Migration20260611104500.js.map +7 -0
- package/dist/modules/audit_logs/services/accessLogService.js +10 -0
- package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +9 -0
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/login.js +4 -13
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/commands/users.js +20 -14
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/entities.js +4 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +35 -2
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/auth/lib/consentIntegrity.js +3 -3
- package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
- package/dist/modules/auth/migrations/Migration20260610120000.js +30 -0
- package/dist/modules/auth/migrations/Migration20260610120000.js.map +7 -0
- package/dist/modules/auth/migrations/Migration20260611103000.js +15 -0
- package/dist/modules/auth/migrations/Migration20260611103000.js.map +7 -0
- package/dist/modules/auth/services/authService.js +5 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +3 -2
- package/dist/modules/auth/services/rbacService.js.map +2 -2
- package/dist/modules/catalog/ai-tools/configuration-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/products-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/variants-pack.js.map +1 -1
- package/dist/modules/communication_channels/data/entities.js.map +1 -1
- package/dist/modules/communication_channels/encryption.js.map +1 -1
- package/dist/modules/communication_channels/lib/thread-matcher.js.map +1 -1
- package/dist/modules/communication_channels/lib/thread-token.js.map +1 -1
- package/dist/modules/currencies/api/currencies/route.js +4 -3
- package/dist/modules/currencies/api/currencies/route.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles.js +2 -1
- package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
- package/dist/modules/customer_accounts/events.js +1 -1
- package/dist/modules/customer_accounts/events.js.map +1 -1
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +1 -1
- package/dist/modules/customers/acl.js +1 -1
- package/dist/modules/customers/acl.js.map +1 -1
- package/dist/modules/customers/ai-tools/companies-pack.js.map +1 -1
- package/dist/modules/customers/ai-tools/deals-pack.js.map +1 -1
- package/dist/modules/customers/ai-tools/people-pack.js.map +1 -1
- package/dist/modules/customers/api/companies/route.js +4 -4
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +43 -2
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/deals/summary/route.js +402 -0
- package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
- package/dist/modules/customers/api/people/route.js +4 -4
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +221 -56
- package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +18 -0
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/cli.js +15 -9
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +5 -5
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +5 -5
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +2 -2
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/entity-roles.js +2 -1
- package/dist/modules/customers/commands/entity-roles.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +8 -5
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/shared.js +21 -6
- package/dist/modules/customers/commands/shared.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +3 -3
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
- package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +100 -17
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +11 -3
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/assignableStaff.js +21 -8
- package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
- package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
- package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
- package/dist/modules/customers/lib/dealsMetrics.js +82 -0
- package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +1 -1
- package/dist/modules/data_sync/api/run.js +1 -1
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +214 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +7 -0
- package/dist/modules/directory/api/organizations/route.js +7 -0
- package/dist/modules/directory/api/organizations/route.js.map +3 -3
- package/dist/modules/directory/backend/directory/branding/page.js +214 -0
- package/dist/modules/directory/backend/directory/branding/page.js.map +7 -0
- package/dist/modules/directory/backend/directory/branding/page.meta.js +26 -0
- package/dist/modules/directory/backend/directory/branding/page.meta.js.map +7 -0
- package/dist/modules/directory/commands/organizations.js +8 -1
- package/dist/modules/directory/commands/organizations.js.map +2 -2
- package/dist/modules/directory/data/entities.js +3 -0
- package/dist/modules/directory/data/entities.js.map +2 -2
- package/dist/modules/directory/data/validators.js +9 -0
- package/dist/modules/directory/data/validators.js.map +2 -2
- package/dist/modules/directory/migrations/Migration20260607222259_directory.js +13 -0
- package/dist/modules/directory/migrations/Migration20260607222259_directory.js.map +7 -0
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +59 -27
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +2 -1
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/entities.js +7 -0
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +26 -15
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
- package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
- package/dist/modules/payment_gateways/api/transactions/route.js +2 -4
- package/dist/modules/payment_gateways/api/transactions/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +7 -2
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +8 -2
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/data/entities.js +2 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +4 -2
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js +16 -0
- package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js.map +7 -0
- package/dist/modules/resources/api/resources.js +2 -3
- package/dist/modules/resources/api/resources.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +2 -2
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +7 -5
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -1
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/salesDocumentsColumns.js +10 -0
- package/dist/modules/sales/components/documents/salesDocumentsColumns.js.map +7 -0
- package/dist/modules/staff/api/team-members.js +9 -2
- package/dist/modules/staff/api/team-members.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/team-members.js +1 -1
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
- package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
- package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +1 -1
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +3 -2
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/generated/entities/organization/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +11 -12
- package/src/bootstrap.ts +65 -7
- package/src/helpers/integration/crmFixtures.ts +21 -1
- package/src/modules/attachments/AGENTS.md +79 -0
- package/src/modules/attachments/api/library/route.ts +2 -2
- package/src/modules/attachments/api/route.ts +2 -0
- package/src/modules/attachments/components/AttachmentContentPreview.tsx +6 -6
- package/src/modules/attachments/lib/access.ts +36 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +14 -2
- package/src/modules/audit_logs/data/entities.ts +1 -0
- package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +10 -0
- package/src/modules/audit_logs/migrations/Migration20260611104500.ts +13 -0
- package/src/modules/audit_logs/services/accessLogService.ts +15 -0
- package/src/modules/auth/api/admin/nav.ts +9 -0
- package/src/modules/auth/api/login.ts +13 -13
- package/src/modules/auth/commands/users.ts +32 -15
- package/src/modules/auth/data/entities.ts +13 -1
- package/src/modules/auth/i18n/de.json +0 -1
- package/src/modules/auth/i18n/en.json +0 -1
- package/src/modules/auth/i18n/es.json +0 -1
- package/src/modules/auth/i18n/pl.json +0 -1
- package/src/modules/auth/lib/backendChrome.tsx +37 -1
- package/src/modules/auth/lib/consentIntegrity.ts +6 -3
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +20 -10
- package/src/modules/auth/migrations/Migration20260610120000.ts +53 -0
- package/src/modules/auth/migrations/Migration20260611103000.ts +21 -0
- package/src/modules/auth/services/authService.ts +24 -4
- package/src/modules/auth/services/rbacService.ts +11 -2
- package/src/modules/catalog/ai-tools/configuration-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/prices-offers-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/products-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/variants-pack.ts +1 -1
- package/src/modules/communication_channels/data/entities.ts +2 -2
- package/src/modules/communication_channels/encryption.ts +1 -1
- package/src/modules/communication_channels/lib/adapter.ts +1 -1
- package/src/modules/communication_channels/lib/thread-matcher.ts +1 -1
- package/src/modules/communication_channels/lib/thread-token.ts +1 -1
- package/src/modules/currencies/api/currencies/route.ts +4 -3
- package/src/modules/customer_accounts/api/admin/roles.ts +2 -1
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
- package/src/modules/customer_accounts/events.ts +1 -1
- package/src/modules/customer_accounts/lib/resolveTenantContext.ts +2 -2
- package/src/modules/customers/acl.ts +1 -1
- package/src/modules/customers/ai-tools/companies-pack.ts +1 -1
- package/src/modules/customers/ai-tools/deals-pack.ts +1 -1
- package/src/modules/customers/ai-tools/people-pack.ts +1 -1
- package/src/modules/customers/api/companies/route.ts +4 -4
- package/src/modules/customers/api/deals/route.ts +51 -2
- package/src/modules/customers/api/deals/summary/route.ts +496 -0
- package/src/modules/customers/api/people/route.ts +4 -4
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -0
- package/src/modules/customers/cli.ts +15 -15
- package/src/modules/customers/commands/addresses.ts +5 -5
- package/src/modules/customers/commands/comments.ts +5 -5
- package/src/modules/customers/commands/deals.ts +2 -2
- package/src/modules/customers/commands/entity-roles.ts +2 -1
- package/src/modules/customers/commands/interactions.ts +8 -5
- package/src/modules/customers/commands/shared.ts +26 -4
- package/src/modules/customers/commands/tags.ts +3 -3
- package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
- package/src/modules/customers/components/detail/DealForm.tsx +121 -19
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +12 -2
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
- package/src/modules/customers/components/detail/assignableStaff.ts +32 -8
- package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
- package/src/modules/customers/i18n/de.json +43 -0
- package/src/modules/customers/i18n/en.json +43 -0
- package/src/modules/customers/i18n/es.json +43 -0
- package/src/modules/customers/i18n/pl.json +43 -0
- package/src/modules/customers/lib/dealsMetrics.ts +159 -0
- package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +1 -1
- package/src/modules/data_sync/api/run.ts +1 -1
- package/src/modules/directory/api/organization-branding/route.ts +238 -0
- package/src/modules/directory/api/organizations/route.ts +7 -0
- package/src/modules/directory/backend/directory/branding/page.meta.ts +24 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +248 -0
- package/src/modules/directory/commands/organizations.ts +9 -1
- package/src/modules/directory/data/entities.ts +3 -0
- package/src/modules/directory/data/validators.ts +12 -0
- package/src/modules/directory/i18n/de.json +21 -0
- package/src/modules/directory/i18n/en.json +21 -0
- package/src/modules/directory/i18n/es.json +21 -0
- package/src/modules/directory/i18n/pl.json +21 -0
- package/src/modules/directory/migrations/.snapshot-open-mercato.json +40 -0
- package/src/modules/directory/migrations/Migration20260607222259_directory.ts +13 -0
- package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
- package/src/modules/directory/utils/organizationScope.ts +85 -30
- package/src/modules/entities/api/definitions.batch.ts +11 -7
- package/src/modules/entities/api/entities.ts +11 -0
- package/src/modules/entities/api/records.ts +46 -25
- package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
- package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
- package/src/modules/entities/i18n/de.json +1 -0
- package/src/modules/entities/i18n/en.json +1 -0
- package/src/modules/entities/i18n/es.json +1 -0
- package/src/modules/entities/i18n/pl.json +1 -0
- package/src/modules/payment_gateways/api/transactions/route.ts +2 -5
- package/src/modules/progress/api/jobs/[id]/route.ts +6 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/progressServiceImpl.ts +7 -1
- package/src/modules/query_index/data/entities.ts +1 -0
- package/src/modules/query_index/lib/engine.ts +11 -5
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +11 -0
- package/src/modules/query_index/migrations/Migration20260611103000_query_index.ts +29 -0
- package/src/modules/resources/api/resources.ts +2 -3
- package/src/modules/sales/api/documents/factory.ts +2 -2
- package/src/modules/sales/commands/documents.ts +7 -5
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
- package/src/modules/sales/components/documents/salesDocumentsColumns.ts +6 -0
- package/src/modules/staff/AGENTS.md +1 -1
- package/src/modules/staff/api/team-members.ts +9 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
- package/src/modules/staff/commands/team-members.ts +5 -2
- package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
- package/src/modules/sync_excel/api/import/route.ts +1 -1
- package/src/modules/workflows/api/definitions/route.ts +3 -2
- package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
- package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
|
@@ -5,6 +5,27 @@
|
|
|
5
5
|
"directory.audit.tenants.create": "Mandanten anlegen",
|
|
6
6
|
"directory.audit.tenants.delete": "Mandanten löschen",
|
|
7
7
|
"directory.audit.tenants.update": "Mandanten aktualisieren",
|
|
8
|
+
"directory.branding.actions.reset": "Standardlogo verwenden",
|
|
9
|
+
"directory.branding.actions.save": "Branding speichern",
|
|
10
|
+
"directory.branding.currentScope": "Aktuelle Organisation",
|
|
11
|
+
"directory.branding.description": "Legen Sie das Logo fest, das in der Backend-Seitenleiste für die aktuell ausgewählte Organisation verwendet wird.",
|
|
12
|
+
"directory.branding.errors.blocked": "Das Speichern des Brandings wurde blockiert.",
|
|
13
|
+
"directory.branding.errors.invalidLogoUrl": "Geben Sie eine gültige Bild-URL ein.",
|
|
14
|
+
"directory.branding.errors.load": "Organisationsbranding konnte nicht geladen werden",
|
|
15
|
+
"directory.branding.errors.notFound": "Organisation nicht gefunden",
|
|
16
|
+
"directory.branding.errors.organizationRequired": "Wählen Sie eine einzelne Organisation aus, bevor Sie das Seitenleisten-Branding ändern.",
|
|
17
|
+
"directory.branding.errors.save": "Organisationsbranding konnte nicht aktualisiert werden",
|
|
18
|
+
"directory.branding.errors.upload": "Logo konnte nicht hochgeladen werden",
|
|
19
|
+
"directory.branding.file.hint": "PNG, JPG, WebP oder SVG funktioniert am besten. Hochgeladene Dateien werden als Organisationsanhänge gespeichert.",
|
|
20
|
+
"directory.branding.file.label": "Logo hochladen",
|
|
21
|
+
"directory.branding.flash.saved": "Organisationsbranding aktualisiert",
|
|
22
|
+
"directory.branding.loading": "Organisationsbranding wird geladen...",
|
|
23
|
+
"directory.branding.nav": "Organisationsbranding",
|
|
24
|
+
"directory.branding.previewAlt": "Logovorschau für {{name}}",
|
|
25
|
+
"directory.branding.title": "Organisationsbranding",
|
|
26
|
+
"directory.branding.url.hint": "Verwenden Sie eine externe Bild-URL oder lassen Sie das Feld leer, um auf das Standardlogo von Open Mercato zurückzufallen.",
|
|
27
|
+
"directory.branding.url.label": "Logo-URL",
|
|
28
|
+
"directory.branding.url.placeholder": "https://example.com/logo.svg",
|
|
8
29
|
"directory.nav.group": "Verzeichnis",
|
|
9
30
|
"directory.nav.organizations": "Organisationen",
|
|
10
31
|
"directory.nav.organizations.create": "Organisation erstellen",
|
|
@@ -5,6 +5,27 @@
|
|
|
5
5
|
"directory.audit.tenants.create": "Create tenant",
|
|
6
6
|
"directory.audit.tenants.delete": "Delete tenant",
|
|
7
7
|
"directory.audit.tenants.update": "Update tenant",
|
|
8
|
+
"directory.branding.actions.reset": "Use default logo",
|
|
9
|
+
"directory.branding.actions.save": "Save branding",
|
|
10
|
+
"directory.branding.currentScope": "Current organization",
|
|
11
|
+
"directory.branding.description": "Set the logo used in the backend sidebar for the currently selected organization.",
|
|
12
|
+
"directory.branding.errors.blocked": "Branding save was blocked.",
|
|
13
|
+
"directory.branding.errors.invalidLogoUrl": "Enter a valid image URL.",
|
|
14
|
+
"directory.branding.errors.load": "Failed to load organization branding",
|
|
15
|
+
"directory.branding.errors.notFound": "Organization not found",
|
|
16
|
+
"directory.branding.errors.organizationRequired": "Select a single organization before changing sidebar branding.",
|
|
17
|
+
"directory.branding.errors.save": "Failed to update organization branding",
|
|
18
|
+
"directory.branding.errors.upload": "Failed to upload logo",
|
|
19
|
+
"directory.branding.file.hint": "PNG, JPG, WebP, or SVG works best. Uploaded files are stored as organization attachments.",
|
|
20
|
+
"directory.branding.file.label": "Upload logo",
|
|
21
|
+
"directory.branding.flash.saved": "Organization branding updated",
|
|
22
|
+
"directory.branding.loading": "Loading organization branding...",
|
|
23
|
+
"directory.branding.nav": "Organization branding",
|
|
24
|
+
"directory.branding.previewAlt": "{{name}} logo preview",
|
|
25
|
+
"directory.branding.title": "Organization branding",
|
|
26
|
+
"directory.branding.url.hint": "Use an external image URL or leave empty to fall back to the default Open Mercato logo.",
|
|
27
|
+
"directory.branding.url.label": "Logo URL",
|
|
28
|
+
"directory.branding.url.placeholder": "https://example.com/logo.svg",
|
|
8
29
|
"directory.nav.group": "Directory",
|
|
9
30
|
"directory.nav.organizations": "Organizations",
|
|
10
31
|
"directory.nav.organizations.create": "Create Organization",
|
|
@@ -5,6 +5,27 @@
|
|
|
5
5
|
"directory.audit.tenants.create": "Crear inquilino",
|
|
6
6
|
"directory.audit.tenants.delete": "Eliminar inquilino",
|
|
7
7
|
"directory.audit.tenants.update": "Actualizar inquilino",
|
|
8
|
+
"directory.branding.actions.reset": "Usar logo predeterminado",
|
|
9
|
+
"directory.branding.actions.save": "Guardar marca",
|
|
10
|
+
"directory.branding.currentScope": "Organización actual",
|
|
11
|
+
"directory.branding.description": "Define el logo usado en la barra lateral del backend para la organización seleccionada.",
|
|
12
|
+
"directory.branding.errors.blocked": "Se bloqueó el guardado de la marca.",
|
|
13
|
+
"directory.branding.errors.invalidLogoUrl": "Introduce una URL de imagen válida.",
|
|
14
|
+
"directory.branding.errors.load": "No se pudo cargar la marca de la organización",
|
|
15
|
+
"directory.branding.errors.notFound": "Organización no encontrada",
|
|
16
|
+
"directory.branding.errors.organizationRequired": "Selecciona una sola organización antes de cambiar la marca de la barra lateral.",
|
|
17
|
+
"directory.branding.errors.save": "No se pudo actualizar la marca de la organización",
|
|
18
|
+
"directory.branding.errors.upload": "No se pudo subir el logo",
|
|
19
|
+
"directory.branding.file.hint": "PNG, JPG, WebP o SVG funcionan mejor. Los archivos subidos se guardan como adjuntos de la organización.",
|
|
20
|
+
"directory.branding.file.label": "Subir logo",
|
|
21
|
+
"directory.branding.flash.saved": "Marca de la organización actualizada",
|
|
22
|
+
"directory.branding.loading": "Cargando marca de la organización...",
|
|
23
|
+
"directory.branding.nav": "Marca de la organización",
|
|
24
|
+
"directory.branding.previewAlt": "Vista previa del logo de {{name}}",
|
|
25
|
+
"directory.branding.title": "Marca de la organización",
|
|
26
|
+
"directory.branding.url.hint": "Usa una URL de imagen externa o deja el campo vacío para volver al logo predeterminado de Open Mercato.",
|
|
27
|
+
"directory.branding.url.label": "URL del logo",
|
|
28
|
+
"directory.branding.url.placeholder": "https://example.com/logo.svg",
|
|
8
29
|
"directory.nav.group": "Directorio",
|
|
9
30
|
"directory.nav.organizations": "Organizaciones",
|
|
10
31
|
"directory.nav.organizations.create": "Crear organización",
|
|
@@ -5,6 +5,27 @@
|
|
|
5
5
|
"directory.audit.tenants.create": "Utwórz najemcę",
|
|
6
6
|
"directory.audit.tenants.delete": "Usuń najemcę",
|
|
7
7
|
"directory.audit.tenants.update": "Aktualizuj najemcę",
|
|
8
|
+
"directory.branding.actions.reset": "Użyj domyślnego logo",
|
|
9
|
+
"directory.branding.actions.save": "Zapisz branding",
|
|
10
|
+
"directory.branding.currentScope": "Aktualna organizacja",
|
|
11
|
+
"directory.branding.description": "Ustaw logo używane w sidebarze backendu dla aktualnie wybranej organizacji.",
|
|
12
|
+
"directory.branding.errors.blocked": "Zapis brandingu został zablokowany.",
|
|
13
|
+
"directory.branding.errors.invalidLogoUrl": "Podaj poprawny adres URL obrazka.",
|
|
14
|
+
"directory.branding.errors.load": "Nie udało się wczytać brandingu organizacji",
|
|
15
|
+
"directory.branding.errors.notFound": "Nie znaleziono organizacji",
|
|
16
|
+
"directory.branding.errors.organizationRequired": "Wybierz pojedynczą organizację przed zmianą brandingu sidebara.",
|
|
17
|
+
"directory.branding.errors.save": "Nie udało się zaktualizować brandingu organizacji",
|
|
18
|
+
"directory.branding.errors.upload": "Nie udało się przesłać logo",
|
|
19
|
+
"directory.branding.file.hint": "Najlepiej sprawdzi się PNG, JPG, WebP albo SVG. Przesłane pliki są zapisywane jako załączniki organizacji.",
|
|
20
|
+
"directory.branding.file.label": "Prześlij logo",
|
|
21
|
+
"directory.branding.flash.saved": "Branding organizacji zaktualizowany",
|
|
22
|
+
"directory.branding.loading": "Ładowanie brandingu organizacji...",
|
|
23
|
+
"directory.branding.nav": "Branding organizacji",
|
|
24
|
+
"directory.branding.previewAlt": "Podgląd logo {{name}}",
|
|
25
|
+
"directory.branding.title": "Branding organizacji",
|
|
26
|
+
"directory.branding.url.hint": "Użyj zewnętrznego adresu obrazka albo zostaw puste, aby wrócić do domyślnego logo Open Mercato.",
|
|
27
|
+
"directory.branding.url.label": "Adres URL logo",
|
|
28
|
+
"directory.branding.url.placeholder": "https://example.com/logo.svg",
|
|
8
29
|
"directory.nav.group": "Katalog",
|
|
9
30
|
"directory.nav.organizations": "Organizacje",
|
|
10
31
|
"directory.nav.organizations.create": "Utwórz organizację",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"scale": null,
|
|
22
22
|
"default": "'[]'",
|
|
23
23
|
"comment": null,
|
|
24
|
+
"collation": null,
|
|
24
25
|
"enumItems": [],
|
|
25
26
|
"mappedType": "json"
|
|
26
27
|
},
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"scale": null,
|
|
38
39
|
"default": "'[]'",
|
|
39
40
|
"comment": null,
|
|
41
|
+
"collation": null,
|
|
40
42
|
"enumItems": [],
|
|
41
43
|
"mappedType": "json"
|
|
42
44
|
},
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
"scale": null,
|
|
54
56
|
"default": null,
|
|
55
57
|
"comment": null,
|
|
58
|
+
"collation": null,
|
|
56
59
|
"enumItems": [],
|
|
57
60
|
"mappedType": "datetime"
|
|
58
61
|
},
|
|
@@ -69,6 +72,7 @@
|
|
|
69
72
|
"scale": null,
|
|
70
73
|
"default": null,
|
|
71
74
|
"comment": null,
|
|
75
|
+
"collation": null,
|
|
72
76
|
"enumItems": [],
|
|
73
77
|
"mappedType": "datetime"
|
|
74
78
|
},
|
|
@@ -85,6 +89,7 @@
|
|
|
85
89
|
"scale": null,
|
|
86
90
|
"default": "0",
|
|
87
91
|
"comment": null,
|
|
92
|
+
"collation": null,
|
|
88
93
|
"enumItems": [],
|
|
89
94
|
"mappedType": "integer"
|
|
90
95
|
},
|
|
@@ -101,6 +106,7 @@
|
|
|
101
106
|
"scale": null,
|
|
102
107
|
"default": "'[]'",
|
|
103
108
|
"comment": null,
|
|
109
|
+
"collation": null,
|
|
104
110
|
"enumItems": [],
|
|
105
111
|
"mappedType": "json"
|
|
106
112
|
},
|
|
@@ -117,6 +123,7 @@
|
|
|
117
123
|
"scale": null,
|
|
118
124
|
"default": "gen_random_uuid()",
|
|
119
125
|
"comment": null,
|
|
126
|
+
"collation": null,
|
|
120
127
|
"enumItems": [],
|
|
121
128
|
"mappedType": "uuid"
|
|
122
129
|
},
|
|
@@ -133,9 +140,27 @@
|
|
|
133
140
|
"scale": null,
|
|
134
141
|
"default": "true",
|
|
135
142
|
"comment": null,
|
|
143
|
+
"collation": null,
|
|
136
144
|
"enumItems": [],
|
|
137
145
|
"mappedType": "boolean"
|
|
138
146
|
},
|
|
147
|
+
"logo_url": {
|
|
148
|
+
"name": "logo_url",
|
|
149
|
+
"type": "text",
|
|
150
|
+
"unsigned": false,
|
|
151
|
+
"autoincrement": false,
|
|
152
|
+
"primary": false,
|
|
153
|
+
"nullable": true,
|
|
154
|
+
"unique": false,
|
|
155
|
+
"length": null,
|
|
156
|
+
"precision": null,
|
|
157
|
+
"scale": null,
|
|
158
|
+
"default": null,
|
|
159
|
+
"comment": null,
|
|
160
|
+
"collation": null,
|
|
161
|
+
"enumItems": [],
|
|
162
|
+
"mappedType": "text"
|
|
163
|
+
},
|
|
139
164
|
"name": {
|
|
140
165
|
"name": "name",
|
|
141
166
|
"type": "text",
|
|
@@ -149,6 +174,7 @@
|
|
|
149
174
|
"scale": null,
|
|
150
175
|
"default": null,
|
|
151
176
|
"comment": null,
|
|
177
|
+
"collation": null,
|
|
152
178
|
"enumItems": [],
|
|
153
179
|
"mappedType": "text"
|
|
154
180
|
},
|
|
@@ -165,6 +191,7 @@
|
|
|
165
191
|
"scale": null,
|
|
166
192
|
"default": null,
|
|
167
193
|
"comment": null,
|
|
194
|
+
"collation": null,
|
|
168
195
|
"enumItems": [],
|
|
169
196
|
"mappedType": "uuid"
|
|
170
197
|
},
|
|
@@ -181,6 +208,7 @@
|
|
|
181
208
|
"scale": null,
|
|
182
209
|
"default": null,
|
|
183
210
|
"comment": null,
|
|
211
|
+
"collation": null,
|
|
184
212
|
"enumItems": [],
|
|
185
213
|
"mappedType": "uuid"
|
|
186
214
|
},
|
|
@@ -197,6 +225,7 @@
|
|
|
197
225
|
"scale": null,
|
|
198
226
|
"default": null,
|
|
199
227
|
"comment": null,
|
|
228
|
+
"collation": null,
|
|
200
229
|
"enumItems": [],
|
|
201
230
|
"mappedType": "text"
|
|
202
231
|
},
|
|
@@ -213,6 +242,7 @@
|
|
|
213
242
|
"scale": null,
|
|
214
243
|
"default": null,
|
|
215
244
|
"comment": null,
|
|
245
|
+
"collation": null,
|
|
216
246
|
"enumItems": [],
|
|
217
247
|
"mappedType": "uuid"
|
|
218
248
|
},
|
|
@@ -229,6 +259,7 @@
|
|
|
229
259
|
"scale": null,
|
|
230
260
|
"default": null,
|
|
231
261
|
"comment": null,
|
|
262
|
+
"collation": null,
|
|
232
263
|
"enumItems": [],
|
|
233
264
|
"mappedType": "text"
|
|
234
265
|
},
|
|
@@ -245,6 +276,7 @@
|
|
|
245
276
|
"scale": null,
|
|
246
277
|
"default": null,
|
|
247
278
|
"comment": null,
|
|
279
|
+
"collation": null,
|
|
248
280
|
"enumItems": [],
|
|
249
281
|
"mappedType": "datetime"
|
|
250
282
|
}
|
|
@@ -273,6 +305,7 @@
|
|
|
273
305
|
}
|
|
274
306
|
],
|
|
275
307
|
"checks": [],
|
|
308
|
+
"triggers": [],
|
|
276
309
|
"foreignKeys": {
|
|
277
310
|
"organizations_tenant_id_foreign": {
|
|
278
311
|
"columnNames": [
|
|
@@ -305,6 +338,7 @@
|
|
|
305
338
|
"scale": null,
|
|
306
339
|
"default": null,
|
|
307
340
|
"comment": null,
|
|
341
|
+
"collation": null,
|
|
308
342
|
"enumItems": [],
|
|
309
343
|
"mappedType": "datetime"
|
|
310
344
|
},
|
|
@@ -321,6 +355,7 @@
|
|
|
321
355
|
"scale": null,
|
|
322
356
|
"default": null,
|
|
323
357
|
"comment": null,
|
|
358
|
+
"collation": null,
|
|
324
359
|
"enumItems": [],
|
|
325
360
|
"mappedType": "datetime"
|
|
326
361
|
},
|
|
@@ -337,6 +372,7 @@
|
|
|
337
372
|
"scale": null,
|
|
338
373
|
"default": "gen_random_uuid()",
|
|
339
374
|
"comment": null,
|
|
375
|
+
"collation": null,
|
|
340
376
|
"enumItems": [],
|
|
341
377
|
"mappedType": "uuid"
|
|
342
378
|
},
|
|
@@ -353,6 +389,7 @@
|
|
|
353
389
|
"scale": null,
|
|
354
390
|
"default": "true",
|
|
355
391
|
"comment": null,
|
|
392
|
+
"collation": null,
|
|
356
393
|
"enumItems": [],
|
|
357
394
|
"mappedType": "boolean"
|
|
358
395
|
},
|
|
@@ -369,6 +406,7 @@
|
|
|
369
406
|
"scale": null,
|
|
370
407
|
"default": null,
|
|
371
408
|
"comment": null,
|
|
409
|
+
"collation": null,
|
|
372
410
|
"enumItems": [],
|
|
373
411
|
"mappedType": "text"
|
|
374
412
|
},
|
|
@@ -385,6 +423,7 @@
|
|
|
385
423
|
"scale": null,
|
|
386
424
|
"default": null,
|
|
387
425
|
"comment": null,
|
|
426
|
+
"collation": null,
|
|
388
427
|
"enumItems": [],
|
|
389
428
|
"mappedType": "datetime"
|
|
390
429
|
}
|
|
@@ -402,6 +441,7 @@
|
|
|
402
441
|
}
|
|
403
442
|
],
|
|
404
443
|
"checks": [],
|
|
444
|
+
"triggers": [],
|
|
405
445
|
"foreignKeys": {},
|
|
406
446
|
"comment": null
|
|
407
447
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260607222259_directory extends Migration {
|
|
4
|
+
|
|
5
|
+
override up(): void | Promise<void> {
|
|
6
|
+
this.addSql(`alter table "organizations" add "logo_url" text null;`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
override down(): void | Promise<void> {
|
|
10
|
+
this.addSql(`alter table "organizations" drop column "logo_url";`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
// drop every cache entry tagged for that tenant; the TTL is the backstop
|
|
8
8
|
// for races where the event fires after a request reads the cache.
|
|
9
9
|
|
|
10
|
+
import { buildOrgScopeTenantCacheTag } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
11
|
+
|
|
10
12
|
type CacheService = {
|
|
11
13
|
deleteByTags(tags: string[]): Promise<number>
|
|
12
14
|
}
|
|
@@ -32,7 +34,7 @@ export default async function handle(
|
|
|
32
34
|
}
|
|
33
35
|
if (!cache) return
|
|
34
36
|
try {
|
|
35
|
-
await cache.deleteByTags([
|
|
37
|
+
await cache.deleteByTags([buildOrgScopeTenantCacheTag(tenantId)])
|
|
36
38
|
} catch {
|
|
37
39
|
// best-effort; TTL is the backstop.
|
|
38
40
|
}
|
|
@@ -21,9 +21,11 @@ export type OrganizationScope = {
|
|
|
21
21
|
// OrganizationScope is a pure function of (userId, tenantId, selectedOrgId,
|
|
22
22
|
// requestedTenant) between membership changes; caching it bypasses 1
|
|
23
23
|
// SELECT on `organizations` per CRUD request. TTL is short (60s default)
|
|
24
|
-
// to keep staleness bounded
|
|
25
|
-
//
|
|
26
|
-
//
|
|
24
|
+
// to keep staleness bounded as a backstop. Tag-based invalidation also fires
|
|
25
|
+
// eagerly: per-user entries are dropped by RbacService.invalidateUserCache
|
|
26
|
+
// (every ACL/role grant change goes through it — see buildOrgScopeUserCacheTag)
|
|
27
|
+
// and per-tenant entries by the directory.organization.* subscriber plus
|
|
28
|
+
// RbacService.invalidateTenantCache (role-ACL changes).
|
|
27
29
|
const ORG_SCOPE_CACHE_KEY_PREFIX = 'org-scope'
|
|
28
30
|
// Phase 4 default-off until the same readiness probe (`GET /api/customers/people`)
|
|
29
31
|
// stays green with the cache layer engaged. Set `OM_ORG_SCOPE_CACHE_TTL_MS=60000`
|
|
@@ -49,10 +51,23 @@ function buildOrgScopeCacheKey(parts: {
|
|
|
49
51
|
return `${ORG_SCOPE_CACHE_KEY_PREFIX}:${parts.userId}:${parts.effectiveTenantId}:${selected}:${requested}`
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
// Tag builders are exported so the modules that own the "this user's scope
|
|
55
|
+
// changed" / "this tenant's org tree changed" signals (auth RBAC invalidation,
|
|
56
|
+
// the directory.organization.* subscriber) can drop the matching cross-request
|
|
57
|
+
// cache entries without re-deriving the tag format. Keeping the format in one
|
|
58
|
+
// place is what lets the TTL be enabled safely (issue #2259).
|
|
59
|
+
export function buildOrgScopeUserCacheTag(userId: string): string {
|
|
60
|
+
return `${ORG_SCOPE_CACHE_KEY_PREFIX}:user:${userId}`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function buildOrgScopeTenantCacheTag(tenantId: string): string {
|
|
64
|
+
return `${ORG_SCOPE_CACHE_KEY_PREFIX}:tenant:${tenantId}`
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
function buildOrgScopeCacheTags(parts: { userId: string; effectiveTenantId: string }): string[] {
|
|
53
68
|
return [
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
buildOrgScopeUserCacheTag(parts.userId),
|
|
70
|
+
buildOrgScopeTenantCacheTag(parts.effectiveTenantId),
|
|
56
71
|
]
|
|
57
72
|
}
|
|
58
73
|
|
|
@@ -82,7 +97,7 @@ export async function invalidateOrganizationScopeCacheForUser(
|
|
|
82
97
|
const cache = resolveCacheFromContainer(container)
|
|
83
98
|
if (!cache?.deleteByTags) return
|
|
84
99
|
try {
|
|
85
|
-
await cache.deleteByTags([
|
|
100
|
+
await cache.deleteByTags([buildOrgScopeUserCacheTag(userId)])
|
|
86
101
|
} catch (err) {
|
|
87
102
|
console.warn('[org-scope:cache] invalidate user failed', err)
|
|
88
103
|
}
|
|
@@ -95,12 +110,36 @@ export async function invalidateOrganizationScopeCacheForTenant(
|
|
|
95
110
|
const cache = resolveCacheFromContainer(container)
|
|
96
111
|
if (!cache?.deleteByTags) return
|
|
97
112
|
try {
|
|
98
|
-
await cache.deleteByTags([
|
|
113
|
+
await cache.deleteByTags([buildOrgScopeTenantCacheTag(tenantId)])
|
|
99
114
|
} catch (err) {
|
|
100
115
|
console.warn('[org-scope:cache] invalidate tenant failed', err)
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
|
|
119
|
+
// Issue #2259 — per-request memoization. resolveOrganizationScopeForRequest
|
|
120
|
+
// runs at least twice per CRUD request: once for the route-level feature check
|
|
121
|
+
// (resolveFeatureCheckContext) and once inside the shared factory's withCtx.
|
|
122
|
+
// Those two call sites use different request-scoped DI containers but are handed
|
|
123
|
+
// the SAME Request instance, so memoizing the resolved scope on a WeakMap keyed
|
|
124
|
+
// by that request collapses the duplicate work — and the duplicate
|
|
125
|
+
// `organizations` SELECT — into a single resolution. The inner map is keyed by
|
|
126
|
+
// the same identity tuple as the cross-request cache key, so distinct explicit
|
|
127
|
+
// selectedId/tenant overrides on one request stay independent. There is no
|
|
128
|
+
// staleness risk: the memo lives only for the lifetime of one request and is
|
|
129
|
+
// dropped with the request object by the GC.
|
|
130
|
+
const orgScopeRequestMemo = new WeakMap<object, Map<string, Promise<OrganizationScope>>>()
|
|
131
|
+
|
|
132
|
+
function getRequestScopeMemo(request: unknown): Map<string, Promise<OrganizationScope>> | null {
|
|
133
|
+
if (!request || (typeof request !== 'object' && typeof request !== 'function')) return null
|
|
134
|
+
const key = request as object
|
|
135
|
+
let memo = orgScopeRequestMemo.get(key)
|
|
136
|
+
if (!memo) {
|
|
137
|
+
memo = new Map<string, Promise<OrganizationScope>>()
|
|
138
|
+
orgScopeRequestMemo.set(key, memo)
|
|
139
|
+
}
|
|
140
|
+
return memo
|
|
141
|
+
}
|
|
142
|
+
|
|
104
143
|
function normalizeOrganizationId(value: unknown): string | null {
|
|
105
144
|
if (typeof value !== 'string') return null
|
|
106
145
|
const trimmed = value.trim()
|
|
@@ -402,35 +441,51 @@ export async function resolveOrganizationScopeForRequest({
|
|
|
402
441
|
})
|
|
403
442
|
: null
|
|
404
443
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
} catch (err) {
|
|
410
|
-
console.warn('[org-scope:cache] read failed', err)
|
|
411
|
-
}
|
|
444
|
+
const requestMemo = getRequestScopeMemo(request)
|
|
445
|
+
if (requestMemo && cacheKey) {
|
|
446
|
+
const memoized = requestMemo.get(cacheKey)
|
|
447
|
+
if (memoized) return memoized
|
|
412
448
|
}
|
|
413
449
|
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
450
|
+
const resolveScope = async (): Promise<OrganizationScope> => {
|
|
451
|
+
if (cache && cacheKey && typeof cache.get === 'function') {
|
|
452
|
+
try {
|
|
453
|
+
const cached = await cache.get(cacheKey)
|
|
454
|
+
if (isValidCachedScope(cached)) return cached
|
|
455
|
+
} catch (err) {
|
|
456
|
+
console.warn('[org-scope:cache] read failed', err)
|
|
457
|
+
}
|
|
458
|
+
}
|
|
421
459
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
|
|
460
|
+
const baseScope = await resolveOrganizationScope({
|
|
461
|
+
em,
|
|
462
|
+
rbac,
|
|
463
|
+
auth: scopedAuth,
|
|
464
|
+
selectedId: rawSelected,
|
|
465
|
+
tenantId: effectiveTenantId,
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
if (cache && cacheKey && userId && typeof cache.set === 'function') {
|
|
469
|
+
try {
|
|
470
|
+
await cache.set(cacheKey, baseScope, {
|
|
471
|
+
ttl: ttlMs,
|
|
472
|
+
tags: buildOrgScopeCacheTags({ userId, effectiveTenantId }),
|
|
473
|
+
})
|
|
474
|
+
} catch (err) {
|
|
475
|
+
console.warn('[org-scope:cache] write failed', err)
|
|
476
|
+
}
|
|
430
477
|
}
|
|
478
|
+
|
|
479
|
+
return baseScope
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (requestMemo && cacheKey) {
|
|
483
|
+
const pending = resolveScope()
|
|
484
|
+
requestMemo.set(cacheKey, pending)
|
|
485
|
+
return pending
|
|
431
486
|
}
|
|
432
487
|
|
|
433
|
-
return
|
|
488
|
+
return resolveScope()
|
|
434
489
|
}
|
|
435
490
|
|
|
436
491
|
export type FeatureCheckContext = {
|
|
@@ -13,16 +13,20 @@ export const metadata = {
|
|
|
13
13
|
POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
const MAX_DEFINITIONS_PER_BATCH = 1000
|
|
17
|
+
|
|
16
18
|
const batchSchema = z
|
|
17
19
|
.object({
|
|
18
20
|
entityId: z.string().regex(/^[a-z0-9_]+:[a-z0-9_]+$/),
|
|
19
|
-
definitions: z
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
definitions: z
|
|
22
|
+
.array(
|
|
23
|
+
upsertCustomFieldDefSchema
|
|
24
|
+
.omit({ entityId: true })
|
|
25
|
+
.extend({
|
|
26
|
+
configJson: z.any().optional(),
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
.max(MAX_DEFINITIONS_PER_BATCH),
|
|
26
30
|
})
|
|
27
31
|
.extend(customFieldEntityConfigSchema.shape)
|
|
28
32
|
|
|
@@ -7,6 +7,7 @@ import { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'
|
|
|
7
7
|
import { upsertCustomEntitySchema } from '@open-mercato/core/modules/entities/data/validators'
|
|
8
8
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
9
9
|
import { isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'
|
|
10
|
+
import { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'
|
|
10
11
|
|
|
11
12
|
export const metadata = {
|
|
12
13
|
GET: { requireAuth: true },
|
|
@@ -107,6 +108,16 @@ export async function POST(req: Request) {
|
|
|
107
108
|
const { resolve } = await createRequestContainer()
|
|
108
109
|
const em = resolve('em') as any
|
|
109
110
|
|
|
111
|
+
// A registration for a module-declared, table-backed system entity would flip
|
|
112
|
+
// query-engine classification to doc storage for the whole entity type (#2939's
|
|
113
|
+
// failure mode via another door) — refuse to create one.
|
|
114
|
+
if (isOrmBackedSystemEntityId(em, input.entityId)) {
|
|
115
|
+
return NextResponse.json(
|
|
116
|
+
{ error: 'System entities cannot be registered as custom entities', code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId: input.entityId },
|
|
117
|
+
{ status: 400 },
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
const where: any = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }
|
|
111
122
|
let ent = await em.findOne(CustomEntity, where)
|
|
112
123
|
if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: new Date() })
|