@endora-commerce/mod-organizations 0.100.0
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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/admin/components/OrganizationSalesRepsTab.d.ts +27 -0
- package/dist/admin/components/OrganizationSalesRepsTab.d.ts.map +1 -0
- package/dist/admin/components/OrganizationSalesRepsTab.js +170 -0
- package/dist/admin/components/OrganizationSalesRepsTab.js.map +1 -0
- package/dist/admin/index.d.ts +30 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +59 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/OrganizationDetail.d.ts +9 -0
- package/dist/admin/pages/OrganizationDetail.d.ts.map +1 -0
- package/dist/admin/pages/OrganizationDetail.js +247 -0
- package/dist/admin/pages/OrganizationDetail.js.map +1 -0
- package/dist/admin/pages/OrganizationsList.d.ts +9 -0
- package/dist/admin/pages/OrganizationsList.d.ts.map +1 -0
- package/dist/admin/pages/OrganizationsList.js +73 -0
- package/dist/admin/pages/OrganizationsList.js.map +1 -0
- package/dist/admin/panels/ApplicablePriceListsPanel.d.ts +12 -0
- package/dist/admin/panels/ApplicablePriceListsPanel.d.ts.map +1 -0
- package/dist/admin/panels/ApplicablePriceListsPanel.js +49 -0
- package/dist/admin/panels/ApplicablePriceListsPanel.js.map +1 -0
- package/dist/admin/panels/FulfilmentStrategyPanel.d.ts +16 -0
- package/dist/admin/panels/FulfilmentStrategyPanel.d.ts.map +1 -0
- package/dist/admin/panels/FulfilmentStrategyPanel.js +29 -0
- package/dist/admin/panels/FulfilmentStrategyPanel.js.map +1 -0
- package/dist/admin/panels/HierarchyPanel.d.ts +15 -0
- package/dist/admin/panels/HierarchyPanel.d.ts.map +1 -0
- package/dist/admin/panels/HierarchyPanel.js +65 -0
- package/dist/admin/panels/HierarchyPanel.js.map +1 -0
- package/dist/admin/panels/ModerationActionsPanel.d.ts +28 -0
- package/dist/admin/panels/ModerationActionsPanel.d.ts.map +1 -0
- package/dist/admin/panels/ModerationActionsPanel.js +111 -0
- package/dist/admin/panels/ModerationActionsPanel.js.map +1 -0
- package/dist/admin/panels/RestrictionsPanel.d.ts +24 -0
- package/dist/admin/panels/RestrictionsPanel.d.ts.map +1 -0
- package/dist/admin/panels/RestrictionsPanel.js +121 -0
- package/dist/admin/panels/RestrictionsPanel.js.map +1 -0
- package/dist/admin/panels/VatValidationPanel.d.ts +28 -0
- package/dist/admin/panels/VatValidationPanel.d.ts.map +1 -0
- package/dist/admin/panels/VatValidationPanel.js +79 -0
- package/dist/admin/panels/VatValidationPanel.js.map +1 -0
- package/dist/backend/commands/move.command.d.ts +11 -0
- package/dist/backend/commands/move.command.d.ts.map +1 -0
- package/dist/backend/commands/move.command.js +11 -0
- package/dist/backend/commands/move.command.js.map +1 -0
- package/dist/backend/commands/set-cart-approval-policy.command.d.ts +31 -0
- package/dist/backend/commands/set-cart-approval-policy.command.d.ts.map +1 -0
- package/dist/backend/commands/set-cart-approval-policy.command.js +56 -0
- package/dist/backend/commands/set-cart-approval-policy.command.js.map +1 -0
- package/dist/backend/commands/set-credit-mode.command.d.ts +16 -0
- package/dist/backend/commands/set-credit-mode.command.d.ts.map +1 -0
- package/dist/backend/commands/set-credit-mode.command.js +25 -0
- package/dist/backend/commands/set-credit-mode.command.js.map +1 -0
- package/dist/backend/commands/set-parent.command.d.ts +26 -0
- package/dist/backend/commands/set-parent.command.d.ts.map +1 -0
- package/dist/backend/commands/set-parent.command.js +51 -0
- package/dist/backend/commands/set-parent.command.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +20 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +11 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +44 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +44 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +20 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +28 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/email-templates/invitation.d.ts +23 -0
- package/dist/backend/email-templates/invitation.d.ts.map +1 -0
- package/dist/backend/email-templates/invitation.js +35 -0
- package/dist/backend/email-templates/invitation.js.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts +54 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.js +242 -0
- package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
- package/dist/backend/email-templates/verification.d.ts +14 -0
- package/dist/backend/email-templates/verification.d.ts.map +1 -0
- package/dist/backend/email-templates/verification.js +34 -0
- package/dist/backend/email-templates/verification.js.map +1 -0
- package/dist/backend/entities/email-verification-token.entity.d.ts +17 -0
- package/dist/backend/entities/email-verification-token.entity.d.ts.map +1 -0
- package/dist/backend/entities/email-verification-token.entity.js +60 -0
- package/dist/backend/entities/email-verification-token.entity.js.map +1 -0
- package/dist/backend/entities/organization-delivery-method-link.entity.d.ts +14 -0
- package/dist/backend/entities/organization-delivery-method-link.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization-delivery-method-link.entity.js +41 -0
- package/dist/backend/entities/organization-delivery-method-link.entity.js.map +1 -0
- package/dist/backend/entities/organization-invitation.entity.d.ts +21 -0
- package/dist/backend/entities/organization-invitation.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization-invitation.entity.js +85 -0
- package/dist/backend/entities/organization-invitation.entity.js.map +1 -0
- package/dist/backend/entities/organization-payment-method-link.entity.d.ts +14 -0
- package/dist/backend/entities/organization-payment-method-link.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization-payment-method-link.entity.js +41 -0
- package/dist/backend/entities/organization-payment-method-link.entity.js.map +1 -0
- package/dist/backend/entities/organization-sales-rep-assignment.entity.d.ts +17 -0
- package/dist/backend/entities/organization-sales-rep-assignment.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization-sales-rep-assignment.entity.js +56 -0
- package/dist/backend/entities/organization-sales-rep-assignment.entity.js.map +1 -0
- package/dist/backend/entities/organization-tax-id-validation.entity.d.ts +29 -0
- package/dist/backend/entities/organization-tax-id-validation.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization-tax-id-validation.entity.js +78 -0
- package/dist/backend/entities/organization-tax-id-validation.entity.js.map +1 -0
- package/dist/backend/entities/organization-warehouse-link.entity.d.ts +15 -0
- package/dist/backend/entities/organization-warehouse-link.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization-warehouse-link.entity.js +42 -0
- package/dist/backend/entities/organization-warehouse-link.entity.js.map +1 -0
- package/dist/backend/entities/organization.entity.d.ts +125 -0
- package/dist/backend/entities/organization.entity.d.ts.map +1 -0
- package/dist/backend/entities/organization.entity.js +282 -0
- package/dist/backend/entities/organization.entity.js.map +1 -0
- package/dist/backend/index.d.ts +250 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +506 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/integrations/ministerstwo-finansow-client.d.ts +22 -0
- package/dist/backend/integrations/ministerstwo-finansow-client.d.ts.map +1 -0
- package/dist/backend/integrations/ministerstwo-finansow-client.js +172 -0
- package/dist/backend/integrations/ministerstwo-finansow-client.js.map +1 -0
- package/dist/backend/integrations/vies-client.d.ts +18 -0
- package/dist/backend/integrations/vies-client.d.ts.map +1 -0
- package/dist/backend/integrations/vies-client.js +133 -0
- package/dist/backend/integrations/vies-client.js.map +1 -0
- package/dist/backend/plugin.d.ts +126 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +139 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +66 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +711 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.customer.d.ts +33 -0
- package/dist/backend/routes.customer.d.ts.map +1 -0
- package/dist/backend/routes.customer.js +139 -0
- package/dist/backend/routes.customer.js.map +1 -0
- package/dist/backend/routes.members.d.ts +31 -0
- package/dist/backend/routes.members.d.ts.map +1 -0
- package/dist/backend/routes.members.js +109 -0
- package/dist/backend/routes.members.js.map +1 -0
- package/dist/backend/routes.public.d.ts +49 -0
- package/dist/backend/routes.public.d.ts.map +1 -0
- package/dist/backend/routes.public.js +220 -0
- package/dist/backend/routes.public.js.map +1 -0
- package/dist/backend/routes.sales-reps.d.ts +46 -0
- package/dist/backend/routes.sales-reps.d.ts.map +1 -0
- package/dist/backend/routes.sales-reps.js +114 -0
- package/dist/backend/routes.sales-reps.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +25 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +65 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/schemas/organization.d.ts +59 -0
- package/dist/backend/schemas/organization.d.ts.map +1 -0
- package/dist/backend/schemas/organization.js +55 -0
- package/dist/backend/schemas/organization.js.map +1 -0
- package/dist/backend/schemas/settings.d.ts +30 -0
- package/dist/backend/schemas/settings.d.ts.map +1 -0
- package/dist/backend/schemas/settings.js +35 -0
- package/dist/backend/schemas/settings.js.map +1 -0
- package/dist/backend/services/email-verification-service.d.ts +45 -0
- package/dist/backend/services/email-verification-service.d.ts.map +1 -0
- package/dist/backend/services/email-verification-service.js +95 -0
- package/dist/backend/services/email-verification-service.js.map +1 -0
- package/dist/backend/services/invitation-service.d.ts +85 -0
- package/dist/backend/services/invitation-service.d.ts.map +1 -0
- package/dist/backend/services/invitation-service.js +266 -0
- package/dist/backend/services/invitation-service.js.map +1 -0
- package/dist/backend/services/normalize-name.d.ts +14 -0
- package/dist/backend/services/normalize-name.d.ts.map +1 -0
- package/dist/backend/services/normalize-name.js +14 -0
- package/dist/backend/services/normalize-name.js.map +1 -0
- package/dist/backend/services/org-registration-notifier.d.ts +36 -0
- package/dist/backend/services/org-registration-notifier.d.ts.map +1 -0
- package/dist/backend/services/org-registration-notifier.js +109 -0
- package/dist/backend/services/org-registration-notifier.js.map +1 -0
- package/dist/backend/services/org-template-email.d.ts +18 -0
- package/dist/backend/services/org-template-email.d.ts.map +1 -0
- package/dist/backend/services/org-template-email.js +16 -0
- package/dist/backend/services/org-template-email.js.map +1 -0
- package/dist/backend/services/organization-context-service.d.ts +52 -0
- package/dist/backend/services/organization-context-service.d.ts.map +1 -0
- package/dist/backend/services/organization-context-service.js +70 -0
- package/dist/backend/services/organization-context-service.js.map +1 -0
- package/dist/backend/services/organization-country-reference.d.ts +11 -0
- package/dist/backend/services/organization-country-reference.d.ts.map +1 -0
- package/dist/backend/services/organization-country-reference.js +24 -0
- package/dist/backend/services/organization-country-reference.js.map +1 -0
- package/dist/backend/services/organization-details-port.d.ts +33 -0
- package/dist/backend/services/organization-details-port.d.ts.map +1 -0
- package/dist/backend/services/organization-details-port.js +95 -0
- package/dist/backend/services/organization-details-port.js.map +1 -0
- package/dist/backend/services/organization-effective-pricelists-service.d.ts +50 -0
- package/dist/backend/services/organization-effective-pricelists-service.d.ts.map +1 -0
- package/dist/backend/services/organization-effective-pricelists-service.js +68 -0
- package/dist/backend/services/organization-effective-pricelists-service.js.map +1 -0
- package/dist/backend/services/organization-inheritance-service.d.ts +73 -0
- package/dist/backend/services/organization-inheritance-service.d.ts.map +1 -0
- package/dist/backend/services/organization-inheritance-service.js +63 -0
- package/dist/backend/services/organization-inheritance-service.js.map +1 -0
- package/dist/backend/services/organization-moderation-service.d.ts +124 -0
- package/dist/backend/services/organization-moderation-service.d.ts.map +1 -0
- package/dist/backend/services/organization-moderation-service.js +337 -0
- package/dist/backend/services/organization-moderation-service.js.map +1 -0
- package/dist/backend/services/organization-restriction-service.d.ts +70 -0
- package/dist/backend/services/organization-restriction-service.d.ts.map +1 -0
- package/dist/backend/services/organization-restriction-service.js +210 -0
- package/dist/backend/services/organization-restriction-service.js.map +1 -0
- package/dist/backend/services/organization-tax-id-validation-service.d.ts +59 -0
- package/dist/backend/services/organization-tax-id-validation-service.d.ts.map +1 -0
- package/dist/backend/services/organization-tax-id-validation-service.js +115 -0
- package/dist/backend/services/organization-tax-id-validation-service.js.map +1 -0
- package/dist/backend/services/organization-tree-service.d.ts +102 -0
- package/dist/backend/services/organization-tree-service.d.ts.map +1 -0
- package/dist/backend/services/organization-tree-service.js +184 -0
- package/dist/backend/services/organization-tree-service.js.map +1 -0
- package/dist/backend/services/personal-organization-service.d.ts +130 -0
- package/dist/backend/services/personal-organization-service.d.ts.map +1 -0
- package/dist/backend/services/personal-organization-service.js +207 -0
- package/dist/backend/services/personal-organization-service.js.map +1 -0
- package/dist/backend/services/registration-service.d.ts +80 -0
- package/dist/backend/services/registration-service.d.ts.map +1 -0
- package/dist/backend/services/registration-service.js +126 -0
- package/dist/backend/services/registration-service.js.map +1 -0
- package/dist/backend/services/sales-rep-assignment-service.d.ts +89 -0
- package/dist/backend/services/sales-rep-assignment-service.d.ts.map +1 -0
- package/dist/backend/services/sales-rep-assignment-service.js +176 -0
- package/dist/backend/services/sales-rep-assignment-service.js.map +1 -0
- package/dist/backend/services/vat-validator-port.d.ts +20 -0
- package/dist/backend/services/vat-validator-port.d.ts.map +1 -0
- package/dist/backend/services/vat-validator-port.js +2 -0
- package/dist/backend/services/vat-validator-port.js.map +1 -0
- package/dist/manifest.d.ts +176 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +503 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260424T205317_organizations_init.d.ts +13 -0
- package/dist/migrations/20260424T205317_organizations_init.d.ts.map +1 -0
- package/dist/migrations/20260424T205317_organizations_init.js +100 -0
- package/dist/migrations/20260424T205317_organizations_init.js.map +1 -0
- package/dist/migrations/20260425T051700_organizations_invitations_init.d.ts +13 -0
- package/dist/migrations/20260425T051700_organizations_invitations_init.d.ts.map +1 -0
- package/dist/migrations/20260425T051700_organizations_invitations_init.js +39 -0
- package/dist/migrations/20260425T051700_organizations_invitations_init.js.map +1 -0
- package/dist/migrations/20260611T140349_organizations_consolidation.d.ts +35 -0
- package/dist/migrations/20260611T140349_organizations_consolidation.d.ts.map +1 -0
- package/dist/migrations/20260611T140349_organizations_consolidation.js +180 -0
- package/dist/migrations/20260611T140349_organizations_consolidation.js.map +1 -0
- package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.d.ts +10 -0
- package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.d.ts.map +1 -0
- package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.js +14 -0
- package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.js.map +1 -0
- package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.d.ts +14 -0
- package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.d.ts.map +1 -0
- package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.js +20 -0
- package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.js.map +1 -0
- package/dist/migrations/20260717T151403_organizations_personal_organizations.d.ts +24 -0
- package/dist/migrations/20260717T151403_organizations_personal_organizations.d.ts.map +1 -0
- package/dist/migrations/20260717T151403_organizations_personal_organizations.js +63 -0
- package/dist/migrations/20260717T151403_organizations_personal_organizations.js.map +1 -0
- package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.d.ts +12 -0
- package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.d.ts.map +1 -0
- package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.js +16 -0
- package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.js.map +1 -0
- package/dist/migrations/20260720T044254_organizations_org_hierarchy.d.ts +23 -0
- package/dist/migrations/20260720T044254_organizations_org_hierarchy.d.ts.map +1 -0
- package/dist/migrations/20260720T044254_organizations_org_hierarchy.js +37 -0
- package/dist/migrations/20260720T044254_organizations_org_hierarchy.js.map +1 -0
- package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.d.ts +27 -0
- package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.d.ts.map +1 -0
- package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.js +44 -0
- package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.js.map +1 -0
- package/dist/migrations/index.d.ts +35 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +45 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +53 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/organization-hierarchy.md +117 -0
- package/docs/organizations.md +312 -0
- package/i18n/en.json +17 -0
- package/i18n/pl.json +17 -0
- package/package.json +107 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { SESSION_COOKIE_NAME, customerLoginRequestSchema, emailVerificationRequestSchema, normalizeEmailAddress, passwordResetConfirmSchema, passwordResetRequestSchema, registerOrganizationRequestSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { currentSalesChannel } from '@endora-commerce/platform/kernel';
|
|
3
|
+
import { buildVerificationEmail } from './email-templates/verification.js';
|
|
4
|
+
export async function registerOrganizationsPublicRoutes(app, deps) {
|
|
5
|
+
const { registrationService, verificationService, customerAuthService, passwordResetService } = deps;
|
|
6
|
+
app.post('/api/v1/organizations/register', { schema: { body: registerOrganizationRequestSchema } }, async (request, reply) => {
|
|
7
|
+
const body = registerOrganizationRequestSchema.parse(request.body);
|
|
8
|
+
const result = await registrationService.registerOrganization(body);
|
|
9
|
+
// Stash the raw token for the test-only probe so integration tests can
|
|
10
|
+
// complete the verify step without reading the mail.
|
|
11
|
+
if (deps.latestTokenByEmail) {
|
|
12
|
+
deps.latestTokenByEmail.set(result.customerAccount.email, result.verificationToken);
|
|
13
|
+
deps.latestTokenByEmail.set('__latest__', result.verificationToken);
|
|
14
|
+
}
|
|
15
|
+
let emailVerificationSent = false;
|
|
16
|
+
try {
|
|
17
|
+
const message = buildVerificationEmail({
|
|
18
|
+
customerAccountId: result.customerAccount.id,
|
|
19
|
+
rawToken: result.verificationToken,
|
|
20
|
+
recipientEmail: result.customerAccount.email,
|
|
21
|
+
organizationName: result.organization.name,
|
|
22
|
+
storefrontBaseUrl: deps.storefrontBaseUrl,
|
|
23
|
+
});
|
|
24
|
+
const verifyUrl = message.meta?.verifyUrl ?? '';
|
|
25
|
+
const sentViaTemplate = deps.templateEmail
|
|
26
|
+
? await deps.templateEmail.trySend({
|
|
27
|
+
code: 'email_verification',
|
|
28
|
+
to: result.customerAccount.email,
|
|
29
|
+
messageId: message.messageId,
|
|
30
|
+
variables: { organizationName: result.organization.name, verifyUrl },
|
|
31
|
+
meta: message.meta,
|
|
32
|
+
})
|
|
33
|
+
: false;
|
|
34
|
+
// The flag is in the response body, so it answers what the transport
|
|
35
|
+
// answered rather than "we reached this line" (D-59).
|
|
36
|
+
const outcome = sentViaTemplate
|
|
37
|
+
? { status: 'sent' }
|
|
38
|
+
: await deps.mailer.send(message);
|
|
39
|
+
emailVerificationSent = outcome.status === 'sent';
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
request.log.error({ err }, 'Failed to send verification email');
|
|
43
|
+
}
|
|
44
|
+
reply.status(201);
|
|
45
|
+
return {
|
|
46
|
+
data: {
|
|
47
|
+
organization: serializeOrganization(result.organization),
|
|
48
|
+
customerAccount: serializeCustomerAccount(result.customerAccount),
|
|
49
|
+
emailVerificationSent,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
app.post('/api/v1/auth/email-verification/verify', { schema: { body: emailVerificationRequestSchema } }, async (request) => {
|
|
54
|
+
const body = emailVerificationRequestSchema.parse(request.body);
|
|
55
|
+
const result = await verificationService.verify(body.token);
|
|
56
|
+
return {
|
|
57
|
+
data: {
|
|
58
|
+
organizationId: result.organizationId,
|
|
59
|
+
customerAccountId: result.customerAccountId,
|
|
60
|
+
verifiedAt: result.verifiedAt.toISOString(),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
app.post('/api/v1/auth/customer/login', { schema: { body: customerLoginRequestSchema } }, async (request, reply) => {
|
|
65
|
+
const body = customerLoginRequestSchema.parse(request.body);
|
|
66
|
+
const channelId = currentSalesChannel()?.id ?? null;
|
|
67
|
+
const result = await customerAuthService.login({
|
|
68
|
+
email: body.email,
|
|
69
|
+
password: body.password,
|
|
70
|
+
salesChannelId: channelId,
|
|
71
|
+
...(request.ip ? { ip: request.ip } : {}),
|
|
72
|
+
...(typeof request.headers['user-agent'] === 'string'
|
|
73
|
+
? { userAgent: request.headers['user-agent'] }
|
|
74
|
+
: {}),
|
|
75
|
+
});
|
|
76
|
+
// Feature 042 — two-step login. The MFA cases carry no session cookie;
|
|
77
|
+
// the client completes the second step at the MFA verify endpoint.
|
|
78
|
+
if (result.status === 'mfaRequired') {
|
|
79
|
+
return { data: { status: 'mfaRequired', challengeId: result.challengeId } };
|
|
80
|
+
}
|
|
81
|
+
if (result.status === 'mfaSetupRequired') {
|
|
82
|
+
return { data: { status: 'mfaSetupRequired', setupTicket: result.setupTicket } };
|
|
83
|
+
}
|
|
84
|
+
setSessionCookie(reply, result.sessionCookieValue, result.sessionExpiresAt);
|
|
85
|
+
// Merge any anonymous cart the caller was carrying into the authenticated
|
|
86
|
+
// cart (feature 027 R-09 / feature 037-cart-merge-on-login). The hook is
|
|
87
|
+
// best-effort: a thrown error MUST NOT break login (037 FR-007/FR-008).
|
|
88
|
+
let cartMerge;
|
|
89
|
+
if (deps.onLogin) {
|
|
90
|
+
const cookies = request.cookies;
|
|
91
|
+
const anon = cookies?.['b2b_cart_anon'];
|
|
92
|
+
const compareAnon = cookies?.['compare_token'];
|
|
93
|
+
try {
|
|
94
|
+
const hookResult = await deps.onLogin({
|
|
95
|
+
customerAccountId: result.customerAccount.id,
|
|
96
|
+
organizationId: result.customerAccount.organizationId ?? null,
|
|
97
|
+
...(anon ? { anonymousCartToken: anon } : {}),
|
|
98
|
+
...(compareAnon ? { anonymousCompareToken: compareAnon } : {}),
|
|
99
|
+
});
|
|
100
|
+
cartMerge = hookResult.cartMerge;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
request.log.error({
|
|
104
|
+
err,
|
|
105
|
+
customerAccountId: result.customerAccount.id,
|
|
106
|
+
hadAnonToken: Boolean(anon),
|
|
107
|
+
}, 'cart_merge_on_login_failed');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Clear the stale anon-cart cookie when the source token has been
|
|
111
|
+
// consumed (adopted/merged) or pointed at an empty cart (noop_empty).
|
|
112
|
+
// Failure and noop_no_anon both leave the cookie alone — failure so the
|
|
113
|
+
// buyer can retry on the next login, noop_no_anon because there was
|
|
114
|
+
// nothing to clear in the first place. See spec R-06.
|
|
115
|
+
if (cartMerge && cartMerge.outcome !== 'noop_no_anon') {
|
|
116
|
+
reply.clearCookie('b2b_cart_anon', { path: '/' });
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
data: {
|
|
120
|
+
status: 'authenticated',
|
|
121
|
+
customerAccount: serializeCustomerAccount(result.customerAccount),
|
|
122
|
+
...(cartMerge ? { cartMerge: narrowCartMergeForHttp(cartMerge) } : {}),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
app.post('/api/v1/auth/password-reset/request', { schema: { body: passwordResetRequestSchema } }, async (request, reply) => {
|
|
127
|
+
const body = passwordResetRequestSchema.parse(request.body);
|
|
128
|
+
// The address is handed over as typed — the port folds it, as every
|
|
129
|
+
// other lookup on that table does. The probe key below is the one place
|
|
130
|
+
// the folded form is needed here, and it uses the same function so the
|
|
131
|
+
// key cannot drift from what the lookup matched.
|
|
132
|
+
const result = await passwordResetService.requestReset(body.email);
|
|
133
|
+
if (deps.latestTokenByEmail && result.rawToken) {
|
|
134
|
+
deps.latestTokenByEmail.set(`reset:${normalizeEmailAddress(body.email)}`, result.rawToken);
|
|
135
|
+
deps.latestTokenByEmail.set('__latest_reset__', result.rawToken);
|
|
136
|
+
}
|
|
137
|
+
// Always 202 — defends against account enumeration.
|
|
138
|
+
return reply.status(202).send();
|
|
139
|
+
});
|
|
140
|
+
app.post('/api/v1/auth/password-reset/confirm', { schema: { body: passwordResetConfirmSchema } }, async (request, reply) => {
|
|
141
|
+
const body = passwordResetConfirmSchema.parse(request.body);
|
|
142
|
+
await passwordResetService.confirmReset(body.token, body.newPassword);
|
|
143
|
+
return reply.status(200).send({ data: { ok: true } });
|
|
144
|
+
});
|
|
145
|
+
app.post('/api/v1/auth/customer/logout', async (request, reply) => {
|
|
146
|
+
const cookies = request.cookies;
|
|
147
|
+
const raw = cookies?.[SESSION_COOKIE_NAME];
|
|
148
|
+
if (raw) {
|
|
149
|
+
const dot = raw.indexOf('.');
|
|
150
|
+
if (dot !== -1) {
|
|
151
|
+
const sessionId = raw.slice(0, dot);
|
|
152
|
+
if (sessionId) {
|
|
153
|
+
await customerAuthService.logout(sessionId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
reply.clearCookie(SESSION_COOKIE_NAME, { path: '/' });
|
|
158
|
+
return reply.status(204).send();
|
|
159
|
+
});
|
|
160
|
+
// Test-only probe for the register→verify integration test.
|
|
161
|
+
if (deps.exposeTestProbe && deps.latestTokenByEmail) {
|
|
162
|
+
app.get('/api/v1/_test/latest-verification-token', async () => {
|
|
163
|
+
const token = deps.latestTokenByEmail.get('__latest__');
|
|
164
|
+
if (!token)
|
|
165
|
+
return { token: null };
|
|
166
|
+
return { token };
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function setSessionCookie(reply, value, expiresAt) {
|
|
171
|
+
reply.setCookie(SESSION_COOKIE_NAME, value, {
|
|
172
|
+
path: '/',
|
|
173
|
+
httpOnly: true,
|
|
174
|
+
sameSite: 'lax',
|
|
175
|
+
secure: process.env['NODE_ENV'] === 'production',
|
|
176
|
+
expires: expiresAt,
|
|
177
|
+
signed: false,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function serializeOrganization(o) {
|
|
181
|
+
return {
|
|
182
|
+
id: o.id,
|
|
183
|
+
name: o.name,
|
|
184
|
+
taxId: o.taxId,
|
|
185
|
+
status: o.status,
|
|
186
|
+
vatStatus: o.vatStatus,
|
|
187
|
+
registeredAddress: o.registeredAddress,
|
|
188
|
+
createdAt: o.createdAt.toISOString(),
|
|
189
|
+
updatedAt: o.updatedAt.toISOString(),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function serializeCustomerAccount(c) {
|
|
193
|
+
return {
|
|
194
|
+
id: c.id,
|
|
195
|
+
organizationId: c.organizationId ?? null,
|
|
196
|
+
email: c.email,
|
|
197
|
+
firstName: c.firstName,
|
|
198
|
+
lastName: c.lastName,
|
|
199
|
+
role: c.role,
|
|
200
|
+
emailVerifiedAt: c.emailVerifiedAt?.toISOString() ?? null,
|
|
201
|
+
// The record already answers this; the entity carried the secret's
|
|
202
|
+
// confirmation timestamp and every caller derived the same boolean from it.
|
|
203
|
+
twoFactorEnabled: c.twoFactorEnabled,
|
|
204
|
+
createdAt: c.createdAt.toISOString(),
|
|
205
|
+
updatedAt: c.updatedAt.toISOString(),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Collapse the internal `CartMergeOutcome` (with the two `noop_*` variants
|
|
210
|
+
* and the line-count diagnostics) into the storefront-facing shape that
|
|
211
|
+
* exposes only `outcome ∈ {adopted, merged, noop}` and the destination
|
|
212
|
+
* cart id. See specs/037-cart-merge-on-login/contracts/cart-merge.md § 1.
|
|
213
|
+
*/
|
|
214
|
+
function narrowCartMergeForHttp(internal) {
|
|
215
|
+
const outcome = internal.outcome === 'noop_empty' || internal.outcome === 'noop_no_anon'
|
|
216
|
+
? 'noop'
|
|
217
|
+
: internal.outcome;
|
|
218
|
+
return { outcome, destinationCartId: internal.destinationCartId };
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=routes.public.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.public.js","sourceRoot":"","sources":["../../src/backend/routes.public.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { type AdminUserReadPort } from '@endora-commerce/contracts';
|
|
3
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
4
|
+
import type { SalesRepAssignmentPort } from './services/sales-rep-assignment-service.js';
|
|
5
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
6
|
+
/**
|
|
7
|
+
* Sales-rep ↔ organization assignment routes (feature 008 / T077).
|
|
8
|
+
*
|
|
9
|
+
* Owned **and registered** by `organizations` since D-166. Three of the four
|
|
10
|
+
* endpoints this file used to hold are here; the fourth —
|
|
11
|
+
* `GET /admin/sales-reps/:adminUserId/organizations`, the only one that reads a
|
|
12
|
+
* `QuoteRequest` — moved to `quote_requests/routes.sales-reps.ts`, which is the
|
|
13
|
+
* module that owns the fact it reports.
|
|
14
|
+
*
|
|
15
|
+
* The split is what makes the gate honest, and it is a consequence rather than
|
|
16
|
+
* a preference. The whole file used to be registered by `quote_requests`
|
|
17
|
+
* through a dynamic `import()`, so all four endpoints were gated by *that*
|
|
18
|
+
* module's effective state and permissioned `rfqs:handle`. Neither half of that
|
|
19
|
+
* arrangement survives inspection: assigning a sales representative qualifies an
|
|
20
|
+
* organisation and has nothing to do with quote requests, so switching quote
|
|
21
|
+
* requests off must not take the assignment screen with it — and registering the
|
|
22
|
+
* file here without splitting it would be worse, because `rfqs:handle` is
|
|
23
|
+
* declared `module: 'quote_requests'` and `organizations` is `nonDeactivatable`,
|
|
24
|
+
* which leaves a permanently mounted screen behind a permission that disappears
|
|
25
|
+
* from `/admin-roles` the moment quote requests is switched off. The gate travels
|
|
26
|
+
* with the routes.
|
|
27
|
+
*
|
|
28
|
+
* `organizations:assign-sales-rep` is the code, and it is the one this file has
|
|
29
|
+
* proposed in its own comment since feature 008 — the placeholder note said
|
|
30
|
+
* "a follow-up can add a dedicated `organizations:assign-sales-rep` permission",
|
|
31
|
+
* and this is that follow-up.
|
|
32
|
+
*
|
|
33
|
+
* Admins are read through `adminUserReadPort` rather than through the
|
|
34
|
+
* `AdminUser` entity, which is what retires this file's two entries in
|
|
35
|
+
* `organizations`' cross-module-import shard: the reads were always
|
|
36
|
+
* `findById`/`findByIds`, both published, and the only thing stopping the port
|
|
37
|
+
* from being injected was that another module built this file's `deps`.
|
|
38
|
+
*/
|
|
39
|
+
export interface SalesRepRoutesDeps {
|
|
40
|
+
emFactory: () => EntityManager;
|
|
41
|
+
requireAdmin: RequireAdminFactory;
|
|
42
|
+
salesRepAssignment: SalesRepAssignmentPort;
|
|
43
|
+
adminUsers: AdminUserReadPort;
|
|
44
|
+
}
|
|
45
|
+
export declare function registerOrganizationsSalesRepRoutes(app: FastifyInstance, deps: SalesRepRoutesDeps): Promise<void>;
|
|
46
|
+
//# sourceMappingURL=routes.sales-reps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.sales-reps.d.ts","sourceRoot":"","sources":["../../src/backend/routes.sales-reps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,mBAAmB,CAAC;IAClC,kBAAkB,EAAE,sBAAsB,CAAC;IAC3C,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAsCD,wBAAsB,mCAAmC,CACvD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CA2Ff"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ERROR_CODES, assignSalesRepRequestSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { getTenantContext } from '@endora-commerce/platform/tenancy';
|
|
4
|
+
import { Organization } from './entities/organization.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* D-260 — **a write that changes the acting principal's own authority cannot be
|
|
7
|
+
* authorised by that authority.**
|
|
8
|
+
*
|
|
9
|
+
* `OrganizationSalesRepAssignment` is the table that *defines* `allowed-set`, so
|
|
10
|
+
* a scoped admin holding `organizations:assign-sales-rep` could `POST` itself on
|
|
11
|
+
* to any organization and widen its own `allowedOrganizationIds` — measured at
|
|
12
|
+
* 201, `[orgA] → [orgA, orgB]`. No classification can save the write:
|
|
13
|
+
* `@OrgScoped` on this entity would be circular (D-259 category 2), and the id
|
|
14
|
+
* arrives from the path rather than from the actor, so no column filter reaches
|
|
15
|
+
* it. Nor is "the scoped role does not hold the code" a boundary —
|
|
16
|
+
* `PUT /admin/admin-roles/:code` upserts an arbitrary permission array onto any
|
|
17
|
+
* role, and the seed that grants this role its codes is a demo fixture.
|
|
18
|
+
*
|
|
19
|
+
* The refusal is therefore `mode: 'all'` / 403 and not the FR-008 not-found arm
|
|
20
|
+
* of the criterion: nothing about the organization's existence is being
|
|
21
|
+
* concealed, the actor is being told it may not write the graph at all. The
|
|
22
|
+
* idiom is `routes.admin.ts`'s credit-inheritance-mode handler, which already
|
|
23
|
+
* reaches for it on the weaker argument of a money-behaviour switch.
|
|
24
|
+
*
|
|
25
|
+
* It is categorical — the actor's *own* organization included. Assigning a
|
|
26
|
+
* second representative to an organization the actor holds still edits the graph
|
|
27
|
+
* that decides who holds what, and the graph is the boundary rather than an
|
|
28
|
+
* operation inside it.
|
|
29
|
+
*/
|
|
30
|
+
function requireUnscopedActor() {
|
|
31
|
+
const ctx = getTenantContext();
|
|
32
|
+
if (!ctx || ctx.mode !== 'all') {
|
|
33
|
+
throw new HttpError(403, ERROR_CODES.FORBIDDEN, 'Only a platform administrator can change sales-representative assignments.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function registerOrganizationsSalesRepRoutes(app, deps) {
|
|
37
|
+
const { emFactory, requireAdmin, salesRepAssignment, adminUsers } = deps;
|
|
38
|
+
const guard = requireAdmin('organizations:assign-sales-rep');
|
|
39
|
+
// GET /api/v1/admin/organizations/:organizationId/sales-reps
|
|
40
|
+
app.get('/api/v1/admin/organizations/:organizationId/sales-reps', { preHandler: guard }, async (request) => {
|
|
41
|
+
const em = emFactory();
|
|
42
|
+
const org = await em.findOne(Organization, { id: request.params.organizationId });
|
|
43
|
+
if (!org)
|
|
44
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Organization not found.');
|
|
45
|
+
const assignments = await salesRepAssignment.listForOrganization(org.id);
|
|
46
|
+
const adminIds = assignments.map((a) => a.adminUserId);
|
|
47
|
+
const admins = adminIds.length > 0 ? await adminUsers.findByIds(adminIds) : [];
|
|
48
|
+
const adminById = new Map(admins.map((a) => [a.id, a]));
|
|
49
|
+
return {
|
|
50
|
+
data: assignments.map((a) => {
|
|
51
|
+
const u = adminById.get(a.adminUserId);
|
|
52
|
+
return {
|
|
53
|
+
id: a.id,
|
|
54
|
+
organizationId: a.organizationId,
|
|
55
|
+
adminUserId: a.adminUserId,
|
|
56
|
+
displayName: u ? `${u.firstName} ${u.lastName}` : 'Unknown',
|
|
57
|
+
email: u?.email ?? '',
|
|
58
|
+
assignedAt: a.createdAt.toISOString(),
|
|
59
|
+
assignedByAdminUserId: a.assignedByAdminUserId ?? null,
|
|
60
|
+
};
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
// POST /api/v1/admin/organizations/:organizationId/sales-reps
|
|
65
|
+
app.post('/api/v1/admin/organizations/:organizationId/sales-reps', { preHandler: guard, schema: { body: assignSalesRepRequestSchema } }, async (request, reply) => {
|
|
66
|
+
requireUnscopedActor(); // D-260 — the graph is the boundary.
|
|
67
|
+
const body = assignSalesRepRequestSchema.parse(request.body);
|
|
68
|
+
const em = emFactory();
|
|
69
|
+
const org = await em.findOne(Organization, { id: request.params.organizationId });
|
|
70
|
+
if (!org)
|
|
71
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Organization not found.');
|
|
72
|
+
const adminUser = await adminUsers.findById(body.adminUserId);
|
|
73
|
+
if (!adminUser) {
|
|
74
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Admin user not found.');
|
|
75
|
+
}
|
|
76
|
+
const actor = request;
|
|
77
|
+
const assignedBy = actor.actor?.kind === 'admin'
|
|
78
|
+
? actor.actor.adminUserId ?? null
|
|
79
|
+
: actor.testActor?.kind === 'admin'
|
|
80
|
+
? actor.testActor.adminUserId ?? null
|
|
81
|
+
: null;
|
|
82
|
+
const created = await salesRepAssignment.assign({
|
|
83
|
+
organizationId: org.id,
|
|
84
|
+
adminUserId: adminUser.id,
|
|
85
|
+
assignedByAdminUserId: assignedBy,
|
|
86
|
+
});
|
|
87
|
+
reply.code(201);
|
|
88
|
+
return {
|
|
89
|
+
data: {
|
|
90
|
+
id: created.id,
|
|
91
|
+
organizationId: created.organizationId,
|
|
92
|
+
adminUserId: created.adminUserId,
|
|
93
|
+
displayName: `${adminUser.firstName} ${adminUser.lastName}`,
|
|
94
|
+
email: adminUser.email,
|
|
95
|
+
assignedAt: created.createdAt.toISOString(),
|
|
96
|
+
assignedByAdminUserId: created.assignedByAdminUserId ?? null,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
// DELETE /api/v1/admin/organizations/:organizationId/sales-reps/:adminUserId
|
|
101
|
+
app.delete('/api/v1/admin/organizations/:organizationId/sales-reps/:adminUserId', { preHandler: guard }, async (request, reply) => {
|
|
102
|
+
requireUnscopedActor(); // D-260 — the graph is the boundary.
|
|
103
|
+
const removed = await salesRepAssignment.unassign({
|
|
104
|
+
organizationId: request.params.organizationId,
|
|
105
|
+
adminUserId: request.params.adminUserId,
|
|
106
|
+
});
|
|
107
|
+
if (!removed) {
|
|
108
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Assignment not found.');
|
|
109
|
+
}
|
|
110
|
+
reply.code(204);
|
|
111
|
+
return null;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=routes.sales-reps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.sales-reps.js","sourceRoot":"","sources":["../../src/backend/routes.sales-reps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { OrganizationContextService } from './services/organization-context-service.js';
|
|
3
|
+
import type { OrganizationRestrictionService } from './services/organization-restriction-service.js';
|
|
4
|
+
export interface OrganizationsStorefrontDeps {
|
|
5
|
+
contextService: OrganizationContextService;
|
|
6
|
+
restrictionService: OrganizationRestrictionService;
|
|
7
|
+
resolveCustomerContext: (req: FastifyRequest) => {
|
|
8
|
+
customerAccountId: string;
|
|
9
|
+
/** Null for no-org Customer accounts (feature 026 US2). */
|
|
10
|
+
organizationId: string | null;
|
|
11
|
+
};
|
|
12
|
+
requireCustomer: (req: FastifyRequest, reply: unknown) => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Storefront-side organization endpoints introduced by feature 026 US4.
|
|
16
|
+
*
|
|
17
|
+
* POST /api/v1/storefront/checkout/preflight
|
|
18
|
+
* Called by the checkout SSR route. Returns 200 with the resolved
|
|
19
|
+
* payment + delivery method allow-list IDs and assigned warehouse IDs
|
|
20
|
+
* when the Customer's Organization is active; 423 with status +
|
|
21
|
+
* localized reason when it cannot transact; 200 with empty allow-lists
|
|
22
|
+
* for no-org Customer accounts (they get the platform defaults).
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerOrganizationsStorefrontRoutes(app: FastifyInstance, deps: OrganizationsStorefrontDeps): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=routes.storefront.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.storefront.d.ts","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4CAA4C,CAAC;AAC7F,OAAO,KAAK,EACV,8BAA8B,EAC/B,MAAM,gDAAgD,CAAC;AAExD,MAAM,WAAW,2BAA2B;IAC1C,cAAc,EAAE,0BAA0B,CAAC;IAC3C,kBAAkB,EAAE,8BAA8B,CAAC;IACnD,sBAAsB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAC/C,iBAAiB,EAAE,MAAM,CAAC;QAC1B,2DAA2D;QAC3D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;IACF,eAAe,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAED;;;;;;;;;GASG;AACH,wBAAsB,qCAAqC,CACzD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
/**
|
|
4
|
+
* Storefront-side organization endpoints introduced by feature 026 US4.
|
|
5
|
+
*
|
|
6
|
+
* POST /api/v1/storefront/checkout/preflight
|
|
7
|
+
* Called by the checkout SSR route. Returns 200 with the resolved
|
|
8
|
+
* payment + delivery method allow-list IDs and assigned warehouse IDs
|
|
9
|
+
* when the Customer's Organization is active; 423 with status +
|
|
10
|
+
* localized reason when it cannot transact; 200 with empty allow-lists
|
|
11
|
+
* for no-org Customer accounts (they get the platform defaults).
|
|
12
|
+
*/
|
|
13
|
+
export async function registerOrganizationsStorefrontRoutes(app, deps) {
|
|
14
|
+
app.post('/api/v1/storefront/checkout/preflight', { preHandler: deps.requireCustomer }, async (request, reply) => {
|
|
15
|
+
const ctx = deps.resolveCustomerContext(request);
|
|
16
|
+
if (!ctx.organizationId) {
|
|
17
|
+
// No-org Customer — platform defaults apply (feature 026 US2).
|
|
18
|
+
reply.status(200);
|
|
19
|
+
return {
|
|
20
|
+
canTransact: true,
|
|
21
|
+
allowedPaymentMethodIds: [],
|
|
22
|
+
allowedDeliveryMethodIds: [],
|
|
23
|
+
assignedWarehouseIds: [],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const org = await deps.contextService.loadEffectiveOrganization(ctx.organizationId);
|
|
27
|
+
if (!org) {
|
|
28
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Organization not found.');
|
|
29
|
+
}
|
|
30
|
+
if (org.status !== 'active') {
|
|
31
|
+
reply.status(423);
|
|
32
|
+
return {
|
|
33
|
+
canTransact: false,
|
|
34
|
+
status: org.status,
|
|
35
|
+
reason: describeStatus(org.status),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const allowLists = await deps.restrictionService.readAllowLists(org.id);
|
|
39
|
+
reply.status(200);
|
|
40
|
+
return {
|
|
41
|
+
canTransact: true,
|
|
42
|
+
allowedPaymentMethodIds: allowLists.paymentMethodIds,
|
|
43
|
+
allowedDeliveryMethodIds: allowLists.deliveryMethodIds,
|
|
44
|
+
assignedWarehouseIds: allowLists.warehouseIds,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Customer-safe localization of the moderation status. The storefront
|
|
50
|
+
* displays this string verbatim; admin-only details (block-reason text)
|
|
51
|
+
* are intentionally NOT exposed here.
|
|
52
|
+
*/
|
|
53
|
+
function describeStatus(status) {
|
|
54
|
+
switch (status) {
|
|
55
|
+
case 'pending_verification':
|
|
56
|
+
return 'Twoja Organizacja oczekuje na weryfikację. Zamówienia będą dostępne po jej zakończeniu.';
|
|
57
|
+
case 'blocked':
|
|
58
|
+
return 'Składanie Zamówień jest obecnie wstrzymane. Prosimy o kontakt z opiekunem konta.';
|
|
59
|
+
case 'rejected':
|
|
60
|
+
return 'Rejestracja Twojej Organizacji została odrzucona. Prosimy o kontakt z administracją platformy.';
|
|
61
|
+
default:
|
|
62
|
+
return 'Składanie Zamówień obecnie niedostępne.';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=routes.storefront.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.storefront.js","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAiB3D;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,qCAAqC,CACzD,GAAoB,EACpB,IAAiC;IAEjC,GAAG,CAAC,IAAI,CACN,uCAAuC,EACvC,EAAE,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,EACpC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEjD,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;YACxB,+DAA+D;YAC/D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClB,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,uBAAuB,EAAE,EAAE;gBAC3B,wBAAwB,EAAE,EAAE;gBAC5B,oBAAoB,EAAE,EAAE;aACzB,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACpF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC5B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClB,OAAO;gBACL,WAAW,EAAE,KAAK;gBAClB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC;aACnC,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,uBAAuB,EAAE,UAAU,CAAC,gBAAgB;YACpD,wBAAwB,EAAE,UAAU,CAAC,iBAAiB;YACtD,oBAAoB,EAAE,UAAU,CAAC,YAAY;SAC9C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CACrB,MAAgE;IAEhE,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,sBAAsB;YACzB,OAAO,yFAAyF,CAAC;QACnG,KAAK,SAAS;YACZ,OAAO,kFAAkF,CAAC;QAC5F,KAAK,UAAU;YACb,OAAO,gGAAgG,CAAC;QAC1G;YACE,OAAO,yCAAyC,CAAC;IACrD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Zod schemas used by feature 026 — moderation, restrictions, and VAT
|
|
4
|
+
* validation. These live in the module (not in `@endora-commerce/contracts`) because
|
|
5
|
+
* they describe internal route bodies; the public contract for the
|
|
6
|
+
* Organization status enum is exported from `@endora-commerce/contracts` and
|
|
7
|
+
* re-imported here.
|
|
8
|
+
*/
|
|
9
|
+
export declare const organizationStatusSchema: z.ZodEnum<{
|
|
10
|
+
pending_verification: "pending_verification";
|
|
11
|
+
active: "active";
|
|
12
|
+
blocked: "blocked";
|
|
13
|
+
rejected: "rejected";
|
|
14
|
+
}>;
|
|
15
|
+
export type OrganizationStatusInput = z.infer<typeof organizationStatusSchema>;
|
|
16
|
+
/** Tax ID — uppercased + stripped of whitespace and dashes at the Zod boundary. */
|
|
17
|
+
export declare const taxIdSchema: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
18
|
+
export declare const approveOrganizationSchema: z.ZodObject<{
|
|
19
|
+
expectedVersion: z.ZodNumber;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export type ApproveOrganizationInput = z.infer<typeof approveOrganizationSchema>;
|
|
22
|
+
export declare const rejectOrganizationSchema: z.ZodObject<{
|
|
23
|
+
expectedVersion: z.ZodNumber;
|
|
24
|
+
reason: z.ZodString;
|
|
25
|
+
notifyCustomerEmail: z.ZodDefault<z.ZodBoolean>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export type RejectOrganizationInput = z.infer<typeof rejectOrganizationSchema>;
|
|
28
|
+
export declare const blockOrganizationSchema: z.ZodObject<{
|
|
29
|
+
expectedVersion: z.ZodNumber;
|
|
30
|
+
reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
export type BlockOrganizationInput = z.infer<typeof blockOrganizationSchema>;
|
|
33
|
+
export declare const unblockOrganizationSchema: z.ZodObject<{
|
|
34
|
+
expectedVersion: z.ZodNumber;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
export type UnblockOrganizationInput = z.infer<typeof unblockOrganizationSchema>;
|
|
37
|
+
export declare const replaceOrgRestrictionsSchema: z.ZodObject<{
|
|
38
|
+
expectedVersion: z.ZodNumber;
|
|
39
|
+
paymentMethodIds: z.ZodArray<z.ZodString>;
|
|
40
|
+
deliveryMethodIds: z.ZodArray<z.ZodString>;
|
|
41
|
+
warehouseIds: z.ZodArray<z.ZodString>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
export type ReplaceOrgRestrictionsInput = z.infer<typeof replaceOrgRestrictionsSchema>;
|
|
44
|
+
export declare const patchOrgRestrictionsSchema: z.ZodObject<{
|
|
45
|
+
expectedVersion: z.ZodNumber;
|
|
46
|
+
add: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
47
|
+
remove: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
export type PatchOrgRestrictionsInput = z.infer<typeof patchOrgRestrictionsSchema>;
|
|
50
|
+
export declare const triggerVatValidationSchema: z.ZodObject<{
|
|
51
|
+
providerHint: z.ZodDefault<z.ZodEnum<{
|
|
52
|
+
vies: "vies";
|
|
53
|
+
mf_pl: "mf_pl";
|
|
54
|
+
auto: "auto";
|
|
55
|
+
}>>;
|
|
56
|
+
applyAutoFill: z.ZodDefault<z.ZodBoolean>;
|
|
57
|
+
}, z.core.$strip>;
|
|
58
|
+
export type TriggerVatValidationInput = z.infer<typeof triggerVatValidationSchema>;
|
|
59
|
+
//# sourceMappingURL=organization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organization.d.ts","sourceRoot":"","sources":["../../../src/backend/schemas/organization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AAEH,eAAO,MAAM,wBAAwB;;;;;EAKnC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE/E,mFAAmF;AACnF,eAAO,MAAM,WAAW,wDAImC,CAAC;AAI5D,eAAO,MAAM,yBAAyB;;iBAEpC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEjF,eAAO,MAAM,wBAAwB;;;;iBAInC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE/E,eAAO,MAAM,uBAAuB;;;iBAGlC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE7E,eAAO,MAAM,yBAAyB;;iBAEpC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAMjF,eAAO,MAAM,4BAA4B;;;;;iBAKvC,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAEvF,eAAO,MAAM,0BAA0B;;;;iBAIrC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAInF,eAAO,MAAM,0BAA0B;;;;;;;iBAGrC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Zod schemas used by feature 026 — moderation, restrictions, and VAT
|
|
4
|
+
* validation. These live in the module (not in `@endora-commerce/contracts`) because
|
|
5
|
+
* they describe internal route bodies; the public contract for the
|
|
6
|
+
* Organization status enum is exported from `@endora-commerce/contracts` and
|
|
7
|
+
* re-imported here.
|
|
8
|
+
*/
|
|
9
|
+
export const organizationStatusSchema = z.enum([
|
|
10
|
+
'pending_verification',
|
|
11
|
+
'active',
|
|
12
|
+
'blocked',
|
|
13
|
+
'rejected',
|
|
14
|
+
]);
|
|
15
|
+
/** Tax ID — uppercased + stripped of whitespace and dashes at the Zod boundary. */
|
|
16
|
+
export const taxIdSchema = z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1)
|
|
19
|
+
.max(64)
|
|
20
|
+
.transform((s) => s.replace(/[\s-]+/g, '').toUpperCase());
|
|
21
|
+
// ── Moderation ────────────────────────────────────────────────────────────
|
|
22
|
+
export const approveOrganizationSchema = z.object({
|
|
23
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
24
|
+
});
|
|
25
|
+
export const rejectOrganizationSchema = z.object({
|
|
26
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
27
|
+
reason: z.string().min(1).max(2000),
|
|
28
|
+
notifyCustomerEmail: z.boolean().default(true),
|
|
29
|
+
});
|
|
30
|
+
export const blockOrganizationSchema = z.object({
|
|
31
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
32
|
+
reason: z.string().max(2000).nullable().optional(),
|
|
33
|
+
});
|
|
34
|
+
export const unblockOrganizationSchema = z.object({
|
|
35
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
36
|
+
});
|
|
37
|
+
// ── Restrictions (per-Organization allow-lists) ───────────────────────────
|
|
38
|
+
const uuidList = z.array(z.string().uuid()).max(500);
|
|
39
|
+
export const replaceOrgRestrictionsSchema = z.object({
|
|
40
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
41
|
+
paymentMethodIds: uuidList,
|
|
42
|
+
deliveryMethodIds: uuidList,
|
|
43
|
+
warehouseIds: uuidList,
|
|
44
|
+
});
|
|
45
|
+
export const patchOrgRestrictionsSchema = z.object({
|
|
46
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
47
|
+
add: uuidList.optional(),
|
|
48
|
+
remove: uuidList.optional(),
|
|
49
|
+
});
|
|
50
|
+
// ── VAT validation ────────────────────────────────────────────────────────
|
|
51
|
+
export const triggerVatValidationSchema = z.object({
|
|
52
|
+
providerHint: z.enum(['auto', 'vies', 'mf_pl']).default('auto'),
|
|
53
|
+
applyAutoFill: z.boolean().default(false),
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=organization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organization.js","sourceRoot":"","sources":["../../../src/backend/schemas/organization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC7C,sBAAsB;IACtB,QAAQ;IACR,SAAS;IACT,UAAU;CACX,CAAC,CAAC;AAGH,mFAAmF;AACnF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC;KACzB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,EAAE,CAAC;KACP,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAE5D,6EAA6E;AAE7E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;CAChD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC/C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IACnC,mBAAmB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC/C,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC/C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;CAChD,CAAC,CAAC;AAGH,6EAA6E;AAE7E,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC/C,gBAAgB,EAAE,QAAQ;IAC1B,iBAAiB,EAAE,QAAQ;IAC3B,YAAY,EAAE,QAAQ;CACvB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC/C,GAAG,EAAE,QAAQ,CAAC,QAAQ,EAAE;IACxB,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAGH,6EAA6E;AAE7E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IAC/D,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAC1C,CAAC,CAAC"}
|