@open-mercato/core 0.6.7-develop.6788.1.c2a1520a30 → 0.6.7-develop.6814.1.0627c7e9f1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/customer_deal_person_link/index.js +2 -0
- package/dist/generated/entities/customer_deal_person_link/index.js.map +2 -2
- package/dist/generated/entities/customer_user_invitation/index.js +2 -0
- package/dist/generated/entities/customer_user_invitation/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/business_rules/api/openmercato-call-options/route.js +85 -0
- package/dist/modules/business_rules/api/openmercato-call-options/route.js.map +7 -0
- package/dist/modules/business_rules/api/rules/route.js +71 -4
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/business_rules/components/ActionBuilder.js +47 -4
- package/dist/modules/business_rules/components/ActionBuilder.js.map +3 -3
- package/dist/modules/business_rules/components/ActionRow.js +106 -16
- package/dist/modules/business_rules/components/ActionRow.js.map +2 -2
- package/dist/modules/business_rules/components/utils/actionValidation.js +6 -0
- package/dist/modules/business_rules/components/utils/actionValidation.js.map +2 -2
- package/dist/modules/business_rules/lib/action-executor.js +148 -0
- package/dist/modules/business_rules/lib/action-executor.js.map +2 -2
- package/dist/modules/business_rules/lib/openmercato-call-options-types.js +5 -0
- package/dist/modules/business_rules/lib/openmercato-call-options-types.js.map +7 -0
- package/dist/modules/business_rules/lib/openmercato-call-options.js +231 -0
- package/dist/modules/business_rules/lib/openmercato-call-options.js.map +7 -0
- package/dist/modules/business_rules/lib/rule-engine.js +5 -1
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +11 -1
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +11 -6
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +12 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users/[id].js +10 -1
- package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users-invite.js +61 -4
- package/dist/modules/customer_accounts/api/admin/users-invite.js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/users-invite.js +20 -2
- package/dist/modules/customer_accounts/api/portal/users-invite.js.map +2 -2
- package/dist/modules/customer_accounts/data/entities.js +3 -0
- package/dist/modules/customer_accounts/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/data/validators.js +1 -0
- package/dist/modules/customer_accounts/data/validators.js.map +2 -2
- package/dist/modules/customer_accounts/emails/CustomerInvitationEmail.js +26 -0
- package/dist/modules/customer_accounts/emails/CustomerInvitationEmail.js.map +7 -0
- package/dist/modules/customer_accounts/lib/customerEntityOwnership.js +29 -3
- package/dist/modules/customer_accounts/lib/customerEntityOwnership.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerUrl.js +19 -10
- package/dist/modules/customer_accounts/lib/customerUrl.js.map +2 -2
- package/dist/modules/customer_accounts/lib/invitationEmail.js +35 -0
- package/dist/modules/customer_accounts/lib/invitationEmail.js.map +7 -0
- package/dist/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.js +13 -0
- package/dist/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.js.map +7 -0
- package/dist/modules/customer_accounts/services/customerInvitationService.js +32 -2
- package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +47 -7
- package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection-table.js +1 -1
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +4 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +7 -3
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +11 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +14 -8
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +21 -6
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/cli.js +17 -7
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +48 -10
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/people.js +2 -0
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js +26 -6
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/DealDetailTabs.js +30 -8
- package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +30 -8
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/data/entities.js +8 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +2 -1
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260723210000_customers.js +15 -0
- package/dist/modules/customers/migrations/Migration20260723210000_customers.js.map +7 -0
- package/dist/modules/customers/search.js +50 -29
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +22 -9
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +13 -4
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +7 -2
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +7 -2
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +8 -3
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +7 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/route.js +8 -3
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/runs.js +7 -2
- package/dist/modules/data_sync/api/runs.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +19 -6
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +13 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/api/validate.js +9 -4
- package/dist/modules/data_sync/api/validate.js.map +2 -2
- package/dist/modules/design_system/acl.js +9 -0
- package/dist/modules/design_system/acl.js.map +7 -0
- package/dist/modules/design_system/backend/design-system/page.js +11 -0
- package/dist/modules/design_system/backend/design-system/page.js.map +7 -0
- package/dist/modules/design_system/backend/design-system/page.meta.js +15 -0
- package/dist/modules/design_system/backend/design-system/page.meta.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js +52 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +7 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js +63 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js +163 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js.map +7 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js +9 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/banners.js +186 -0
- package/dist/modules/design_system/gallery/entries/banners.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/buttons.js +361 -0
- package/dist/modules/design_system/gallery/entries/buttons.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/charts.js +457 -0
- package/dist/modules/design_system/gallery/entries/charts.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/dates.js +306 -0
- package/dist/modules/design_system/gallery/entries/dates.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/detail.js +358 -0
- package/dist/modules/design_system/gallery/entries/detail.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/display.js +785 -0
- package/dist/modules/design_system/gallery/entries/display.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/feedback.js +658 -0
- package/dist/modules/design_system/gallery/entries/feedback.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/filters.js +505 -0
- package/dist/modules/design_system/gallery/entries/filters.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/foundations.js +453 -0
- package/dist/modules/design_system/gallery/entries/foundations.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/icons.js +140 -0
- package/dist/modules/design_system/gallery/entries/icons.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/inputs.js +1217 -0
- package/dist/modules/design_system/gallery/entries/inputs.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/messages.js +334 -0
- package/dist/modules/design_system/gallery/entries/messages.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/navigation.js +475 -0
- package/dist/modules/design_system/gallery/entries/navigation.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/notifications.js +257 -0
- package/dist/modules/design_system/gallery/entries/notifications.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/overlays.js +576 -0
- package/dist/modules/design_system/gallery/entries/overlays.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/scaffolding.js +378 -0
- package/dist/modules/design_system/gallery/entries/scaffolding.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/schedule.js +297 -0
- package/dist/modules/design_system/gallery/entries/schedule.js.map +7 -0
- package/dist/modules/design_system/gallery/registry.js +139 -0
- package/dist/modules/design_system/gallery/registry.js.map +7 -0
- package/dist/modules/design_system/gallery/types.js +1 -0
- package/dist/modules/design_system/gallery/types.js.map +7 -0
- package/dist/modules/design_system/index.js +9 -0
- package/dist/modules/design_system/index.js.map +7 -0
- package/dist/modules/design_system/setup.js +14 -0
- package/dist/modules/design_system/setup.js.map +7 -0
- package/dist/modules/entities/api/entity-settings.js +59 -9
- package/dist/modules/entities/api/entity-settings.js.map +2 -2
- package/dist/modules/entities/backend/config/settings/page.js +10 -0
- package/dist/modules/entities/backend/config/settings/page.js.map +7 -0
- package/dist/modules/entities/backend/config/settings/page.meta.js +36 -0
- package/dist/modules/entities/backend/config/settings/page.meta.js.map +7 -0
- package/dist/modules/entities/backend/entities/user/create/page.js +16 -7
- package/dist/modules/entities/backend/entities/user/create/page.js.map +2 -2
- package/dist/modules/entities/components/EntitySettingsManager.js +136 -0
- package/dist/modules/entities/components/EntitySettingsManager.js.map +7 -0
- package/dist/modules/inbox_ops/api/webhook/inbound.js +14 -10
- package/dist/modules/inbox_ops/api/webhook/inbound.js.map +2 -2
- package/dist/modules/inbox_ops/search.js +15 -4
- package/dist/modules/inbox_ops/search.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +11 -5
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/health/route.js +6 -3
- package/dist/modules/integrations/api/[id]/health/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +6 -3
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/state/route.js +6 -3
- package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/version/route.js +6 -3
- package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
- package/dist/modules/integrations/api/logs/route.js +6 -3
- package/dist/modules/integrations/api/logs/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +6 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/api/umes-read.js +2 -2
- package/dist/modules/integrations/api/umes-read.js.map +2 -2
- package/dist/modules/integrations/lib/organization-scope.js +2 -9
- package/dist/modules/integrations/lib/organization-scope.js.map +2 -2
- package/dist/modules/messages/search.js +10 -5
- package/dist/modules/messages/search.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +11 -1
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/data/entities.js +4 -0
- package/dist/modules/payment_gateways/data/entities.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +12 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/payment_gateways/lib/session-idempotency.js +33 -0
- package/dist/modules/payment_gateways/lib/session-idempotency.js.map +2 -2
- package/dist/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.js +18 -0
- package/dist/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.js.map +7 -0
- package/dist/modules/payment_gateways/setup.js +47 -0
- package/dist/modules/payment_gateways/setup.js.map +2 -2
- package/dist/modules/payment_gateways/workers/session-initialization-prune.js +50 -0
- package/dist/modules/payment_gateways/workers/session-initialization-prune.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +134 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.js.map +7 -0
- package/dist/modules/portal/lib/navigation.js +7 -0
- package/dist/modules/portal/lib/navigation.js.map +7 -0
- 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 +75 -50
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +15 -5
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js +17 -0
- package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js.map +7 -0
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +11 -1
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
- package/generated/entities/customer_deal_person_link/index.ts +1 -0
- package/generated/entities/customer_user_invitation/index.ts +1 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +16 -16
- package/src/modules/business_rules/api/openmercato-call-options/route.ts +94 -0
- package/src/modules/business_rules/api/rules/route.ts +92 -4
- package/src/modules/business_rules/components/ActionBuilder.tsx +54 -5
- package/src/modules/business_rules/components/ActionRow.tsx +156 -18
- package/src/modules/business_rules/components/utils/actionValidation.ts +6 -0
- package/src/modules/business_rules/i18n/de.json +12 -0
- package/src/modules/business_rules/i18n/en.json +12 -0
- package/src/modules/business_rules/i18n/es.json +12 -0
- package/src/modules/business_rules/i18n/pl.json +12 -0
- package/src/modules/business_rules/lib/action-executor.ts +198 -0
- package/src/modules/business_rules/lib/openmercato-call-options-types.ts +31 -0
- package/src/modules/business_rules/lib/openmercato-call-options.ts +297 -0
- package/src/modules/business_rules/lib/rule-engine.ts +4 -0
- package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +11 -1
- package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +17 -11
- package/src/modules/configs/lib/upgrade-actions.ts +14 -0
- package/src/modules/customer_accounts/api/admin/users/[id].ts +16 -1
- package/src/modules/customer_accounts/api/admin/users-invite.ts +78 -3
- package/src/modules/customer_accounts/api/portal/users-invite.ts +22 -1
- package/src/modules/customer_accounts/data/entities.ts +3 -0
- package/src/modules/customer_accounts/data/validators.ts +1 -0
- package/src/modules/customer_accounts/emails/CustomerInvitationEmail.tsx +45 -0
- package/src/modules/customer_accounts/i18n/de.json +6 -0
- package/src/modules/customer_accounts/i18n/en.json +6 -0
- package/src/modules/customer_accounts/i18n/es.json +6 -0
- package/src/modules/customer_accounts/i18n/pl.json +6 -0
- package/src/modules/customer_accounts/lib/customerEntityOwnership.ts +68 -2
- package/src/modules/customer_accounts/lib/customerUrl.ts +27 -11
- package/src/modules/customer_accounts/lib/invitationEmail.ts +42 -0
- package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +16 -0
- package/src/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.ts +13 -0
- package/src/modules/customer_accounts/services/customerInvitationService.ts +56 -3
- package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +75 -8
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
- package/src/modules/customer_accounts/widgets/injection-table.ts +4 -1
- package/src/modules/customers/api/deals/[id]/people/route.ts +3 -0
- package/src/modules/customers/api/deals/[id]/route.ts +10 -2
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +12 -1
- package/src/modules/customers/backend/customers/deals/[id]/hooks/types.ts +1 -0
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -9
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +24 -6
- package/src/modules/customers/cli.ts +17 -7
- package/src/modules/customers/commands/deals.ts +50 -9
- package/src/modules/customers/commands/people.ts +3 -0
- package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +30 -7
- package/src/modules/customers/components/detail/DealDetailTabs.tsx +34 -9
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +34 -9
- package/src/modules/customers/data/entities.ts +9 -1
- package/src/modules/customers/data/validators.ts +1 -0
- package/src/modules/customers/i18n/de.json +18 -0
- package/src/modules/customers/i18n/en.json +18 -0
- package/src/modules/customers/i18n/es.json +18 -0
- package/src/modules/customers/i18n/pl.json +18 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +27 -1
- package/src/modules/customers/migrations/Migration20260723210000_customers.ts +15 -0
- package/src/modules/customers/search.ts +59 -27
- package/src/modules/data_sync/AGENTS.md +1 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +22 -9
- package/src/modules/data_sync/api/mappings/route.ts +13 -4
- package/src/modules/data_sync/api/options.ts +7 -2
- package/src/modules/data_sync/api/run.ts +7 -2
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +8 -3
- package/src/modules/data_sync/api/runs/[id]/retry.ts +7 -2
- package/src/modules/data_sync/api/runs/[id]/route.ts +8 -3
- package/src/modules/data_sync/api/runs.ts +7 -2
- package/src/modules/data_sync/api/schedules/[id]/route.ts +19 -6
- package/src/modules/data_sync/api/schedules/route.ts +13 -4
- package/src/modules/data_sync/api/validate.ts +9 -4
- package/src/modules/design_system/AGENTS.md +34 -0
- package/src/modules/design_system/acl.ts +5 -0
- package/src/modules/design_system/backend/design-system/page.meta.ts +11 -0
- package/src/modules/design_system/backend/design-system/page.tsx +14 -0
- package/src/modules/design_system/gallery/components/CodeSnippet.tsx +55 -0
- package/src/modules/design_system/gallery/components/EntryCard.tsx +95 -0
- package/src/modules/design_system/gallery/components/GalleryShell.tsx +206 -0
- package/src/modules/design_system/gallery/components/VariantPreview.tsx +15 -0
- package/src/modules/design_system/gallery/entries/banners.tsx +226 -0
- package/src/modules/design_system/gallery/entries/buttons.tsx +422 -0
- package/src/modules/design_system/gallery/entries/charts.tsx +518 -0
- package/src/modules/design_system/gallery/entries/dates.tsx +325 -0
- package/src/modules/design_system/gallery/entries/detail.tsx +403 -0
- package/src/modules/design_system/gallery/entries/display.tsx +893 -0
- package/src/modules/design_system/gallery/entries/feedback.tsx +746 -0
- package/src/modules/design_system/gallery/entries/filters.tsx +570 -0
- package/src/modules/design_system/gallery/entries/foundations.tsx +529 -0
- package/src/modules/design_system/gallery/entries/icons.tsx +168 -0
- package/src/modules/design_system/gallery/entries/inputs.tsx +1516 -0
- package/src/modules/design_system/gallery/entries/messages.tsx +339 -0
- package/src/modules/design_system/gallery/entries/navigation.tsx +573 -0
- package/src/modules/design_system/gallery/entries/notifications.tsx +285 -0
- package/src/modules/design_system/gallery/entries/overlays.tsx +677 -0
- package/src/modules/design_system/gallery/entries/scaffolding.tsx +430 -0
- package/src/modules/design_system/gallery/entries/schedule.tsx +307 -0
- package/src/modules/design_system/gallery/registry.ts +152 -0
- package/src/modules/design_system/gallery/types.ts +32 -0
- package/src/modules/design_system/i18n/.hardcoded-allowlist.json +71 -0
- package/src/modules/design_system/i18n/de.json +46 -0
- package/src/modules/design_system/i18n/en.json +46 -0
- package/src/modules/design_system/i18n/es.json +46 -0
- package/src/modules/design_system/i18n/pl.json +46 -0
- package/src/modules/design_system/index.ts +7 -0
- package/src/modules/design_system/setup.ts +12 -0
- package/src/modules/entities/api/entity-settings.ts +66 -22
- package/src/modules/entities/backend/config/settings/page.meta.ts +34 -0
- package/src/modules/entities/backend/config/settings/page.tsx +12 -0
- package/src/modules/entities/backend/entities/user/create/page.tsx +16 -7
- package/src/modules/entities/components/EntitySettingsManager.tsx +160 -0
- package/src/modules/entities/i18n/de.json +9 -0
- package/src/modules/entities/i18n/en.json +9 -0
- package/src/modules/entities/i18n/es.json +9 -0
- package/src/modules/entities/i18n/pl.json +9 -0
- package/src/modules/inbox_ops/api/webhook/inbound.ts +14 -12
- package/src/modules/inbox_ops/i18n/de.json +3 -0
- package/src/modules/inbox_ops/i18n/en.json +3 -0
- package/src/modules/inbox_ops/i18n/es.json +3 -0
- package/src/modules/inbox_ops/i18n/pl.json +3 -0
- package/src/modules/inbox_ops/search.ts +20 -4
- package/src/modules/integrations/api/[id]/credentials/route.ts +11 -5
- package/src/modules/integrations/api/[id]/health/route.ts +6 -3
- package/src/modules/integrations/api/[id]/route.ts +6 -3
- package/src/modules/integrations/api/[id]/state/route.ts +6 -3
- package/src/modules/integrations/api/[id]/version/route.ts +6 -3
- package/src/modules/integrations/api/logs/route.ts +6 -3
- package/src/modules/integrations/api/route.ts +6 -3
- package/src/modules/integrations/api/umes-read.ts +2 -2
- package/src/modules/integrations/lib/organization-scope.ts +5 -25
- package/src/modules/messages/i18n/de.json +2 -0
- package/src/modules/messages/i18n/en.json +2 -0
- package/src/modules/messages/i18n/es.json +2 -0
- package/src/modules/messages/i18n/pl.json +2 -0
- package/src/modules/messages/search.ts +11 -5
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +13 -1
- package/src/modules/payment_gateways/data/entities.ts +5 -0
- package/src/modules/payment_gateways/i18n/de.json +5 -1
- package/src/modules/payment_gateways/i18n/en.json +5 -1
- package/src/modules/payment_gateways/i18n/es.json +5 -1
- package/src/modules/payment_gateways/i18n/pl.json +5 -1
- package/src/modules/payment_gateways/lib/gateway-service.ts +12 -2
- package/src/modules/payment_gateways/lib/session-idempotency.ts +36 -0
- package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +10 -1
- package/src/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.ts +17 -0
- package/src/modules/payment_gateways/setup.ts +62 -0
- package/src/modules/payment_gateways/workers/session-initialization-prune.ts +63 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +164 -0
- package/src/modules/portal/i18n/de.json +18 -0
- package/src/modules/portal/i18n/en.json +18 -0
- package/src/modules/portal/i18n/es.json +18 -0
- package/src/modules/portal/i18n/pl.json +18 -0
- package/src/modules/portal/lib/navigation.ts +3 -0
- package/src/modules/query_index/data/entities.ts +7 -0
- package/src/modules/query_index/lib/engine.ts +92 -63
- package/src/modules/query_index/lib/search-tokens.ts +16 -4
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +12 -0
- package/src/modules/query_index/migrations/Migration20260731105052_query_index.ts +35 -0
- package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +11 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"design_system.entries.button.description": "Disparador de acción principal con toda la escala de variantes y tamaños del DS.",
|
|
3
|
+
"design_system.entries.buttonGroup.description": "Grupo unido de botones de acción relacionados que comparten un borde.",
|
|
4
|
+
"design_system.entries.fancyButton.description": "Botón con degradado, de estilo promocional, para acciones destacadas.",
|
|
5
|
+
"design_system.entries.iconButton.description": "Botón cuadrado solo con icono; siempre requiere un aria-label.",
|
|
6
|
+
"design_system.entries.linkButton.description": "Enlace de texto con estilo de control del DS.",
|
|
7
|
+
"design_system.entries.socialButton.description": "Botón de autenticación con colores de marca para inicio de sesión social.",
|
|
8
|
+
"design_system.families.banners": "Avisos y llamadas",
|
|
9
|
+
"design_system.families.buttons": "Botones",
|
|
10
|
+
"design_system.families.charts": "Gráficos",
|
|
11
|
+
"design_system.families.dates": "Fechas y hora",
|
|
12
|
+
"design_system.families.detail": "Secciones de detalle",
|
|
13
|
+
"design_system.families.display": "Visualización de datos",
|
|
14
|
+
"design_system.families.feedback": "Retroalimentación",
|
|
15
|
+
"design_system.families.filters": "Filtros",
|
|
16
|
+
"design_system.families.foundations": "Fundamentos",
|
|
17
|
+
"design_system.families.icons": "Iconos",
|
|
18
|
+
"design_system.families.inputs": "Campos de formulario",
|
|
19
|
+
"design_system.families.messages": "Mensajes",
|
|
20
|
+
"design_system.families.navigation": "Navegación",
|
|
21
|
+
"design_system.families.notifications": "Notificaciones",
|
|
22
|
+
"design_system.families.overlays": "Superposiciones",
|
|
23
|
+
"design_system.families.scaffolding": "Estructura de página",
|
|
24
|
+
"design_system.families.schedule": "Agenda",
|
|
25
|
+
"design_system.gallery.copied": "Fragmento copiado al portapapeles",
|
|
26
|
+
"design_system.gallery.copy": "Copiar código",
|
|
27
|
+
"design_system.gallery.copyFailed": "No se pudo copiar el fragmento",
|
|
28
|
+
"design_system.gallery.familiesGroup": "Familias",
|
|
29
|
+
"design_system.gallery.familyContents": "Componentes de esta familia",
|
|
30
|
+
"design_system.gallery.iconCopied": "Copiado: {snippet}",
|
|
31
|
+
"design_system.gallery.iconCopyJsx": "Copiar JSX (lucide-react)",
|
|
32
|
+
"design_system.gallery.iconCopyMeta": "Copiar nombre para page.meta icon",
|
|
33
|
+
"design_system.gallery.iconCountAll": "{total} iconos",
|
|
34
|
+
"design_system.gallery.iconCountFiltered": "{visible} de {total} iconos",
|
|
35
|
+
"design_system.gallery.iconSearchPlaceholder": "Filtrar iconos…",
|
|
36
|
+
"design_system.gallery.loadFailed": "No se pudo cargar esta familia",
|
|
37
|
+
"design_system.gallery.noResults": "Ningún componente coincide con la búsqueda",
|
|
38
|
+
"design_system.gallery.openInFigma": "Abrir en Figma",
|
|
39
|
+
"design_system.gallery.retry": "Reintentar",
|
|
40
|
+
"design_system.gallery.searchPlaceholder": "Buscar componentes…",
|
|
41
|
+
"design_system.gallery.usageDo": "Haz",
|
|
42
|
+
"design_system.gallery.usageDont": "Evita",
|
|
43
|
+
"design_system.gallery.viewDocs": "Ver documentación",
|
|
44
|
+
"design_system.nav.group": "Desarrollador",
|
|
45
|
+
"design_system.nav.title": "Sistema de diseño"
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"design_system.entries.button.description": "Podstawowy przycisk akcji z pełną skalą wariantów i rozmiarów DS.",
|
|
3
|
+
"design_system.entries.buttonGroup.description": "Połączona grupa powiązanych przycisków akcji ze wspólnym obramowaniem.",
|
|
4
|
+
"design_system.entries.fancyButton.description": "Gradientowy przycisk o marketingowym charakterze dla wyróżnionych akcji.",
|
|
5
|
+
"design_system.entries.iconButton.description": "Kwadratowy przycisk z samą ikoną; zawsze wymaga aria-label.",
|
|
6
|
+
"design_system.entries.linkButton.description": "Tekstowy odnośnik w stylu kontrolki DS.",
|
|
7
|
+
"design_system.entries.socialButton.description": "Przycisk logowania w kolorach marki dla logowania społecznościowego.",
|
|
8
|
+
"design_system.families.banners": "Banery i wskazówki",
|
|
9
|
+
"design_system.families.buttons": "Przyciski",
|
|
10
|
+
"design_system.families.charts": "Wykresy",
|
|
11
|
+
"design_system.families.dates": "Daty i czas",
|
|
12
|
+
"design_system.families.detail": "Sekcje szczegółów",
|
|
13
|
+
"design_system.families.display": "Prezentacja danych",
|
|
14
|
+
"design_system.families.feedback": "Informacje zwrotne",
|
|
15
|
+
"design_system.families.filters": "Filtry",
|
|
16
|
+
"design_system.families.foundations": "Fundamenty",
|
|
17
|
+
"design_system.families.icons": "Ikony",
|
|
18
|
+
"design_system.families.inputs": "Pola formularzy",
|
|
19
|
+
"design_system.families.messages": "Wiadomości",
|
|
20
|
+
"design_system.families.navigation": "Nawigacja",
|
|
21
|
+
"design_system.families.notifications": "Powiadomienia",
|
|
22
|
+
"design_system.families.overlays": "Nakładki",
|
|
23
|
+
"design_system.families.scaffolding": "Szkielet strony",
|
|
24
|
+
"design_system.families.schedule": "Harmonogram",
|
|
25
|
+
"design_system.gallery.copied": "Fragment skopiowany do schowka",
|
|
26
|
+
"design_system.gallery.copy": "Kopiuj kod",
|
|
27
|
+
"design_system.gallery.copyFailed": "Nie udało się skopiować fragmentu",
|
|
28
|
+
"design_system.gallery.familiesGroup": "Rodziny",
|
|
29
|
+
"design_system.gallery.familyContents": "Komponenty w tej rodzinie",
|
|
30
|
+
"design_system.gallery.iconCopied": "Skopiowano: {snippet}",
|
|
31
|
+
"design_system.gallery.iconCopyJsx": "Kopiuj JSX (lucide-react)",
|
|
32
|
+
"design_system.gallery.iconCopyMeta": "Kopiuj nazwę do page.meta icon",
|
|
33
|
+
"design_system.gallery.iconCountAll": "Ikony: {total}",
|
|
34
|
+
"design_system.gallery.iconCountFiltered": "{visible} z {total} ikon",
|
|
35
|
+
"design_system.gallery.iconSearchPlaceholder": "Filtruj ikony…",
|
|
36
|
+
"design_system.gallery.loadFailed": "Nie udało się załadować tej rodziny",
|
|
37
|
+
"design_system.gallery.noResults": "Żaden komponent nie pasuje do wyszukiwania",
|
|
38
|
+
"design_system.gallery.openInFigma": "Otwórz w Figmie",
|
|
39
|
+
"design_system.gallery.retry": "Spróbuj ponownie",
|
|
40
|
+
"design_system.gallery.searchPlaceholder": "Szukaj komponentów…",
|
|
41
|
+
"design_system.gallery.usageDo": "Rób",
|
|
42
|
+
"design_system.gallery.usageDont": "Unikaj",
|
|
43
|
+
"design_system.gallery.viewDocs": "Zobacz dokumentację",
|
|
44
|
+
"design_system.nav.group": "Deweloper",
|
|
45
|
+
"design_system.nav.title": "Design system"
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
|
|
2
|
+
|
|
3
|
+
export const setup: ModuleSetupConfig = {
|
|
4
|
+
defaultRoleFeatures: {
|
|
5
|
+
// View-only module: the gallery reads no tenant data, so the grant is safe
|
|
6
|
+
// to hand out broadly. Superadmin is covered by wildcard grants.
|
|
7
|
+
admin: ['design_system.view'],
|
|
8
|
+
employee: ['design_system.view'],
|
|
9
|
+
},
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default setup
|
|
@@ -3,6 +3,10 @@ import { z } from 'zod'
|
|
|
3
3
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
4
4
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
5
5
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
6
|
+
import { beginEntitiesMutationGuard } from './definitions.mutation-guard'
|
|
7
|
+
import { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
8
|
+
import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
9
|
+
import type { ModuleConfigService } from '../../configs/lib/module-config-service'
|
|
6
10
|
|
|
7
11
|
// Tenant-scoped policy: when true, new custom entities are created with
|
|
8
12
|
// `access_restricted = true` unless the create request explicitly sets the flag.
|
|
@@ -14,24 +18,10 @@ export const metadata = {
|
|
|
14
18
|
PUT: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
type ModuleConfigServiceLike = {
|
|
18
|
-
getValue: (
|
|
19
|
-
moduleId: string,
|
|
20
|
-
name: string,
|
|
21
|
-
options?: { defaultValue?: unknown; scope?: { tenantId?: string | null } },
|
|
22
|
-
) => Promise<unknown>
|
|
23
|
-
setValue: (
|
|
24
|
-
moduleId: string,
|
|
25
|
-
name: string,
|
|
26
|
-
value: unknown,
|
|
27
|
-
scope?: { tenantId?: string | null },
|
|
28
|
-
) => Promise<unknown>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
21
|
async function readPolicy(tenantId: string | null): Promise<boolean> {
|
|
32
22
|
try {
|
|
33
23
|
const { resolve } = await createRequestContainer()
|
|
34
|
-
const moduleConfigService = resolve('moduleConfigService') as
|
|
24
|
+
const moduleConfigService = resolve('moduleConfigService') as ModuleConfigService
|
|
35
25
|
const value = await moduleConfigService.getValue(CONFIG_MODULE, NEW_ENTITIES_RESTRICTED_KEY, {
|
|
36
26
|
defaultValue: false,
|
|
37
27
|
scope: { tenantId },
|
|
@@ -45,12 +35,20 @@ async function readPolicy(tenantId: string | null): Promise<boolean> {
|
|
|
45
35
|
export async function GET(req: Request) {
|
|
46
36
|
const auth = await getAuthFromRequest(req)
|
|
47
37
|
if (!auth || !auth.tenantId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
48
|
-
const
|
|
49
|
-
|
|
38
|
+
const { resolve } = await createRequestContainer()
|
|
39
|
+
const moduleConfigService = resolve('moduleConfigService') as ModuleConfigService
|
|
40
|
+
const record = await moduleConfigService.getRecord(CONFIG_MODULE, NEW_ENTITIES_RESTRICTED_KEY, {
|
|
41
|
+
tenantId: auth.tenantId ?? null,
|
|
42
|
+
})
|
|
43
|
+
return NextResponse.json({
|
|
44
|
+
newEntitiesRestrictedByDefault: record ? record.value === true : false,
|
|
45
|
+
updatedAt: record ? record.updatedAt : null,
|
|
46
|
+
})
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
const putBodySchema = z.object({
|
|
53
50
|
newEntitiesRestrictedByDefault: z.boolean(),
|
|
51
|
+
expectedUpdatedAt: z.string().optional(),
|
|
54
52
|
})
|
|
55
53
|
|
|
56
54
|
export async function PUT(req: Request) {
|
|
@@ -62,19 +60,64 @@ export async function PUT(req: Request) {
|
|
|
62
60
|
if (!parsed.success) {
|
|
63
61
|
return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })
|
|
64
62
|
}
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
63
|
+
const container = await createRequestContainer()
|
|
64
|
+
const { resolve } = container
|
|
65
|
+
const moduleConfigService = resolve('moduleConfigService') as ModuleConfigService
|
|
66
|
+
|
|
67
|
+
const currentRecord = await moduleConfigService.getRecord(
|
|
68
|
+
CONFIG_MODULE,
|
|
69
|
+
NEW_ENTITIES_RESTRICTED_KEY,
|
|
70
|
+
{ tenantId: auth.tenantId ?? null },
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const guard = await beginEntitiesMutationGuard({
|
|
74
|
+
container,
|
|
75
|
+
auth,
|
|
76
|
+
req,
|
|
77
|
+
resourceKind: 'entities.settings',
|
|
78
|
+
resourceId: NEW_ENTITIES_RESTRICTED_KEY,
|
|
79
|
+
operation: currentRecord ? 'update' : 'create',
|
|
80
|
+
mutationPayload: parsed.data as unknown as Record<string, unknown>,
|
|
81
|
+
})
|
|
82
|
+
if (guard.blockedResponse) return guard.blockedResponse
|
|
83
|
+
|
|
84
|
+
const ifMatchHeader = req.headers.get('If-Match') || req.headers.get('x-om-ext-optimistic-lock-expected-updated-at')
|
|
85
|
+
const expectedUpdatedAt = ifMatchHeader || parsed.data.expectedUpdatedAt
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await enforceCommandOptimisticLockWithGuards(container, {
|
|
89
|
+
resourceKind: 'entities.settings',
|
|
90
|
+
resourceId: NEW_ENTITIES_RESTRICTED_KEY,
|
|
91
|
+
current: currentRecord ? currentRecord.updatedAt : null,
|
|
92
|
+
expected: expectedUpdatedAt,
|
|
93
|
+
request: req,
|
|
94
|
+
})
|
|
95
|
+
} catch (lockError) {
|
|
96
|
+
if (isCrudHttpError(lockError)) {
|
|
97
|
+
return NextResponse.json(lockError.body, { status: lockError.status })
|
|
98
|
+
}
|
|
99
|
+
throw lockError
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const updatedRecord = await moduleConfigService.setValue(
|
|
68
103
|
CONFIG_MODULE,
|
|
69
104
|
NEW_ENTITIES_RESTRICTED_KEY,
|
|
70
105
|
parsed.data.newEntitiesRestrictedByDefault,
|
|
71
106
|
{ tenantId: auth.tenantId ?? null },
|
|
72
107
|
)
|
|
73
|
-
|
|
108
|
+
|
|
109
|
+
await guard.runAfterSuccess()
|
|
110
|
+
|
|
111
|
+
return NextResponse.json({
|
|
112
|
+
ok: true,
|
|
113
|
+
newEntitiesRestrictedByDefault: parsed.data.newEntitiesRestrictedByDefault,
|
|
114
|
+
updatedAt: updatedRecord ? updatedRecord.updatedAt : null,
|
|
115
|
+
})
|
|
74
116
|
}
|
|
75
117
|
|
|
76
118
|
const settingsResponseSchema = z.object({
|
|
77
119
|
newEntitiesRestrictedByDefault: z.boolean(),
|
|
120
|
+
updatedAt: z.string().nullable().optional(),
|
|
78
121
|
})
|
|
79
122
|
|
|
80
123
|
export const openApi: OpenApiRouteDoc = {
|
|
@@ -94,9 +137,10 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
94
137
|
description: 'Sets the tenant-scoped default-restricted policy for new custom entities.',
|
|
95
138
|
requestBody: { schema: putBodySchema },
|
|
96
139
|
responses: [
|
|
97
|
-
{ status: 200, description: 'Updated settings', schema: z.object({ ok: z.boolean(), newEntitiesRestrictedByDefault: z.boolean() }) },
|
|
140
|
+
{ status: 200, description: 'Updated settings', schema: z.object({ ok: z.boolean(), newEntitiesRestrictedByDefault: z.boolean(), updatedAt: z.string().nullable().optional() }) },
|
|
98
141
|
{ status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },
|
|
99
142
|
{ status: 401, description: 'Missing authentication', schema: z.object({ error: z.string() }) },
|
|
143
|
+
{ status: 409, description: 'Optimistic lock conflict', schema: z.object({ code: z.string(), error: z.string() }) },
|
|
100
144
|
],
|
|
101
145
|
},
|
|
102
146
|
},
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const settingsIcon = React.createElement(
|
|
4
|
+
'svg',
|
|
5
|
+
{
|
|
6
|
+
width: 16,
|
|
7
|
+
height: 16,
|
|
8
|
+
viewBox: '0 0 24 24',
|
|
9
|
+
fill: 'none',
|
|
10
|
+
stroke: 'currentColor',
|
|
11
|
+
strokeWidth: 2,
|
|
12
|
+
strokeLinecap: 'round',
|
|
13
|
+
strokeLinejoin: 'round',
|
|
14
|
+
},
|
|
15
|
+
React.createElement('circle', { cx: 12, cy: 12, r: 3 }),
|
|
16
|
+
React.createElement('path', {
|
|
17
|
+
d: 'M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z'
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export const metadata = {
|
|
22
|
+
requireAuth: true,
|
|
23
|
+
requireFeatures: ['entities.definitions.manage'],
|
|
24
|
+
pageTitle: 'Custom Entities',
|
|
25
|
+
pageTitleKey: 'entities.settings.title',
|
|
26
|
+
pageGroup: 'Module Configs',
|
|
27
|
+
pageGroupKey: 'settings.sections.moduleConfigs',
|
|
28
|
+
pageOrder: 5,
|
|
29
|
+
icon: settingsIcon,
|
|
30
|
+
pageContext: 'settings' as const,
|
|
31
|
+
breadcrumb: [
|
|
32
|
+
{ label: 'Custom Entities', labelKey: 'entities.settings.title' },
|
|
33
|
+
],
|
|
34
|
+
} as const
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
2
|
+
import { EntitySettingsManager } from '../../../components/EntitySettingsManager'
|
|
3
|
+
|
|
4
|
+
export default function EntitySettingsPage() {
|
|
5
|
+
return (
|
|
6
|
+
<Page>
|
|
7
|
+
<PageBody className="space-y-8">
|
|
8
|
+
<EntitySettingsManager />
|
|
9
|
+
</PageBody>
|
|
10
|
+
</Page>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -10,6 +10,7 @@ import { useRouter } from 'next/navigation'
|
|
|
10
10
|
import { pushWithFlash } from '@open-mercato/ui/backend/utils/flash'
|
|
11
11
|
import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
12
12
|
import { LoadingMessage } from '@open-mercato/ui/backend/detail'
|
|
13
|
+
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
13
14
|
|
|
14
15
|
const schema = upsertCustomEntitySchema
|
|
15
16
|
|
|
@@ -24,15 +25,23 @@ export default function CreateEntityPage() {
|
|
|
24
25
|
React.useEffect(() => {
|
|
25
26
|
let active = true
|
|
26
27
|
;(async () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
try {
|
|
29
|
+
const data = await readApiResultOrThrow<{ newEntitiesRestrictedByDefault?: boolean }>(
|
|
30
|
+
'/api/entities/entity-settings',
|
|
31
|
+
undefined,
|
|
32
|
+
{
|
|
33
|
+
errorMessage: t('entities.userEntities.errors.settingsLoadFailed', 'Could not load default restriction policy; using default'),
|
|
34
|
+
fallback: { newEntitiesRestrictedByDefault: false },
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
if (active) setDefaultRestricted(data?.newEntitiesRestrictedByDefault === true)
|
|
38
|
+
} catch {
|
|
39
|
+
flash(t('entities.userEntities.errors.settingsLoadFailed', 'Could not load default restriction policy; using default'), 'warning')
|
|
40
|
+
if (active) setDefaultRestricted(false)
|
|
41
|
+
}
|
|
33
42
|
})()
|
|
34
43
|
return () => { active = false }
|
|
35
|
-
}, [])
|
|
44
|
+
}, [t])
|
|
36
45
|
const fields = React.useMemo<CrudField[]>(() => ([
|
|
37
46
|
{
|
|
38
47
|
id: 'entityId',
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
5
|
+
import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
|
|
6
|
+
import { Label } from '@open-mercato/ui/primitives/label'
|
|
7
|
+
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
8
|
+
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
9
|
+
import { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
10
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
11
|
+
import { ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
12
|
+
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
13
|
+
import { buildOptimisticLockHeader, extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
14
|
+
import { withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
15
|
+
|
|
16
|
+
export function EntitySettingsManager(): React.ReactElement {
|
|
17
|
+
const t = useT()
|
|
18
|
+
const [loading, setLoading] = React.useState(true)
|
|
19
|
+
const [loadError, setLoadError] = React.useState(false)
|
|
20
|
+
const [saving, setSaving] = React.useState(false)
|
|
21
|
+
const [restricted, setRestricted] = React.useState(false)
|
|
22
|
+
const [updatedAt, setUpdatedAt] = React.useState<string | null>(null)
|
|
23
|
+
|
|
24
|
+
const { runMutation } = useGuardedMutation({
|
|
25
|
+
contextId: 'custom-entities-settings',
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const loadSettings = React.useCallback(async () => {
|
|
29
|
+
setLoading(true)
|
|
30
|
+
setLoadError(false)
|
|
31
|
+
try {
|
|
32
|
+
const data = await readApiResultOrThrow<{ newEntitiesRestrictedByDefault?: boolean; updatedAt?: string | null }>(
|
|
33
|
+
'/api/entities/entity-settings',
|
|
34
|
+
undefined,
|
|
35
|
+
{
|
|
36
|
+
errorMessage: t('entities.settings.errors.loadFailed', 'Failed to load settings'),
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
setRestricted(data.newEntitiesRestrictedByDefault === true)
|
|
40
|
+
setUpdatedAt(data.updatedAt ?? null)
|
|
41
|
+
} catch {
|
|
42
|
+
setLoadError(true)
|
|
43
|
+
} finally {
|
|
44
|
+
setLoading(false)
|
|
45
|
+
}
|
|
46
|
+
}, [t])
|
|
47
|
+
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
void loadSettings()
|
|
50
|
+
}, [loadSettings])
|
|
51
|
+
|
|
52
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
53
|
+
e.preventDefault()
|
|
54
|
+
setSaving(true)
|
|
55
|
+
try {
|
|
56
|
+
const headers = buildOptimisticLockHeader(updatedAt)
|
|
57
|
+
await runMutation({
|
|
58
|
+
operation: () =>
|
|
59
|
+
withScopedApiRequestHeaders(headers, async () => {
|
|
60
|
+
const res = await apiCallOrThrow<{ ok: boolean; newEntitiesRestrictedByDefault: boolean; updatedAt: string | null }>(
|
|
61
|
+
'/api/entities/entity-settings',
|
|
62
|
+
{
|
|
63
|
+
method: 'PUT',
|
|
64
|
+
headers: { 'content-type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
newEntitiesRestrictedByDefault: restricted,
|
|
67
|
+
expectedUpdatedAt: updatedAt ?? undefined,
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
errorMessage: t('entities.settings.errors.saveFailed', 'Failed to save settings'),
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if (res.ok && res.result) {
|
|
76
|
+
setUpdatedAt(res.result.updatedAt)
|
|
77
|
+
flash(t('entities.settings.flash.saved', 'Settings saved'), 'success')
|
|
78
|
+
}
|
|
79
|
+
return res
|
|
80
|
+
}),
|
|
81
|
+
context: {},
|
|
82
|
+
mutationPayload: {
|
|
83
|
+
newEntitiesRestrictedByDefault: restricted,
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
} catch (err) {
|
|
87
|
+
const conflict = extractOptimisticLockConflict(err)
|
|
88
|
+
if (conflict) {
|
|
89
|
+
setLoading(true)
|
|
90
|
+
await loadSettings()
|
|
91
|
+
} else {
|
|
92
|
+
flash(t('entities.settings.errors.saveFailed', 'Failed to save settings'), 'error')
|
|
93
|
+
}
|
|
94
|
+
} finally {
|
|
95
|
+
setSaving(false)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (loading) {
|
|
100
|
+
return (
|
|
101
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
102
|
+
<Spinner className="h-4 w-4" />
|
|
103
|
+
<span>{t('entities.userEntities.form.loading', 'Loading…')}</span>
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (loadError) {
|
|
109
|
+
return (
|
|
110
|
+
<ErrorMessage
|
|
111
|
+
label={t('entities.settings.errors.loadFailed', 'Failed to load settings')}
|
|
112
|
+
action={(
|
|
113
|
+
<Button type="button" variant="outline" size="sm" onClick={() => void loadSettings()}>
|
|
114
|
+
{t('common.retry', 'Retry')}
|
|
115
|
+
</Button>
|
|
116
|
+
)}
|
|
117
|
+
/>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<form onSubmit={(e) => void handleSubmit(e)} className="space-y-6 max-w-2xl">
|
|
123
|
+
<div className="space-y-1">
|
|
124
|
+
<h3 className="text-base font-semibold">
|
|
125
|
+
{t('entities.settings.title', 'Custom Entities')}
|
|
126
|
+
</h3>
|
|
127
|
+
<p className="text-sm text-muted-foreground">
|
|
128
|
+
{t('entities.settings.description', 'Manage global custom entity configurations.')}
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div className="rounded-lg border bg-card p-4 space-y-4">
|
|
133
|
+
<div className="flex items-start gap-3">
|
|
134
|
+
<Checkbox
|
|
135
|
+
id="newEntitiesRestrictedByDefault"
|
|
136
|
+
checked={restricted}
|
|
137
|
+
onCheckedChange={(checked) => setRestricted(checked === true)}
|
|
138
|
+
disabled={saving}
|
|
139
|
+
className="mt-1"
|
|
140
|
+
/>
|
|
141
|
+
<div className="space-y-1">
|
|
142
|
+
<Label htmlFor="newEntitiesRestrictedByDefault" className="font-medium cursor-pointer">
|
|
143
|
+
{t('entities.settings.newEntitiesRestrictedByDefault.label', 'Restrict record access by default')}
|
|
144
|
+
</Label>
|
|
145
|
+
<p className="text-sm text-muted-foreground">
|
|
146
|
+
{t('entities.settings.newEntitiesRestrictedByDefault.description', 'Require explicit per-entity permissions for records of newly created custom entities by default.')}
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div className="flex items-center justify-end gap-3 border-t pt-4">
|
|
153
|
+
<Button type="submit" disabled={saving}>
|
|
154
|
+
{saving ? <Spinner className="mr-2 h-4 w-4" /> : null}
|
|
155
|
+
{t('common.save', 'Save')}
|
|
156
|
+
</Button>
|
|
157
|
+
</div>
|
|
158
|
+
</form>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
@@ -196,6 +196,14 @@
|
|
|
196
196
|
"entities.nav.systemEntities": "Systementitäten",
|
|
197
197
|
"entities.nav.userEntities": "Benutzerentitäten",
|
|
198
198
|
"entities.nav.userEntities.create": "Entität erstellen",
|
|
199
|
+
"entities.settings.description": "Globale Konfigurationen für benutzerdefinierte Entitäten verwalten.",
|
|
200
|
+
"entities.settings.errors.conflict": "Die Einstellungen wurden von einem anderen Benutzer geändert. Bitte neu laden.",
|
|
201
|
+
"entities.settings.errors.loadFailed": "Laden der Einstellungen fehlgeschlagen",
|
|
202
|
+
"entities.settings.errors.saveFailed": "Speichern der Einstellungen fehlgeschlagen",
|
|
203
|
+
"entities.settings.flash.saved": "Einstellungen gespeichert",
|
|
204
|
+
"entities.settings.newEntitiesRestrictedByDefault.description": "Für Datensätze neu erstellter benutzerdefinierter Entitäten standardmäßig explizite Berechtigungen pro Entität erfordern.",
|
|
205
|
+
"entities.settings.newEntitiesRestrictedByDefault.label": "Datensatzzugriff standardmäßig einschränken",
|
|
206
|
+
"entities.settings.title": "Benutzerdefinierte Entitäten",
|
|
199
207
|
"entities.userEntities.edit.errors.deleteEntityFailed": "Entität konnte nicht gelöscht werden",
|
|
200
208
|
"entities.userEntities.edit.errors.invalidEntityDescription": "Die angeforderte Entitäts-ID fehlt oder ist ungültig.",
|
|
201
209
|
"entities.userEntities.edit.errors.invalidEntityId": "Ungültige Entitäts-ID",
|
|
@@ -221,6 +229,7 @@
|
|
|
221
229
|
"entities.userEntities.errors.entityIdExists": "Entitäts-ID ist bereits vorhanden",
|
|
222
230
|
"entities.userEntities.errors.entityIdRequired": "Entitäts-ID ist erforderlich",
|
|
223
231
|
"entities.userEntities.errors.loadFailed": "Entitäten konnten nicht geladen werden",
|
|
232
|
+
"entities.userEntities.errors.settingsLoadFailed": "Standardeinschränkungsrichtlinie konnte nicht geladen werden; Standardwert wird verwendet",
|
|
224
233
|
"entities.userEntities.flash.created": "Entität erstellt",
|
|
225
234
|
"entities.userEntities.form.accessRestricted.help": "Erfordert eine explizite entitätsspezifische Berechtigung zum Anzeigen oder Verwalten der Datensätze dieser Entität. Deaktiviert lassen, um allen Benutzern mit der allgemeinen Datensatzberechtigung Zugriff zu gewähren.",
|
|
226
235
|
"entities.userEntities.form.accessRestricted.label": "Datensatzzugriff einschränken",
|
|
@@ -196,6 +196,14 @@
|
|
|
196
196
|
"entities.nav.systemEntities": "System Entities",
|
|
197
197
|
"entities.nav.userEntities": "User Entities",
|
|
198
198
|
"entities.nav.userEntities.create": "Create Entity",
|
|
199
|
+
"entities.settings.description": "Manage global custom entity configurations.",
|
|
200
|
+
"entities.settings.errors.conflict": "Settings were modified by another user. Please reload.",
|
|
201
|
+
"entities.settings.errors.loadFailed": "Failed to load settings",
|
|
202
|
+
"entities.settings.errors.saveFailed": "Failed to save settings",
|
|
203
|
+
"entities.settings.flash.saved": "Settings saved",
|
|
204
|
+
"entities.settings.newEntitiesRestrictedByDefault.description": "Require explicit per-entity permissions for records of newly created custom entities by default.",
|
|
205
|
+
"entities.settings.newEntitiesRestrictedByDefault.label": "Restrict record access by default",
|
|
206
|
+
"entities.settings.title": "Custom Entities",
|
|
199
207
|
"entities.userEntities.edit.errors.deleteEntityFailed": "Failed to delete entity",
|
|
200
208
|
"entities.userEntities.edit.errors.invalidEntityDescription": "The requested entity ID is missing or invalid.",
|
|
201
209
|
"entities.userEntities.edit.errors.invalidEntityId": "Invalid entity ID",
|
|
@@ -221,6 +229,7 @@
|
|
|
221
229
|
"entities.userEntities.errors.entityIdExists": "Entity ID already exists",
|
|
222
230
|
"entities.userEntities.errors.entityIdRequired": "Entity ID is required",
|
|
223
231
|
"entities.userEntities.errors.loadFailed": "Failed to load entities",
|
|
232
|
+
"entities.userEntities.errors.settingsLoadFailed": "Could not load default restriction policy; using default",
|
|
224
233
|
"entities.userEntities.flash.created": "Entity created",
|
|
225
234
|
"entities.userEntities.form.accessRestricted.help": "Require an explicit per-entity permission to view or manage this entity’s records. Leave off to allow anyone with the general records permission.",
|
|
226
235
|
"entities.userEntities.form.accessRestricted.label": "Restrict record access",
|
|
@@ -196,6 +196,14 @@
|
|
|
196
196
|
"entities.nav.systemEntities": "Entidades del sistema",
|
|
197
197
|
"entities.nav.userEntities": "Entidades de usuario",
|
|
198
198
|
"entities.nav.userEntities.create": "Crear entidad",
|
|
199
|
+
"entities.settings.description": "Administrar configuraciones globales de entidades personalizadas.",
|
|
200
|
+
"entities.settings.errors.conflict": "La configuración fue modificada por otro usuario. Por favor recarga.",
|
|
201
|
+
"entities.settings.errors.loadFailed": "Error al cargar la configuración",
|
|
202
|
+
"entities.settings.errors.saveFailed": "Error al guardar la configuración",
|
|
203
|
+
"entities.settings.flash.saved": "Configuración guardada",
|
|
204
|
+
"entities.settings.newEntitiesRestrictedByDefault.description": "Requerir permisos explícitos por entidad para registros de nuevas entidades creadas por defecto.",
|
|
205
|
+
"entities.settings.newEntitiesRestrictedByDefault.label": "Restringir acceso a registros por defecto",
|
|
206
|
+
"entities.settings.title": "Entidades personalizadas",
|
|
199
207
|
"entities.userEntities.edit.errors.deleteEntityFailed": "No se pudo eliminar la entidad",
|
|
200
208
|
"entities.userEntities.edit.errors.invalidEntityDescription": "Falta el ID de la entidad solicitada o no es válido.",
|
|
201
209
|
"entities.userEntities.edit.errors.invalidEntityId": "ID de entidad no válido",
|
|
@@ -221,6 +229,7 @@
|
|
|
221
229
|
"entities.userEntities.errors.entityIdExists": "El ID de la entidad ya existe",
|
|
222
230
|
"entities.userEntities.errors.entityIdRequired": "El ID de la entidad es obligatorio",
|
|
223
231
|
"entities.userEntities.errors.loadFailed": "No se pudieron cargar las entidades",
|
|
232
|
+
"entities.userEntities.errors.settingsLoadFailed": "No se pudo cargar la política de restricción por defecto; usando valor predeterminado",
|
|
224
233
|
"entities.userEntities.flash.created": "Entidad creada",
|
|
225
234
|
"entities.userEntities.form.accessRestricted.help": "Requiere un permiso explícito por entidad para ver o gestionar los registros de esta entidad. Déjalo desactivado para permitir el acceso a cualquiera con el permiso general de registros.",
|
|
226
235
|
"entities.userEntities.form.accessRestricted.label": "Restringir acceso a registros",
|
|
@@ -196,6 +196,14 @@
|
|
|
196
196
|
"entities.nav.systemEntities": "Encje systemowe",
|
|
197
197
|
"entities.nav.userEntities": "Encje użytkownika",
|
|
198
198
|
"entities.nav.userEntities.create": "Utwórz encję",
|
|
199
|
+
"entities.settings.description": "Zarządzaj globalnymi ustawieniami niestandardowych encji.",
|
|
200
|
+
"entities.settings.errors.conflict": "Ustawienia zostały zmodyfikowane przez innego użytkownika. Proszę przeładować.",
|
|
201
|
+
"entities.settings.errors.loadFailed": "Nie udało się załadować ustawień",
|
|
202
|
+
"entities.settings.errors.saveFailed": "Nie udało się zapisać ustawień",
|
|
203
|
+
"entities.settings.flash.saved": "Ustawienia zostały zapisane",
|
|
204
|
+
"entities.settings.newEntitiesRestrictedByDefault.description": "Domyślnie wymagaj jawnych uprawnień dla poszczególnych encji do rekordów nowo tworzonych niestandardowych encji.",
|
|
205
|
+
"entities.settings.newEntitiesRestrictedByDefault.label": "Domyślnie ograniczaj dostęp do rekordów",
|
|
206
|
+
"entities.settings.title": "Niestandardowe encje",
|
|
199
207
|
"entities.userEntities.edit.errors.deleteEntityFailed": "Nie udało się usunąć encji",
|
|
200
208
|
"entities.userEntities.edit.errors.invalidEntityDescription": "Brakuje identyfikatora encji albo jest on nieprawidłowy.",
|
|
201
209
|
"entities.userEntities.edit.errors.invalidEntityId": "Nieprawidłowy identyfikator encji",
|
|
@@ -221,6 +229,7 @@
|
|
|
221
229
|
"entities.userEntities.errors.entityIdExists": "Identyfikator encji już istnieje",
|
|
222
230
|
"entities.userEntities.errors.entityIdRequired": "Identyfikator encji jest wymagany",
|
|
223
231
|
"entities.userEntities.errors.loadFailed": "Nie udało się wczytać encji",
|
|
232
|
+
"entities.userEntities.errors.settingsLoadFailed": "Nie udało się załadować domyślnej polityki ograniczeń; używanie wartości domyślnej",
|
|
224
233
|
"entities.userEntities.flash.created": "Encja została utworzona",
|
|
225
234
|
"entities.userEntities.form.accessRestricted.help": "Wymaga jawnego uprawnienia dla tej encji, aby przeglądać lub zarządzać jej rekordami. Pozostaw wyłączone, aby zezwolić na dostęp każdemu z ogólnym uprawnieniem do rekordów.",
|
|
226
235
|
"entities.userEntities.form.accessRestricted.label": "Ogranicz dostęp do rekordów",
|
|
@@ -13,6 +13,11 @@ import { parseInboundEmail } from '../../lib/emailParser'
|
|
|
13
13
|
import { checkRateLimit } from '../../lib/rateLimiter'
|
|
14
14
|
import { emitInboxOpsEvent } from '../../events'
|
|
15
15
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
16
|
+
import {
|
|
17
|
+
readBoundedRequestBody,
|
|
18
|
+
resolveWebhookBodyLimitBytes,
|
|
19
|
+
WebhookBodyTooLargeError,
|
|
20
|
+
} from '@open-mercato/shared/lib/webhooks'
|
|
16
21
|
|
|
17
22
|
const logger = createLogger('inbox_ops').child({ component: 'webhook' })
|
|
18
23
|
|
|
@@ -20,7 +25,10 @@ export const metadata = {
|
|
|
20
25
|
POST: { requireAuth: false },
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
const MAX_PAYLOAD_SIZE =
|
|
28
|
+
const MAX_PAYLOAD_SIZE = resolveWebhookBodyLimitBytes(
|
|
29
|
+
process.env.INBOX_OPS_WEBHOOK_MAX_BODY_BYTES ?? process.env.OM_WEBHOOK_MAX_BODY_BYTES,
|
|
30
|
+
2 * 1024 * 1024,
|
|
31
|
+
)
|
|
24
32
|
const REPLAY_WINDOW_MS = 5 * 60 * 1000 // 5 minutes
|
|
25
33
|
|
|
26
34
|
function isTimestampFresh(timestamp: string): boolean {
|
|
@@ -211,22 +219,16 @@ export async function POST(req: Request) {
|
|
|
211
219
|
return NextResponse.json({ error: 'Service unavailable' }, { status: 503 })
|
|
212
220
|
}
|
|
213
221
|
|
|
214
|
-
const contentLength = parseInt(req.headers.get('content-length') || '0', 10)
|
|
215
|
-
if (contentLength > MAX_PAYLOAD_SIZE) {
|
|
216
|
-
return NextResponse.json({ error: 'Payload too large' }, { status: 413 })
|
|
217
|
-
}
|
|
218
|
-
|
|
219
222
|
let rawBody: string
|
|
220
223
|
try {
|
|
221
|
-
rawBody = await req
|
|
222
|
-
} catch {
|
|
224
|
+
rawBody = await readBoundedRequestBody(req, { maxBytes: MAX_PAYLOAD_SIZE })
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (error instanceof WebhookBodyTooLargeError) {
|
|
227
|
+
return NextResponse.json({ error: 'Payload too large' }, { status: 413 })
|
|
228
|
+
}
|
|
223
229
|
return NextResponse.json({ error: 'Bad request' }, { status: 400 })
|
|
224
230
|
}
|
|
225
231
|
|
|
226
|
-
if (rawBody.length > MAX_PAYLOAD_SIZE) {
|
|
227
|
-
return NextResponse.json({ error: 'Payload too large' }, { status: 413 })
|
|
228
|
-
}
|
|
229
|
-
|
|
230
232
|
const verified = await verifyAndParse(req, rawBody)
|
|
231
233
|
if (!verified.ok) {
|
|
232
234
|
return verified.response
|