@open-mercato/core 0.7.1-develop.7153.1.7145d295e6 → 0.7.1-develop.7170.1.d95074d7ba

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.
Files changed (116) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +2 -2
  3. package/dist/modules/auth/commands/users.js +14 -1
  4. package/dist/modules/auth/commands/users.js.map +2 -2
  5. package/dist/modules/auth/data/entities.js +2 -2
  6. package/dist/modules/auth/data/entities.js.map +2 -2
  7. package/dist/modules/auth/lib/backendChrome.js +26 -3
  8. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  9. package/dist/modules/auth/lib/profile-sections.js +1 -1
  10. package/dist/modules/auth/lib/profile-sections.js.map +2 -2
  11. package/dist/modules/auth/migrations/Migration20260907120000_auth.js +19 -0
  12. package/dist/modules/auth/migrations/Migration20260907120000_auth.js.map +7 -0
  13. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +2 -2
  14. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +1 -1
  15. package/dist/modules/customer_accounts/api/admin/demo-accounts.js +118 -0
  16. package/dist/modules/customer_accounts/api/admin/demo-accounts.js.map +7 -0
  17. package/dist/modules/customer_accounts/api/invitations/accept.js +23 -6
  18. package/dist/modules/customer_accounts/api/invitations/accept.js.map +2 -2
  19. package/dist/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.js +10 -13
  20. package/dist/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.js.map +2 -2
  21. package/dist/modules/customer_accounts/backend/customer_accounts/useDemoPortalAccounts.js +28 -0
  22. package/dist/modules/customer_accounts/backend/customer_accounts/useDemoPortalAccounts.js.map +7 -0
  23. package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +6 -1
  24. package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
  25. package/dist/modules/customer_accounts/data/entities.js +4 -1
  26. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  27. package/dist/modules/customer_accounts/lib/exampleAccounts.js +9 -0
  28. package/dist/modules/customer_accounts/lib/exampleAccounts.js.map +7 -0
  29. package/dist/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.js +16 -0
  30. package/dist/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.js.map +7 -0
  31. package/dist/modules/customer_accounts/services/customerInvitationService.js +53 -6
  32. package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
  33. package/dist/modules/customer_accounts/setup.js +2 -6
  34. package/dist/modules/customer_accounts/setup.js.map +2 -2
  35. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +55 -40
  36. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  37. package/dist/modules/customers/commands/people.js +16 -1
  38. package/dist/modules/customers/commands/people.js.map +2 -2
  39. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +18 -36
  40. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  41. package/dist/modules/data_sync/lib/sync-schedule-service.js +25 -23
  42. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  43. package/dist/modules/entities/api/records.js +18 -15
  44. package/dist/modules/entities/api/records.js.map +3 -3
  45. package/dist/modules/entities/lib/scoped-field-defs.js +52 -0
  46. package/dist/modules/entities/lib/scoped-field-defs.js.map +7 -0
  47. package/dist/modules/entities/lib/validation.js +4 -35
  48. package/dist/modules/entities/lib/validation.js.map +2 -2
  49. package/dist/modules/integrations/backend/integrations/[id]/page.js +32 -54
  50. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  51. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +12 -29
  52. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  53. package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +2 -2
  54. package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +1 -1
  55. package/dist/modules/progress/lib/progressServiceImpl.js +14 -3
  56. package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
  57. package/dist/modules/sales/backend/sales/documents/[id]/page.js +9 -9
  58. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  59. package/dist/modules/sales/components/documents/LineItemDialog.js +50 -10
  60. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  61. package/dist/modules/sales/components/documents/PaymentsSection.js +8 -3
  62. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  63. package/dist/modules/sales/components/documents/ReturnEditDialog.js +2 -7
  64. package/dist/modules/sales/components/documents/ReturnEditDialog.js.map +2 -2
  65. package/dist/modules/sales/components/documents/ReturnsSection.js +2 -7
  66. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  67. package/dist/modules/sales/components/documents/ShipmentsSection.js +3 -8
  68. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  69. package/package.json +7 -7
  70. package/src/modules/auth/commands/users.ts +14 -1
  71. package/src/modules/auth/data/entities.ts +2 -2
  72. package/src/modules/auth/lib/backendChrome.tsx +46 -2
  73. package/src/modules/auth/lib/profile-sections.tsx +14 -3
  74. package/src/modules/auth/migrations/.snapshot-open-mercato.json +4 -2
  75. package/src/modules/auth/migrations/Migration20260907120000_auth.ts +21 -0
  76. package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +2 -2
  77. package/src/modules/customer_accounts/AGENTS.md +3 -1
  78. package/src/modules/customer_accounts/api/admin/demo-accounts.ts +149 -0
  79. package/src/modules/customer_accounts/api/invitations/accept.ts +23 -6
  80. package/src/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.tsx +43 -41
  81. package/src/modules/customer_accounts/backend/customer_accounts/useDemoPortalAccounts.ts +42 -0
  82. package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +10 -3
  83. package/src/modules/customer_accounts/data/entities.ts +5 -1
  84. package/src/modules/customer_accounts/i18n/de.json +4 -2
  85. package/src/modules/customer_accounts/i18n/en.json +4 -2
  86. package/src/modules/customer_accounts/i18n/es.json +4 -2
  87. package/src/modules/customer_accounts/i18n/ko.json +4 -2
  88. package/src/modules/customer_accounts/i18n/pl.json +4 -2
  89. package/src/modules/customer_accounts/lib/exampleAccounts.ts +18 -0
  90. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +5 -7
  91. package/src/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.ts +23 -0
  92. package/src/modules/customer_accounts/services/customerInvitationService.ts +89 -5
  93. package/src/modules/customer_accounts/setup.ts +2 -6
  94. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +74 -41
  95. package/src/modules/customers/commands/people.ts +15 -0
  96. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +19 -44
  97. package/src/modules/data_sync/lib/sync-schedule-service.ts +32 -26
  98. package/src/modules/entities/api/records.ts +24 -15
  99. package/src/modules/entities/lib/scoped-field-defs.ts +76 -0
  100. package/src/modules/entities/lib/validation.ts +5 -45
  101. package/src/modules/integrations/backend/integrations/[id]/page.tsx +33 -62
  102. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +13 -37
  103. package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +2 -2
  104. package/src/modules/progress/AGENTS.md +1 -1
  105. package/src/modules/progress/lib/progressServiceImpl.ts +26 -3
  106. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +19 -25
  107. package/src/modules/sales/components/documents/LineItemDialog.tsx +53 -9
  108. package/src/modules/sales/components/documents/PaymentsSection.tsx +8 -8
  109. package/src/modules/sales/components/documents/ReturnEditDialog.tsx +2 -8
  110. package/src/modules/sales/components/documents/ReturnsSection.tsx +8 -7
  111. package/src/modules/sales/components/documents/ShipmentsSection.tsx +9 -8
  112. package/src/modules/sales/i18n/de.json +2 -0
  113. package/src/modules/sales/i18n/en.json +2 -0
  114. package/src/modules/sales/i18n/es.json +2 -0
  115. package/src/modules/sales/i18n/ko.json +2 -0
  116. package/src/modules/sales/i18n/pl.json +2 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customer_accounts/data/entities.ts"],
4
- "sourcesContent": ["import { Check, Entity, Enum, Index, ManyToOne, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\nexport type DomainProvider = 'traefik'\nexport type DomainStatus = 'pending' | 'verified' | 'active' | 'dns_failed' | 'tls_failed'\n\nexport const DOMAIN_PROVIDERS: readonly DomainProvider[] = ['traefik'] as const\nexport const DOMAIN_STATUSES: readonly DomainStatus[] = [\n 'pending',\n 'verified',\n 'active',\n 'dns_failed',\n 'tls_failed',\n] as const\n\n@Entity({ tableName: 'customer_users' })\n@Unique({ properties: ['tenantId', 'emailHash'], name: 'customer_users_tenant_email_hash_uniq' })\nexport class CustomerUser {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text' })\n email!: string\n\n @Property({ name: 'email_hash', type: 'text' })\n @Index({ name: 'customer_users_email_hash_idx' })\n emailHash!: string\n\n @Property({ name: 'password_hash', type: 'text', nullable: true })\n passwordHash?: string | null\n\n @Property({ name: 'display_name', type: 'text' })\n displayName!: string\n\n @Property({ name: 'email_verified_at', type: Date, nullable: true })\n emailVerifiedAt?: Date | null\n\n @Property({ name: 'failed_login_attempts', type: 'int', default: 0 })\n failedLoginAttempts: number = 0\n\n @Property({ name: 'locked_until', type: Date, nullable: true })\n lockedUntil?: Date | null\n\n @Property({ name: 'last_login_at', type: Date, nullable: true })\n lastLoginAt?: Date | null\n\n @Property({ name: 'sessions_revoked_at', type: Date, nullable: true })\n sessionsRevokedAt?: Date | null\n\n @Property({ name: 'person_entity_id', type: 'uuid', nullable: true })\n @Index({ name: 'customer_users_person_entity_idx' })\n personEntityId?: string | null\n\n @Property({ name: 'customer_entity_id', type: 'uuid', nullable: true })\n @Index({ name: 'customer_users_customer_entity_idx' })\n customerEntityId?: string | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_roles' })\n@Unique({ properties: ['tenantId', 'slug'], name: 'customer_roles_tenant_slug_uniq' })\nexport class CustomerRole {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text' })\n name!: string\n\n @Property({ type: 'text' })\n slug!: string\n\n @Property({ type: 'text', nullable: true })\n description?: string | null\n\n @Property({ name: 'is_default', type: 'boolean', default: false })\n isDefault: boolean = false\n\n @Property({ name: 'is_system', type: 'boolean', default: false })\n isSystem: boolean = false\n\n @Property({ name: 'customer_assignable', type: 'boolean', default: false })\n customerAssignable: boolean = false\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_role_acls' })\n@Unique({ properties: ['role', 'tenantId'], name: 'customer_role_acls_role_tenant_uniq' })\nexport class CustomerRoleAcl {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerRole)\n role!: CustomerRole\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'features_json', type: 'json', nullable: true })\n featuresJson?: string[] | null\n\n @Property({ name: 'is_portal_admin', type: 'boolean', default: false })\n isPortalAdmin: boolean = false\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_roles' })\n@Unique({ properties: ['user', 'role'], name: 'customer_user_roles_user_role_uniq' })\nexport class CustomerUserRole {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @ManyToOne(() => CustomerRole)\n role!: CustomerRole\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_acls' })\n@Unique({ properties: ['user', 'tenantId'], name: 'customer_user_acls_user_tenant_uniq' })\nexport class CustomerUserAcl {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'features_json', type: 'json', nullable: true })\n featuresJson?: string[] | null\n\n @Property({ name: 'is_portal_admin', type: 'boolean', default: false })\n isPortalAdmin: boolean = false\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_sessions' })\n@Index({ properties: ['tokenHash'], name: 'customer_user_sessions_token_hash_idx' })\nexport class CustomerUserSession {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ name: 'token_hash', type: 'text' })\n tokenHash!: string\n\n @Property({ name: 'ip_address', type: 'text', nullable: true })\n ipAddress?: string | null\n\n @Property({ name: 'user_agent', type: 'text', nullable: true })\n userAgent?: string | null\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'last_used_at', type: Date, nullable: true })\n lastUsedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_email_verifications' })\n@Index({ properties: ['token'], name: 'customer_user_email_verifications_token_idx' })\nexport class CustomerUserEmailVerification {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ type: 'text' })\n token!: string\n\n @Property({ type: 'text', default: 'email_verification' })\n purpose: string = 'email_verification'\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'customer_user_password_resets' })\n@Index({ properties: ['token'], name: 'customer_user_password_resets_token_idx' })\nexport class CustomerUserPasswordReset {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ type: 'text' })\n token!: string\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'customer_user_invitations' })\n@Index({ properties: ['token'], name: 'customer_user_invitations_token_idx' })\n@Index({ properties: ['tenantId', 'emailHash'], name: 'customer_user_invitations_tenant_email_hash_idx' })\nexport class CustomerUserInvitation {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text' })\n email!: string\n\n @Property({ name: 'email_hash', type: 'text' })\n emailHash!: string\n\n @Property({ type: 'text' })\n token!: string\n\n @Property({ name: 'customer_entity_id', type: 'uuid', nullable: true })\n customerEntityId?: string | null\n\n @Property({ name: 'person_entity_id', type: 'uuid', nullable: true })\n personEntityId?: string | null\n\n @Property({ name: 'role_ids_json', type: 'json', nullable: true })\n roleIdsJson?: string[] | null\n\n @Property({ name: 'invited_by_user_id', type: 'uuid', nullable: true })\n invitedByUserId?: string | null\n\n @Property({ name: 'invited_by_customer_user_id', type: 'uuid', nullable: true })\n invitedByCustomerUserId?: string | null\n\n @Property({ name: 'display_name', type: 'text', nullable: true })\n displayName?: string | null\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'accepted_at', type: Date, nullable: true })\n acceptedAt?: Date | null\n\n @Property({ name: 'cancelled_at', type: Date, nullable: true })\n cancelledAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'domain_mappings' })\n@Unique({ properties: ['hostname'], name: 'domain_mappings_hostname_unique' })\n@Index({ properties: ['organizationId'], name: 'domain_mappings_organization_id_idx' })\n@Index({ properties: ['tenantId'], name: 'domain_mappings_tenant_id_idx' })\n@Unique({\n name: 'domain_mappings_replaces_domain_id_unique',\n expression:\n `create unique index \"domain_mappings_replaces_domain_id_unique\" on \"domain_mappings\" (\"replaces_domain_id\") where \"replaces_domain_id\" is not null`,\n})\n@Index({\n name: 'domain_mappings_pending_verification_idx',\n expression:\n `create index \"domain_mappings_pending_verification_idx\" on \"domain_mappings\" (\"status\", \"last_dns_check_at\") where \"status\" in ('pending', 'dns_failed')`,\n})\n@Index({\n name: 'domain_mappings_pending_tls_idx',\n expression:\n `create index \"domain_mappings_pending_tls_idx\" on \"domain_mappings\" (\"status\", \"updated_at\") where \"status\" in ('verified', 'tls_failed')`,\n})\n@Check({\n name: 'domain_mappings_hostname_normalized_chk',\n expression: `\"hostname\" = lower(\"hostname\") and \"hostname\" not like '%.'`,\n})\nexport class DomainMapping {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ type: 'text' })\n hostname!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @ManyToOne(() => DomainMapping, { fieldName: 'replaces_domain_id', nullable: true })\n replacesDomain?: DomainMapping | null\n\n @Enum({ items: () => DOMAIN_PROVIDERS as unknown as string[], type: 'text', name: 'provider' })\n provider: DomainProvider = 'traefik'\n\n @Enum({ items: () => DOMAIN_STATUSES as unknown as string[], type: 'text', name: 'status' })\n status: DomainStatus = 'pending'\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'last_dns_check_at', type: Date, nullable: true })\n lastDnsCheckAt?: Date | null\n\n @Property({ name: 'dns_failure_reason', type: 'text', nullable: true, length: 500 })\n dnsFailureReason?: string | null\n\n @Property({ name: 'tls_failure_reason', type: 'text', nullable: true, length: 500 })\n tlsFailureReason?: string | null\n\n @Property({ name: 'tls_retry_count', type: 'int', default: 0 })\n tlsRetryCount: number = 0\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date | null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Check, Entity, Enum, Index, ManyToOne, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\nexport type DomainProvider = 'traefik'\nexport type DomainStatus = 'pending' | 'verified' | 'active' | 'dns_failed' | 'tls_failed'\n\nexport const DOMAIN_PROVIDERS: readonly DomainProvider[] = ['traefik'] as const\nexport const DOMAIN_STATUSES: readonly DomainStatus[] = [\n 'pending',\n 'verified',\n 'active',\n 'dns_failed',\n 'tls_failed',\n] as const\n\n@Entity({ tableName: 'customer_users' })\n@Index({\n name: 'customer_users_tenant_email_hash_uniq',\n expression:\n 'create unique index \"customer_users_tenant_email_hash_uniq\" on \"customer_users\" (\"tenant_id\", \"email_hash\") where \"deleted_at\" is null',\n})\nexport class CustomerUser {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text' })\n email!: string\n\n @Property({ name: 'email_hash', type: 'text' })\n @Index({ name: 'customer_users_email_hash_idx' })\n emailHash!: string\n\n @Property({ name: 'password_hash', type: 'text', nullable: true })\n passwordHash?: string | null\n\n @Property({ name: 'display_name', type: 'text' })\n displayName!: string\n\n @Property({ name: 'email_verified_at', type: Date, nullable: true })\n emailVerifiedAt?: Date | null\n\n @Property({ name: 'failed_login_attempts', type: 'int', default: 0 })\n failedLoginAttempts: number = 0\n\n @Property({ name: 'locked_until', type: Date, nullable: true })\n lockedUntil?: Date | null\n\n @Property({ name: 'last_login_at', type: Date, nullable: true })\n lastLoginAt?: Date | null\n\n @Property({ name: 'sessions_revoked_at', type: Date, nullable: true })\n sessionsRevokedAt?: Date | null\n\n @Property({ name: 'person_entity_id', type: 'uuid', nullable: true })\n @Index({ name: 'customer_users_person_entity_idx' })\n personEntityId?: string | null\n\n @Property({ name: 'customer_entity_id', type: 'uuid', nullable: true })\n @Index({ name: 'customer_users_customer_entity_idx' })\n customerEntityId?: string | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_roles' })\n@Unique({ properties: ['tenantId', 'slug'], name: 'customer_roles_tenant_slug_uniq' })\nexport class CustomerRole {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text' })\n name!: string\n\n @Property({ type: 'text' })\n slug!: string\n\n @Property({ type: 'text', nullable: true })\n description?: string | null\n\n @Property({ name: 'is_default', type: 'boolean', default: false })\n isDefault: boolean = false\n\n @Property({ name: 'is_system', type: 'boolean', default: false })\n isSystem: boolean = false\n\n @Property({ name: 'customer_assignable', type: 'boolean', default: false })\n customerAssignable: boolean = false\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_role_acls' })\n@Unique({ properties: ['role', 'tenantId'], name: 'customer_role_acls_role_tenant_uniq' })\nexport class CustomerRoleAcl {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerRole)\n role!: CustomerRole\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'features_json', type: 'json', nullable: true })\n featuresJson?: string[] | null\n\n @Property({ name: 'is_portal_admin', type: 'boolean', default: false })\n isPortalAdmin: boolean = false\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_roles' })\n@Unique({ properties: ['user', 'role'], name: 'customer_user_roles_user_role_uniq' })\nexport class CustomerUserRole {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @ManyToOne(() => CustomerRole)\n role!: CustomerRole\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_acls' })\n@Unique({ properties: ['user', 'tenantId'], name: 'customer_user_acls_user_tenant_uniq' })\nexport class CustomerUserAcl {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'features_json', type: 'json', nullable: true })\n featuresJson?: string[] | null\n\n @Property({ name: 'is_portal_admin', type: 'boolean', default: false })\n isPortalAdmin: boolean = false\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_sessions' })\n@Index({ properties: ['tokenHash'], name: 'customer_user_sessions_token_hash_idx' })\nexport class CustomerUserSession {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ name: 'token_hash', type: 'text' })\n tokenHash!: string\n\n @Property({ name: 'ip_address', type: 'text', nullable: true })\n ipAddress?: string | null\n\n @Property({ name: 'user_agent', type: 'text', nullable: true })\n userAgent?: string | null\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'last_used_at', type: Date, nullable: true })\n lastUsedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'customer_user_email_verifications' })\n@Index({ properties: ['token'], name: 'customer_user_email_verifications_token_idx' })\nexport class CustomerUserEmailVerification {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ type: 'text' })\n token!: string\n\n @Property({ type: 'text', default: 'email_verification' })\n purpose: string = 'email_verification'\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'customer_user_password_resets' })\n@Index({ properties: ['token'], name: 'customer_user_password_resets_token_idx' })\nexport class CustomerUserPasswordReset {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @ManyToOne(() => CustomerUser)\n user!: CustomerUser\n\n @Property({ type: 'text' })\n token!: string\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'customer_user_invitations' })\n@Index({ properties: ['token'], name: 'customer_user_invitations_token_idx' })\n@Index({ properties: ['tenantId', 'emailHash'], name: 'customer_user_invitations_tenant_email_hash_idx' })\nexport class CustomerUserInvitation {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text' })\n email!: string\n\n @Property({ name: 'email_hash', type: 'text' })\n emailHash!: string\n\n @Property({ type: 'text' })\n token!: string\n\n @Property({ name: 'customer_entity_id', type: 'uuid', nullable: true })\n customerEntityId?: string | null\n\n @Property({ name: 'person_entity_id', type: 'uuid', nullable: true })\n personEntityId?: string | null\n\n @Property({ name: 'role_ids_json', type: 'json', nullable: true })\n roleIdsJson?: string[] | null\n\n @Property({ name: 'invited_by_user_id', type: 'uuid', nullable: true })\n invitedByUserId?: string | null\n\n @Property({ name: 'invited_by_customer_user_id', type: 'uuid', nullable: true })\n invitedByCustomerUserId?: string | null\n\n @Property({ name: 'display_name', type: 'text', nullable: true })\n displayName?: string | null\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'accepted_at', type: Date, nullable: true })\n acceptedAt?: Date | null\n\n @Property({ name: 'cancelled_at', type: Date, nullable: true })\n cancelledAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'domain_mappings' })\n@Unique({ properties: ['hostname'], name: 'domain_mappings_hostname_unique' })\n@Index({ properties: ['organizationId'], name: 'domain_mappings_organization_id_idx' })\n@Index({ properties: ['tenantId'], name: 'domain_mappings_tenant_id_idx' })\n@Unique({\n name: 'domain_mappings_replaces_domain_id_unique',\n expression:\n `create unique index \"domain_mappings_replaces_domain_id_unique\" on \"domain_mappings\" (\"replaces_domain_id\") where \"replaces_domain_id\" is not null`,\n})\n@Index({\n name: 'domain_mappings_pending_verification_idx',\n expression:\n `create index \"domain_mappings_pending_verification_idx\" on \"domain_mappings\" (\"status\", \"last_dns_check_at\") where \"status\" in ('pending', 'dns_failed')`,\n})\n@Index({\n name: 'domain_mappings_pending_tls_idx',\n expression:\n `create index \"domain_mappings_pending_tls_idx\" on \"domain_mappings\" (\"status\", \"updated_at\") where \"status\" in ('verified', 'tls_failed')`,\n})\n@Check({\n name: 'domain_mappings_hostname_normalized_chk',\n expression: `\"hostname\" = lower(\"hostname\") and \"hostname\" not like '%.'`,\n})\nexport class DomainMapping {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ type: 'text' })\n hostname!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @ManyToOne(() => DomainMapping, { fieldName: 'replaces_domain_id', nullable: true })\n replacesDomain?: DomainMapping | null\n\n @Enum({ items: () => DOMAIN_PROVIDERS as unknown as string[], type: 'text', name: 'provider' })\n provider: DomainProvider = 'traefik'\n\n @Enum({ items: () => DOMAIN_STATUSES as unknown as string[], type: 'text', name: 'status' })\n status: DomainStatus = 'pending'\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'last_dns_check_at', type: Date, nullable: true })\n lastDnsCheckAt?: Date | null\n\n @Property({ name: 'dns_failure_reason', type: 'text', nullable: true, length: 500 })\n dnsFailureReason?: string | null\n\n @Property({ name: 'tls_failure_reason', type: 'text', nullable: true, length: 500 })\n tlsFailureReason?: string | null\n\n @Property({ name: 'tls_retry_count', type: 'int', default: 0 })\n tlsRetryCount: number = 0\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date(), nullable: true })\n updatedAt?: Date | null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,9 @@
1
+ const EXAMPLE_PORTAL_ACCOUNTS = [
2
+ { email: "alice.johnson@example.com", displayName: "Alice Johnson", password: "Password123!", roleSlug: "portal_admin" },
3
+ { email: "bob.smith@example.com", displayName: "Bob Smith", password: "Password123!", roleSlug: "buyer" },
4
+ { email: "carol.white@example.com", displayName: "Carol White", password: "Password123!", roleSlug: "viewer" }
5
+ ];
6
+ export {
7
+ EXAMPLE_PORTAL_ACCOUNTS
8
+ };
9
+ //# sourceMappingURL=exampleAccounts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/customer_accounts/lib/exampleAccounts.ts"],
4
+ "sourcesContent": ["export type ExamplePortalAccount = {\n email: string\n displayName: string\n password: string\n roleSlug: string\n}\n\n/**\n * Portal accounts created by the module's `seedExamples` hook. Setup seeds from\n * this list and the admin demo-credentials endpoint reports against it, so the\n * advertised credentials can never drift from the seeded ones (#3198) and are\n * only surfaced for accounts that actually exist in the current scope (#5669).\n */\nexport const EXAMPLE_PORTAL_ACCOUNTS: readonly ExamplePortalAccount[] = [\n { email: 'alice.johnson@example.com', displayName: 'Alice Johnson', password: 'Password123!', roleSlug: 'portal_admin' },\n { email: 'bob.smith@example.com', displayName: 'Bob Smith', password: 'Password123!', roleSlug: 'buyer' },\n { email: 'carol.white@example.com', displayName: 'Carol White', password: 'Password123!', roleSlug: 'viewer' },\n] as const\n"],
5
+ "mappings": "AAaO,MAAM,0BAA2D;AAAA,EACtE,EAAE,OAAO,6BAA6B,aAAa,iBAAiB,UAAU,gBAAgB,UAAU,eAAe;AAAA,EACvH,EAAE,OAAO,yBAAyB,aAAa,aAAa,UAAU,gBAAgB,UAAU,QAAQ;AAAA,EACxG,EAAE,OAAO,2BAA2B,aAAa,eAAe,UAAU,gBAAgB,UAAU,SAAS;AAC/G;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260827160000_customer_accounts extends Migration {
3
+ async up() {
4
+ this.addSql(`alter table "customer_users" drop constraint "customer_users_tenant_email_hash_uniq";`);
5
+ this.addSql(`create unique index "customer_users_tenant_email_hash_uniq" on "customer_users" ("tenant_id", "email_hash") where "deleted_at" is null;`);
6
+ }
7
+ async down() {
8
+ this.addSql(`drop index if exists "customer_users_tenant_email_hash_uniq";`);
9
+ this.addSql(`delete from "customer_users" losing using "customer_users" winner where losing."deleted_at" is not null and losing."tenant_id" = winner."tenant_id" and losing."email_hash" = winner."email_hash" and losing."id" <> winner."id" and (winner."deleted_at" is null or winner."created_at" > losing."created_at" or (winner."created_at" = losing."created_at" and winner."id" > losing."id"));`);
10
+ this.addSql(`alter table "customer_users" add constraint "customer_users_tenant_email_hash_uniq" unique ("tenant_id", "email_hash");`);
11
+ }
12
+ }
13
+ export {
14
+ Migration20260827160000_customer_accounts
15
+ };
16
+ //# sourceMappingURL=Migration20260827160000_customer_accounts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/customer_accounts/migrations/Migration20260827160000_customer_accounts.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260827160000_customer_accounts extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`alter table \"customer_users\" drop constraint \"customer_users_tenant_email_hash_uniq\";`);\n this.addSql(`create unique index \"customer_users_tenant_email_hash_uniq\" on \"customer_users\" (\"tenant_id\", \"email_hash\") where \"deleted_at\" is null;`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop index if exists \"customer_users_tenant_email_hash_uniq\";`);\n // Once the partial index has been live, a soft-deleted row legitimately shares\n // (tenant_id, email_hash) with a newer row \u2014 which is the point of this change \u2014\n // so the full constraint cannot be restored while both exist. Purge the losing\n // soft-deleted duplicates first: this is destructive, but it only removes rows\n // that were already logically deleted, and without it `down()` fails on exactly\n // the data the migration enables. The surviving row is the active one, else the\n // most recently created.\n this.addSql(`delete from \"customer_users\" losing using \"customer_users\" winner where losing.\"deleted_at\" is not null and losing.\"tenant_id\" = winner.\"tenant_id\" and losing.\"email_hash\" = winner.\"email_hash\" and losing.\"id\" <> winner.\"id\" and (winner.\"deleted_at\" is null or winner.\"created_at\" > losing.\"created_at\" or (winner.\"created_at\" = losing.\"created_at\" and winner.\"id\" > losing.\"id\"));`);\n this.addSql(`alter table \"customer_users\" add constraint \"customer_users_tenant_email_hash_uniq\" unique (\"tenant_id\", \"email_hash\");`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,kDAAkD,UAAU;AAAA,EAEvE,MAAe,KAAoB;AACjC,SAAK,OAAO,uFAAuF;AACnG,SAAK,OAAO,yIAAyI;AAAA,EACvJ;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,+DAA+D;AAQ3E,SAAK,OAAO,+XAA+X;AAC3Y,SAAK,OAAO,yHAAyH;AAAA,EACvI;AAEF;",
6
+ "names": []
7
+ }
@@ -1,3 +1,5 @@
1
+ var _a, _b;
2
+ import { UniqueConstraintViolationException } from "@mikro-orm/core";
1
3
  import { hash } from "bcryptjs";
2
4
  import {
3
5
  CustomerUser,
@@ -6,10 +8,32 @@ import {
6
8
  CustomerRole
7
9
  } from "@open-mercato/core/modules/customer_accounts/data/entities";
8
10
  import { generateSecureToken, hashToken } from "@open-mercato/core/modules/customer_accounts/lib/tokenGenerator";
9
- import { hashForLookup } from "@open-mercato/shared/lib/encryption/aes";
11
+ import { hashForLookup, lookupHashCandidates } from "@open-mercato/shared/lib/encryption/aes";
10
12
  import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
11
13
  const BCRYPT_COST = 10;
12
14
  const INVITATION_TTL_MS = 72 * 60 * 60 * 1e3;
15
+ const CUSTOMER_INVITATION_EMAIL_CONFLICT_MARKER = /* @__PURE__ */ Symbol.for("@open-mercato/CustomerInvitationEmailConflictError");
16
+ class CustomerInvitationEmailConflictError extends (_b = Error, _a = CUSTOMER_INVITATION_EMAIL_CONFLICT_MARKER, _b) {
17
+ constructor(email) {
18
+ super("[internal] An account with this email address already exists");
19
+ this.email = email;
20
+ this[_a] = true;
21
+ this.name = "CustomerInvitationEmailConflictError";
22
+ }
23
+ }
24
+ function isCustomerInvitationEmailConflictError(error) {
25
+ return !!error && typeof error === "object" && error[CUSTOMER_INVITATION_EMAIL_CONFLICT_MARKER] === true;
26
+ }
27
+ const POSTGRES_UNIQUE_VIOLATION = "23505";
28
+ function isUniqueConstraintViolation(error) {
29
+ if (error instanceof UniqueConstraintViolationException) return true;
30
+ if (!error || typeof error !== "object") return false;
31
+ const candidate = error;
32
+ if (candidate.name === "UniqueConstraintViolationException") return true;
33
+ if (candidate.code === POSTGRES_UNIQUE_VIOLATION) return true;
34
+ const cause = candidate.cause;
35
+ return !!cause && typeof cause === "object" && cause.code === POSTGRES_UNIQUE_VIOLATION;
36
+ }
13
37
  class CustomerInvitationService {
14
38
  constructor(em) {
15
39
  this.em = em;
@@ -49,11 +73,11 @@ class CustomerInvitationService {
49
73
  existing.email = normalizedEmail;
50
74
  existing.token = tokenHashed;
51
75
  existing.customerEntityId = options.customerEntityId || null;
52
- existing.personEntityId = options.personEntityId || null;
76
+ existing.personEntityId = options.personEntityId || existing.personEntityId || null;
53
77
  existing.roleIdsJson = options.roleIds;
54
78
  existing.invitedByUserId = options.invitedByUserId || null;
55
79
  existing.invitedByCustomerUserId = options.invitedByCustomerUserId || null;
56
- existing.displayName = options.displayName || null;
80
+ existing.displayName = options.displayName || existing.displayName || null;
57
81
  existing.expiresAt = expiresAt;
58
82
  await this.em.flush();
59
83
  return { invitation: existing, rawToken: token, reused: true, rollbackState };
@@ -108,8 +132,22 @@ class CustomerInvitationService {
108
132
  async acceptInvitation(token, password, displayName) {
109
133
  const invitation = await this.findByToken(token);
110
134
  if (!invitation) return null;
111
- const passwordHash = await hash(password, BCRYPT_COST);
112
135
  const emailHash = hashForLookup(invitation.email);
136
+ const existingActiveUser = await findOneWithDecryption(
137
+ this.em,
138
+ CustomerUser,
139
+ {
140
+ tenantId: invitation.tenantId,
141
+ emailHash: { $in: lookupHashCandidates(invitation.email) },
142
+ deletedAt: null
143
+ },
144
+ void 0,
145
+ { tenantId: invitation.tenantId, organizationId: invitation.organizationId }
146
+ );
147
+ if (existingActiveUser) {
148
+ throw new CustomerInvitationEmailConflictError(invitation.email);
149
+ }
150
+ const passwordHash = await hash(password, BCRYPT_COST);
113
151
  const user = this.em.create(CustomerUser, {
114
152
  email: invitation.email,
115
153
  emailHash,
@@ -148,11 +186,20 @@ class CustomerInvitationService {
148
186
  this.em.persist(userRole);
149
187
  }
150
188
  invitation.acceptedAt = /* @__PURE__ */ new Date();
151
- await this.em.flush();
189
+ try {
190
+ await this.em.flush();
191
+ } catch (error) {
192
+ if (isUniqueConstraintViolation(error)) {
193
+ throw new CustomerInvitationEmailConflictError(invitation.email);
194
+ }
195
+ throw error;
196
+ }
152
197
  return { user, invitation };
153
198
  }
154
199
  }
155
200
  export {
156
- CustomerInvitationService
201
+ CustomerInvitationEmailConflictError,
202
+ CustomerInvitationService,
203
+ isCustomerInvitationEmailConflictError
157
204
  };
158
205
  //# sourceMappingURL=customerInvitationService.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customer_accounts/services/customerInvitationService.ts"],
4
- "sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { hash } from 'bcryptjs'\nimport {\n CustomerUser,\n CustomerUserInvitation,\n CustomerUserRole,\n CustomerRole,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { generateSecureToken, hashToken } from '@open-mercato/core/modules/customer_accounts/lib/tokenGenerator'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nconst BCRYPT_COST = 10\nconst INVITATION_TTL_MS = 72 * 60 * 60 * 1000 // 72 hours\n\nexport type CustomerInvitationRollbackState = {\n email: string\n token: string\n customerEntityId: string | null\n personEntityId: string | null\n roleIdsJson: string[]\n invitedByUserId: string | null\n invitedByCustomerUserId: string | null\n displayName: string | null\n expiresAt: Date\n}\n\nexport class CustomerInvitationService {\n constructor(private em: EntityManager) {}\n\n async createInvitation(\n email: string,\n scope: { tenantId: string; organizationId: string },\n options: {\n customerEntityId?: string | null\n personEntityId?: string | null\n roleIds: string[]\n invitedByUserId?: string | null\n invitedByCustomerUserId?: string | null\n displayName?: string | null\n },\n ): Promise<{\n invitation: CustomerUserInvitation\n rawToken: string\n reused: boolean\n rollbackState: CustomerInvitationRollbackState | null\n }> {\n const token = generateSecureToken()\n const emailHash = hashForLookup(email)\n const normalizedEmail = email.toLowerCase().trim()\n const expiresAt = new Date(Date.now() + INVITATION_TTL_MS)\n const tokenHashed = hashToken(token)\n\n // Dedupe: reuse an existing pending (not accepted, not cancelled, unexpired)\n // invitation for the same recipient instead of inserting a new row. This caps\n // row/token growth and keeps a single live token per concurrently-pending\n // (tenant, organization, email) tuple.\n const existing = await findOneWithDecryption(\n this.em,\n CustomerUserInvitation,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n emailHash,\n acceptedAt: null,\n cancelledAt: null,\n expiresAt: { $gt: new Date() },\n } as any,\n undefined,\n { tenantId: scope.tenantId, organizationId: scope.organizationId },\n )\n\n if (existing) {\n const rollbackState: CustomerInvitationRollbackState = {\n email: existing.email,\n token: existing.token,\n customerEntityId: existing.customerEntityId ?? null,\n personEntityId: existing.personEntityId ?? null,\n roleIdsJson: [...(existing.roleIdsJson ?? [])],\n invitedByUserId: existing.invitedByUserId ?? null,\n invitedByCustomerUserId: existing.invitedByCustomerUserId ?? null,\n displayName: existing.displayName ?? null,\n expiresAt: new Date(existing.expiresAt),\n }\n existing.email = normalizedEmail\n existing.token = tokenHashed\n existing.customerEntityId = options.customerEntityId || null\n existing.personEntityId = options.personEntityId || null\n existing.roleIdsJson = options.roleIds\n existing.invitedByUserId = options.invitedByUserId || null\n existing.invitedByCustomerUserId = options.invitedByCustomerUserId || null\n existing.displayName = options.displayName || null\n existing.expiresAt = expiresAt\n await this.em.flush()\n return { invitation: existing, rawToken: token, reused: true, rollbackState }\n }\n\n const invitation = this.em.create(CustomerUserInvitation, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n email: normalizedEmail,\n emailHash,\n token: tokenHashed,\n customerEntityId: options.customerEntityId || null,\n personEntityId: options.personEntityId || null,\n roleIdsJson: options.roleIds,\n invitedByUserId: options.invitedByUserId || null,\n invitedByCustomerUserId: options.invitedByCustomerUserId || null,\n displayName: options.displayName || null,\n expiresAt,\n createdAt: new Date(),\n } as any) as CustomerUserInvitation\n await this.em.persist(invitation).flush()\n return { invitation, rawToken: token, reused: false, rollbackState: null }\n }\n\n async rollbackInvitation(\n invitation: CustomerUserInvitation,\n rollbackState: CustomerInvitationRollbackState | null,\n ): Promise<void> {\n if (!rollbackState) {\n await this.em.remove(invitation).flush()\n return\n }\n\n invitation.email = rollbackState.email\n invitation.token = rollbackState.token\n invitation.customerEntityId = rollbackState.customerEntityId\n invitation.personEntityId = rollbackState.personEntityId\n invitation.roleIdsJson = [...rollbackState.roleIdsJson]\n invitation.invitedByUserId = rollbackState.invitedByUserId\n invitation.invitedByCustomerUserId = rollbackState.invitedByCustomerUserId\n invitation.displayName = rollbackState.displayName\n invitation.expiresAt = new Date(rollbackState.expiresAt)\n await this.em.flush()\n }\n\n async findByToken(token: string): Promise<CustomerUserInvitation | null> {\n const tokenHashed = hashToken(token)\n const invitation = await findOneWithDecryption(\n this.em,\n CustomerUserInvitation,\n { token: tokenHashed } as any,\n )\n if (!invitation) return null\n if (invitation.acceptedAt) return null\n if (invitation.cancelledAt) return null\n if (invitation.expiresAt.getTime() < Date.now()) return null\n return invitation\n }\n\n async acceptInvitation(\n token: string,\n password: string,\n displayName: string,\n ): Promise<{ user: CustomerUser; invitation: CustomerUserInvitation } | null> {\n const invitation = await this.findByToken(token)\n if (!invitation) return null\n\n const passwordHash = await hash(password, BCRYPT_COST)\n const emailHash = hashForLookup(invitation.email)\n\n // Create user\n const user = this.em.create(CustomerUser, {\n email: invitation.email,\n emailHash,\n passwordHash,\n displayName: displayName || invitation.displayName || invitation.email,\n tenantId: invitation.tenantId,\n organizationId: invitation.organizationId,\n customerEntityId: invitation.customerEntityId || null,\n personEntityId: invitation.personEntityId || null,\n isActive: true,\n emailVerifiedAt: new Date(), // Invitation implicitly verifies email\n failedLoginAttempts: 0,\n createdAt: new Date(),\n } as any) as CustomerUser\n this.em.persist(user)\n\n // Assign roles\n const roleIds = Array.isArray(invitation.roleIdsJson) ? invitation.roleIdsJson : []\n const roles = roleIds.length > 0\n ? await findWithDecryption(\n this.em,\n CustomerRole,\n {\n id: { $in: roleIds } as any,\n tenantId: invitation.tenantId,\n organizationId: invitation.organizationId,\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: invitation.tenantId, organizationId: invitation.organizationId },\n )\n : []\n for (const role of roles) {\n const userRole = this.em.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n this.em.persist(userRole)\n }\n\n // Mark invitation as accepted\n invitation.acceptedAt = new Date()\n\n await this.em.flush()\n return { user, invitation }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { UniqueConstraintViolationException } from '@mikro-orm/core'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { hash } from 'bcryptjs'\nimport {\n CustomerUser,\n CustomerUserInvitation,\n CustomerUserRole,\n CustomerRole,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { generateSecureToken, hashToken } from '@open-mercato/core/modules/customer_accounts/lib/tokenGenerator'\nimport { hashForLookup, lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nconst BCRYPT_COST = 10\nconst INVITATION_TTL_MS = 72 * 60 * 60 * 1000 // 72 hours\n\n// Use Symbol.for so the marker survives module duplication across bundle\n// boundaries: the accept route and this service are bundled into separate\n// chunks, so `instanceof` between them is false and the 409 mapping would\n// silently degrade back into the raw 500 this error exists to prevent.\nconst CUSTOMER_INVITATION_EMAIL_CONFLICT_MARKER = Symbol.for('@open-mercato/CustomerInvitationEmailConflictError')\n\nexport class CustomerInvitationEmailConflictError extends Error {\n readonly [CUSTOMER_INVITATION_EMAIL_CONFLICT_MARKER] = true\n\n constructor(public readonly email: string) {\n super('[internal] An account with this email address already exists')\n this.name = 'CustomerInvitationEmailConflictError'\n }\n}\n\n/**\n * Bundle-safe check for {@link CustomerInvitationEmailConflictError}. Always prefer\n * this over `instanceof` \u2014 API routes reach this service through DI and resolve a\n * different copy of this module.\n */\nexport function isCustomerInvitationEmailConflictError(\n error: unknown,\n): error is CustomerInvitationEmailConflictError {\n return !!error\n && typeof error === 'object'\n && (error as Record<symbol, unknown>)[CUSTOMER_INVITATION_EMAIL_CONFLICT_MARKER] === true\n}\n\nconst POSTGRES_UNIQUE_VIOLATION = '23505'\n\n/**\n * Unique-constraint detection that does not depend on `instanceof`: the MikroORM\n * copy this module is bundled with is not necessarily the one the driver throws\n * from, so fall back to the exception name and the Postgres SQLSTATE.\n */\nfunction isUniqueConstraintViolation(error: unknown): boolean {\n if (error instanceof UniqueConstraintViolationException) return true\n if (!error || typeof error !== 'object') return false\n const candidate = error as { name?: unknown; code?: unknown; cause?: unknown }\n if (candidate.name === 'UniqueConstraintViolationException') return true\n if (candidate.code === POSTGRES_UNIQUE_VIOLATION) return true\n const cause = candidate.cause as { code?: unknown } | undefined\n return !!cause && typeof cause === 'object' && cause.code === POSTGRES_UNIQUE_VIOLATION\n}\n\nexport type CustomerInvitationRollbackState = {\n email: string\n token: string\n customerEntityId: string | null\n personEntityId: string | null\n roleIdsJson: string[]\n invitedByUserId: string | null\n invitedByCustomerUserId: string | null\n displayName: string | null\n expiresAt: Date\n}\n\nexport class CustomerInvitationService {\n constructor(private em: EntityManager) {}\n\n async createInvitation(\n email: string,\n scope: { tenantId: string; organizationId: string },\n options: {\n customerEntityId?: string | null\n personEntityId?: string | null\n roleIds: string[]\n invitedByUserId?: string | null\n invitedByCustomerUserId?: string | null\n displayName?: string | null\n },\n ): Promise<{\n invitation: CustomerUserInvitation\n rawToken: string\n reused: boolean\n rollbackState: CustomerInvitationRollbackState | null\n }> {\n const token = generateSecureToken()\n const emailHash = hashForLookup(email)\n const normalizedEmail = email.toLowerCase().trim()\n const expiresAt = new Date(Date.now() + INVITATION_TTL_MS)\n const tokenHashed = hashToken(token)\n\n // Dedupe: reuse an existing pending (not accepted, not cancelled, unexpired)\n // invitation for the same recipient instead of inserting a new row. This caps\n // row/token growth and keeps a single live token per concurrently-pending\n // (tenant, organization, email) tuple.\n const existing = await findOneWithDecryption(\n this.em,\n CustomerUserInvitation,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n emailHash,\n acceptedAt: null,\n cancelledAt: null,\n expiresAt: { $gt: new Date() },\n } as any,\n undefined,\n { tenantId: scope.tenantId, organizationId: scope.organizationId },\n )\n\n if (existing) {\n const rollbackState: CustomerInvitationRollbackState = {\n email: existing.email,\n token: existing.token,\n customerEntityId: existing.customerEntityId ?? null,\n personEntityId: existing.personEntityId ?? null,\n roleIdsJson: [...(existing.roleIdsJson ?? [])],\n invitedByUserId: existing.invitedByUserId ?? null,\n invitedByCustomerUserId: existing.invitedByCustomerUserId ?? null,\n displayName: existing.displayName ?? null,\n expiresAt: new Date(existing.expiresAt),\n }\n existing.email = normalizedEmail\n existing.token = tokenHashed\n existing.customerEntityId = options.customerEntityId || null\n // Keep the CRM person link and the recipient's display name when the\n // re-invite does not carry them: the portal invite route and the admin users\n // page only ever know the company, so overwriting with null would strip an\n // invitation raised from a person card off that person's account-status card\n // and drop the personalization from the invitation email (#5499).\n existing.personEntityId = options.personEntityId || existing.personEntityId || null\n existing.roleIdsJson = options.roleIds\n existing.invitedByUserId = options.invitedByUserId || null\n existing.invitedByCustomerUserId = options.invitedByCustomerUserId || null\n existing.displayName = options.displayName || existing.displayName || null\n existing.expiresAt = expiresAt\n await this.em.flush()\n return { invitation: existing, rawToken: token, reused: true, rollbackState }\n }\n\n const invitation = this.em.create(CustomerUserInvitation, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n email: normalizedEmail,\n emailHash,\n token: tokenHashed,\n customerEntityId: options.customerEntityId || null,\n personEntityId: options.personEntityId || null,\n roleIdsJson: options.roleIds,\n invitedByUserId: options.invitedByUserId || null,\n invitedByCustomerUserId: options.invitedByCustomerUserId || null,\n displayName: options.displayName || null,\n expiresAt,\n createdAt: new Date(),\n } as any) as CustomerUserInvitation\n await this.em.persist(invitation).flush()\n return { invitation, rawToken: token, reused: false, rollbackState: null }\n }\n\n async rollbackInvitation(\n invitation: CustomerUserInvitation,\n rollbackState: CustomerInvitationRollbackState | null,\n ): Promise<void> {\n if (!rollbackState) {\n await this.em.remove(invitation).flush()\n return\n }\n\n invitation.email = rollbackState.email\n invitation.token = rollbackState.token\n invitation.customerEntityId = rollbackState.customerEntityId\n invitation.personEntityId = rollbackState.personEntityId\n invitation.roleIdsJson = [...rollbackState.roleIdsJson]\n invitation.invitedByUserId = rollbackState.invitedByUserId\n invitation.invitedByCustomerUserId = rollbackState.invitedByCustomerUserId\n invitation.displayName = rollbackState.displayName\n invitation.expiresAt = new Date(rollbackState.expiresAt)\n await this.em.flush()\n }\n\n async findByToken(token: string): Promise<CustomerUserInvitation | null> {\n const tokenHashed = hashToken(token)\n const invitation = await findOneWithDecryption(\n this.em,\n CustomerUserInvitation,\n { token: tokenHashed } as any,\n )\n if (!invitation) return null\n if (invitation.acceptedAt) return null\n if (invitation.cancelledAt) return null\n if (invitation.expiresAt.getTime() < Date.now()) return null\n return invitation\n }\n\n async acceptInvitation(\n token: string,\n password: string,\n displayName: string,\n ): Promise<{ user: CustomerUser; invitation: CustomerUserInvitation } | null> {\n const invitation = await this.findByToken(token)\n if (!invitation) return null\n\n const emailHash = hashForLookup(invitation.email)\n\n // A soft-deleted CustomerUser row does not block re-invitation (the unique\n // index only applies to non-deleted rows), but an active account with the\n // same email is a genuine conflict \u2014 fail with a domain error here instead\n // of letting the DB unique-constraint violation surface as a raw 500.\n // The lookup matches both hash formats: rows written before the keyed digest\n // still carry the legacy hash, and the partial index cannot relate the two.\n const existingActiveUser = await findOneWithDecryption(\n this.em,\n CustomerUser,\n {\n tenantId: invitation.tenantId,\n emailHash: { $in: lookupHashCandidates(invitation.email) },\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: invitation.tenantId, organizationId: invitation.organizationId },\n )\n if (existingActiveUser) {\n throw new CustomerInvitationEmailConflictError(invitation.email)\n }\n\n const passwordHash = await hash(password, BCRYPT_COST)\n\n // Create user\n const user = this.em.create(CustomerUser, {\n email: invitation.email,\n emailHash,\n passwordHash,\n displayName: displayName || invitation.displayName || invitation.email,\n tenantId: invitation.tenantId,\n organizationId: invitation.organizationId,\n customerEntityId: invitation.customerEntityId || null,\n personEntityId: invitation.personEntityId || null,\n isActive: true,\n emailVerifiedAt: new Date(), // Invitation implicitly verifies email\n failedLoginAttempts: 0,\n createdAt: new Date(),\n } as any) as CustomerUser\n this.em.persist(user)\n\n // Assign roles\n const roleIds = Array.isArray(invitation.roleIdsJson) ? invitation.roleIdsJson : []\n const roles = roleIds.length > 0\n ? await findWithDecryption(\n this.em,\n CustomerRole,\n {\n id: { $in: roleIds } as any,\n tenantId: invitation.tenantId,\n organizationId: invitation.organizationId,\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: invitation.tenantId, organizationId: invitation.organizationId },\n )\n : []\n for (const role of roles) {\n const userRole = this.em.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n this.em.persist(userRole)\n }\n\n // Mark invitation as accepted\n invitation.acceptedAt = new Date()\n\n // The guard above is only the friendly fast path: it and this write are\n // separated by a bcrypt hash and a role query, so two concurrent accepts can\n // both pass it. The partial unique index is the real authority \u2014 translate\n // its violation onto the same domain error so both paths answer 409.\n try {\n await this.em.flush()\n } catch (error) {\n if (isUniqueConstraintViolation(error)) {\n throw new CustomerInvitationEmailConflictError(invitation.email)\n }\n throw error\n }\n return { user, invitation }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import { hash } from "bcryptjs";
2
2
  import { hashForLookup } from "@open-mercato/shared/lib/encryption/aes";
3
+ import { EXAMPLE_PORTAL_ACCOUNTS } from "@open-mercato/core/modules/customer_accounts/lib/exampleAccounts";
3
4
  import {
4
5
  CustomerRole,
5
6
  CustomerRoleAcl,
@@ -188,12 +189,7 @@ const setup = {
188
189
  },
189
190
  async seedExamples({ em, tenantId, organizationId }) {
190
191
  const BCRYPT_COST = 10;
191
- const exampleUsers = [
192
- { email: "alice.johnson@example.com", displayName: "Alice Johnson", password: "Password123!", roleSlug: "portal_admin" },
193
- { email: "bob.smith@example.com", displayName: "Bob Smith", password: "Password123!", roleSlug: "buyer" },
194
- { email: "carol.white@example.com", displayName: "Carol White", password: "Password123!", roleSlug: "viewer" }
195
- ];
196
- for (const entry of exampleUsers) {
192
+ for (const entry of EXAMPLE_PORTAL_ACCOUNTS) {
197
193
  const emailHash = hashForLookup(entry.email);
198
194
  const existing = await em.findOne(CustomerUser, { emailHash, tenantId, deletedAt: null });
199
195
  if (existing) continue;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/customer_accounts/setup.ts"],
4
- "sourcesContent": ["import type { ModuleSetupConfig, DefaultCustomerRoleFeatures } from '@open-mercato/shared/modules/setup'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Module } from '@open-mercato/shared/modules/registry'\nimport { hash } from 'bcryptjs'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport {\n CustomerRole,\n CustomerRoleAcl,\n CustomerUser,\n CustomerUserRole,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\n\ninterface SeedScope {\n tenantId: string\n organizationId: string\n}\n\ntype SchedulerServiceLike = {\n register: (registration: {\n id: string\n name: string\n description?: string\n scopeType: 'system'\n scheduleType: 'interval'\n scheduleValue: string\n timezone?: string\n targetType: 'queue'\n targetQueue: string\n targetPayload?: Record<string, unknown>\n sourceType: 'module'\n sourceModule: string\n isEnabled?: boolean\n }) => Promise<void>\n}\n\n// Stable, deterministic UUIDs for the two domain-routing system schedules so\n// re-running setup re-upserts the same rows instead of creating duplicates.\nconst DOMAIN_VERIFICATION_SCHEDULE_ID = '5e9ef5fc-1f4d-5b3d-8b58-8e1a5a4d1001'\nconst DOMAIN_TLS_RETRY_SCHEDULE_ID = '5e9ef5fc-1f4d-5b3d-8b58-8e1a5a4d1002'\n\nfunction intervalSecondsFromEnv(envName: string, fallbackSeconds: number): string {\n const parsed = Number.parseInt(process.env[envName] ?? '', 10)\n const seconds = Number.isFinite(parsed) && parsed > 0 ? parsed : fallbackSeconds\n return `${seconds}s`\n}\n\nasync function registerDomainSchedules(\n container: { hasRegistration?: (name: string) => boolean; resolve: (name: string) => unknown },\n): Promise<void> {\n if (typeof container.hasRegistration !== 'function' || !container.hasRegistration('schedulerService')) {\n return\n }\n const schedulerService = container.resolve('schedulerService') as SchedulerServiceLike\n\n await schedulerService.register({\n id: DOMAIN_VERIFICATION_SCHEDULE_ID,\n name: 'Custom domain \u2014 DNS auto-verification',\n description:\n 'Re-runs DNS verification for custom-domain mappings stuck in pending or dns_failed, and triggers TLS health checks on success.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: intervalSecondsFromEnv('DOMAIN_AUTO_VERIFY_INTERVAL_SECONDS', 300),\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'domain-verification',\n sourceType: 'module',\n sourceModule: 'customer_accounts',\n isEnabled: true,\n })\n\n await schedulerService.register({\n id: DOMAIN_TLS_RETRY_SCHEDULE_ID,\n name: 'Custom domain \u2014 TLS retry',\n description:\n 'Retries TLS certificate provisioning for verified or tls_failed custom-domain mappings, with batch cap and adaptive backoff on Let\\'s Encrypt rate limits.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: intervalSecondsFromEnv('DOMAIN_TLS_RETRY_INTERVAL_SECONDS', 1800),\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'domain-tls-retry',\n sourceType: 'module',\n sourceModule: 'customer_accounts',\n isEnabled: true,\n })\n}\n\nconst DEFAULT_ROLES = [\n {\n name: 'Portal Admin',\n slug: 'portal_admin',\n description: 'Full portal administration access',\n isSystem: true,\n customerAssignable: false,\n isDefault: false,\n acl: {\n isPortalAdmin: true,\n features: ['portal.*'],\n },\n },\n {\n name: 'Buyer',\n slug: 'buyer',\n description: 'Standard buyer access with ordering capabilities',\n isSystem: true,\n customerAssignable: true,\n isDefault: true,\n acl: {\n isPortalAdmin: false,\n features: [\n 'portal.account.manage',\n 'portal.orders.view',\n 'portal.orders.create',\n 'portal.quotes.view',\n 'portal.quotes.request',\n 'portal.invoices.view',\n 'portal.catalog.view',\n ],\n },\n },\n {\n name: 'Viewer',\n slug: 'viewer',\n description: 'Read-only portal access',\n isSystem: true,\n customerAssignable: true,\n isDefault: false,\n acl: {\n isPortalAdmin: false,\n features: [\n 'portal.account.manage',\n 'portal.orders.view',\n 'portal.invoices.view',\n 'portal.catalog.view',\n ],\n },\n },\n]\n\nconst DEFAULT_CUSTOMER_ROLE_FEATURES = Object.fromEntries(\n DEFAULT_ROLES.map((role) => [role.slug, [...role.acl.features]]),\n)\n\n/**\n * Collect defaultCustomerRoleFeatures from all enabled modules and merge\n * them into the corresponding CustomerRoleAcl records.\n */\nasync function ensureDefaultCustomerRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules: Module[],\n): Promise<void> {\n const featuresByRole: Record<string, string[]> = {}\n\n for (const mod of modules) {\n const customerRoleFeatures = mod.setup?.defaultCustomerRoleFeatures\n if (!customerRoleFeatures) continue\n for (const [roleSlug, features] of Object.entries(customerRoleFeatures)) {\n if (!features || !features.length) continue\n if (!featuresByRole[roleSlug]) featuresByRole[roleSlug] = []\n featuresByRole[roleSlug].push(...features)\n }\n }\n\n const roleSlugs = Object.keys(featuresByRole)\n if (!roleSlugs.length) return\n\n for (const roleSlug of roleSlugs) {\n const role = await em.findOne(CustomerRole, { tenantId, slug: roleSlug, deletedAt: null })\n if (!role) continue\n\n const acl = await em.findOne(CustomerRoleAcl, { role: role.id as any, tenantId })\n if (!acl) continue\n\n const currentFeatures = Array.isArray(acl.featuresJson) ? acl.featuresJson : []\n const merged = Array.from(new Set([...currentFeatures, ...featuresByRole[roleSlug]]))\n const changed =\n merged.length !== currentFeatures.length ||\n merged.some((value, index) => value !== currentFeatures[index])\n if (changed) {\n acl.featuresJson = merged\n em.persist(acl)\n }\n }\n await em.flush()\n}\n\nasync function seedDefaultRoles(em: EntityManager, scope: SeedScope): Promise<void> {\n for (const roleDef of DEFAULT_ROLES) {\n const existing = await em.findOne(CustomerRole, {\n tenantId: scope.tenantId,\n slug: roleDef.slug,\n deletedAt: null,\n })\n if (existing) continue\n\n const role = em.create(CustomerRole, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: roleDef.name,\n slug: roleDef.slug,\n description: roleDef.description,\n isSystem: roleDef.isSystem,\n customerAssignable: roleDef.customerAssignable,\n isDefault: roleDef.isDefault,\n createdAt: new Date(),\n } as any)\n em.persist(role)\n\n const acl = em.create(CustomerRoleAcl, {\n role,\n tenantId: scope.tenantId,\n featuresJson: roleDef.acl.features,\n isPortalAdmin: roleDef.acl.isPortalAdmin,\n createdAt: new Date(),\n } as any)\n em.persist(acl)\n }\n await em.flush()\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['customer_accounts.*'],\n admin: ['customer_accounts.*'],\n },\n // Keep the portal feature catalog declarative so the shared feature policy\n // can project portal.* into concrete capabilities without importing core.\n defaultCustomerRoleFeatures: DEFAULT_CUSTOMER_ROLE_FEATURES,\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n await seedDefaultRoles(em, { tenantId, organizationId })\n },\n\n async seedDefaults({ em, tenantId, organizationId, container }) {\n await seedDefaultRoles(em, { tenantId, organizationId })\n // Merge defaultCustomerRoleFeatures from all enabled modules\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const allModules = getModules()\n await ensureDefaultCustomerRoleAcls(em, tenantId, allModules)\n } catch {\n // Modules may not be registered yet during initial setup\n }\n\n // System-scoped schedules: register is idempotent, so re-running setup\n // (or running it for additional tenants) just upserts the same two rows.\n try {\n await registerDomainSchedules(\n container as { hasRegistration?: (name: string) => boolean; resolve: (name: string) => unknown },\n )\n } catch {\n // Scheduler may not be installed in this deployment; ignore.\n }\n },\n\n async seedExamples({ em, tenantId, organizationId }) {\n const BCRYPT_COST = 10\n const exampleUsers = [\n { email: 'alice.johnson@example.com', displayName: 'Alice Johnson', password: 'Password123!', roleSlug: 'portal_admin' },\n { email: 'bob.smith@example.com', displayName: 'Bob Smith', password: 'Password123!', roleSlug: 'buyer' },\n { email: 'carol.white@example.com', displayName: 'Carol White', password: 'Password123!', roleSlug: 'viewer' },\n ]\n\n for (const entry of exampleUsers) {\n const emailHash = hashForLookup(entry.email)\n const existing = await em.findOne(CustomerUser, { emailHash, tenantId, deletedAt: null })\n if (existing) continue\n\n const passwordHash = await hash(entry.password, BCRYPT_COST)\n const user = em.create(CustomerUser, {\n email: entry.email,\n emailHash,\n passwordHash,\n displayName: entry.displayName,\n tenantId,\n organizationId,\n isActive: true,\n failedLoginAttempts: 0,\n emailVerifiedAt: new Date(),\n createdAt: new Date(),\n } as any)\n em.persist(user)\n\n const role = await em.findOne(CustomerRole, { tenantId, slug: entry.roleSlug, deletedAt: null })\n if (role) {\n const userRole = em.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n em.persist(userRole)\n }\n }\n await em.flush()\n },\n}\n\nexport default setup\n"],
5
- "mappings": "AAGA,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA2BP,MAAM,kCAAkC;AACxC,MAAM,+BAA+B;AAErC,SAAS,uBAAuB,SAAiB,iBAAiC;AAChF,QAAM,SAAS,OAAO,SAAS,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE;AAC7D,QAAM,UAAU,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AACjE,SAAO,GAAG,OAAO;AACnB;AAEA,eAAe,wBACb,WACe;AACf,MAAI,OAAO,UAAU,oBAAoB,cAAc,CAAC,UAAU,gBAAgB,kBAAkB,GAAG;AACrG;AAAA,EACF;AACA,QAAM,mBAAmB,UAAU,QAAQ,kBAAkB;AAE7D,QAAM,iBAAiB,SAAS;AAAA,IAC9B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,IACX,cAAc;AAAA,IACd,eAAe,uBAAuB,uCAAuC,GAAG;AAAA,IAChF,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW;AAAA,EACb,CAAC;AAED,QAAM,iBAAiB,SAAS;AAAA,IAC9B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,IACX,cAAc;AAAA,IACd,eAAe,uBAAuB,qCAAqC,IAAI;AAAA,IAC/E,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW;AAAA,EACb,CAAC;AACH;AAEA,MAAM,gBAAgB;AAAA,EACpB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,KAAK;AAAA,MACH,eAAe;AAAA,MACf,UAAU,CAAC,UAAU;AAAA,IACvB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,KAAK;AAAA,MACH,eAAe;AAAA,MACf,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,KAAK;AAAA,MACH,eAAe;AAAA,MACf,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,iCAAiC,OAAO;AAAA,EAC5C,cAAc,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,QAAQ,CAAC,CAAC;AACjE;AAMA,eAAe,8BACb,IACA,UACA,SACe;AACf,QAAM,iBAA2C,CAAC;AAElD,aAAW,OAAO,SAAS;AACzB,UAAM,uBAAuB,IAAI,OAAO;AACxC,QAAI,CAAC,qBAAsB;AAC3B,eAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,oBAAoB,GAAG;AACvE,UAAI,CAAC,YAAY,CAAC,SAAS,OAAQ;AACnC,UAAI,CAAC,eAAe,QAAQ,EAAG,gBAAe,QAAQ,IAAI,CAAC;AAC3D,qBAAe,QAAQ,EAAE,KAAK,GAAG,QAAQ;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,KAAK,cAAc;AAC5C,MAAI,CAAC,UAAU,OAAQ;AAEvB,aAAW,YAAY,WAAW;AAChC,UAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,UAAU,MAAM,UAAU,WAAW,KAAK,CAAC;AACzF,QAAI,CAAC,KAAM;AAEX,UAAM,MAAM,MAAM,GAAG,QAAQ,iBAAiB,EAAE,MAAM,KAAK,IAAW,SAAS,CAAC;AAChF,QAAI,CAAC,IAAK;AAEV,UAAM,kBAAkB,MAAM,QAAQ,IAAI,YAAY,IAAI,IAAI,eAAe,CAAC;AAC9E,UAAM,SAAS,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,eAAe,QAAQ,CAAC,CAAC,CAAC;AACpF,UAAM,UACJ,OAAO,WAAW,gBAAgB,UAClC,OAAO,KAAK,CAAC,OAAO,UAAU,UAAU,gBAAgB,KAAK,CAAC;AAChE,QAAI,SAAS;AACX,UAAI,eAAe;AACnB,SAAG,QAAQ,GAAG;AAAA,IAChB;AAAA,EACF;AACA,QAAM,GAAG,MAAM;AACjB;AAEA,eAAe,iBAAiB,IAAmB,OAAiC;AAClF,aAAW,WAAW,eAAe;AACnC,UAAM,WAAW,MAAM,GAAG,QAAQ,cAAc;AAAA,MAC9C,UAAU,MAAM;AAAA,MAChB,MAAM,QAAQ;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AACD,QAAI,SAAU;AAEd,UAAM,OAAO,GAAG,OAAO,cAAc;AAAA,MACnC,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,oBAAoB,QAAQ;AAAA,MAC5B,WAAW,QAAQ;AAAA,MACnB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAQ;AACR,OAAG,QAAQ,IAAI;AAEf,UAAM,MAAM,GAAG,OAAO,iBAAiB;AAAA,MACrC;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,cAAc,QAAQ,IAAI;AAAA,MAC1B,eAAe,QAAQ,IAAI;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAQ;AACR,OAAG,QAAQ,GAAG;AAAA,EAChB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,qBAAqB;AAAA,IAClC,OAAO,CAAC,qBAAqB;AAAA,EAC/B;AAAA;AAAA;AAAA,EAGA,6BAA6B;AAAA,EAE7B,MAAM,gBAAgB,EAAE,IAAI,UAAU,eAAe,GAAG;AACtD,UAAM,iBAAiB,IAAI,EAAE,UAAU,eAAe,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,aAAa,EAAE,IAAI,UAAU,gBAAgB,UAAU,GAAG;AAC9D,UAAM,iBAAiB,IAAI,EAAE,UAAU,eAAe,CAAC;AAEvD,QAAI;AACF,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,2CAA2C;AAC/E,YAAM,aAAa,WAAW;AAC9B,YAAM,8BAA8B,IAAI,UAAU,UAAU;AAAA,IAC9D,QAAQ;AAAA,IAER;AAIA,QAAI;AACF,YAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,EAAE,IAAI,UAAU,eAAe,GAAG;AACnD,UAAM,cAAc;AACpB,UAAM,eAAe;AAAA,MACnB,EAAE,OAAO,6BAA6B,aAAa,iBAAiB,UAAU,gBAAgB,UAAU,eAAe;AAAA,MACvH,EAAE,OAAO,yBAAyB,aAAa,aAAa,UAAU,gBAAgB,UAAU,QAAQ;AAAA,MACxG,EAAE,OAAO,2BAA2B,aAAa,eAAe,UAAU,gBAAgB,UAAU,SAAS;AAAA,IAC/G;AAEA,eAAW,SAAS,cAAc;AAChC,YAAM,YAAY,cAAc,MAAM,KAAK;AAC3C,YAAM,WAAW,MAAM,GAAG,QAAQ,cAAc,EAAE,WAAW,UAAU,WAAW,KAAK,CAAC;AACxF,UAAI,SAAU;AAEd,YAAM,eAAe,MAAM,KAAK,MAAM,UAAU,WAAW;AAC3D,YAAM,OAAO,GAAG,OAAO,cAAc;AAAA,QACnC,OAAO,MAAM;AAAA,QACb;AAAA,QACA;AAAA,QACA,aAAa,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,qBAAqB;AAAA,QACrB,iBAAiB,oBAAI,KAAK;AAAA,QAC1B,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAQ;AACR,SAAG,QAAQ,IAAI;AAEf,YAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,UAAU,MAAM,MAAM,UAAU,WAAW,KAAK,CAAC;AAC/F,UAAI,MAAM;AACR,cAAM,WAAW,GAAG,OAAO,kBAAkB;AAAA,UAC3C;AAAA,UACA;AAAA,UACA,WAAW,oBAAI,KAAK;AAAA,QACtB,CAAQ;AACR,WAAG,QAAQ,QAAQ;AAAA,MACrB;AAAA,IACF;AACA,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import type { ModuleSetupConfig, DefaultCustomerRoleFeatures } from '@open-mercato/shared/modules/setup'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Module } from '@open-mercato/shared/modules/registry'\nimport { hash } from 'bcryptjs'\nimport { hashForLookup } from '@open-mercato/shared/lib/encryption/aes'\nimport { EXAMPLE_PORTAL_ACCOUNTS } from '@open-mercato/core/modules/customer_accounts/lib/exampleAccounts'\nimport {\n CustomerRole,\n CustomerRoleAcl,\n CustomerUser,\n CustomerUserRole,\n} from '@open-mercato/core/modules/customer_accounts/data/entities'\n\ninterface SeedScope {\n tenantId: string\n organizationId: string\n}\n\ntype SchedulerServiceLike = {\n register: (registration: {\n id: string\n name: string\n description?: string\n scopeType: 'system'\n scheduleType: 'interval'\n scheduleValue: string\n timezone?: string\n targetType: 'queue'\n targetQueue: string\n targetPayload?: Record<string, unknown>\n sourceType: 'module'\n sourceModule: string\n isEnabled?: boolean\n }) => Promise<void>\n}\n\n// Stable, deterministic UUIDs for the two domain-routing system schedules so\n// re-running setup re-upserts the same rows instead of creating duplicates.\nconst DOMAIN_VERIFICATION_SCHEDULE_ID = '5e9ef5fc-1f4d-5b3d-8b58-8e1a5a4d1001'\nconst DOMAIN_TLS_RETRY_SCHEDULE_ID = '5e9ef5fc-1f4d-5b3d-8b58-8e1a5a4d1002'\n\nfunction intervalSecondsFromEnv(envName: string, fallbackSeconds: number): string {\n const parsed = Number.parseInt(process.env[envName] ?? '', 10)\n const seconds = Number.isFinite(parsed) && parsed > 0 ? parsed : fallbackSeconds\n return `${seconds}s`\n}\n\nasync function registerDomainSchedules(\n container: { hasRegistration?: (name: string) => boolean; resolve: (name: string) => unknown },\n): Promise<void> {\n if (typeof container.hasRegistration !== 'function' || !container.hasRegistration('schedulerService')) {\n return\n }\n const schedulerService = container.resolve('schedulerService') as SchedulerServiceLike\n\n await schedulerService.register({\n id: DOMAIN_VERIFICATION_SCHEDULE_ID,\n name: 'Custom domain \u2014 DNS auto-verification',\n description:\n 'Re-runs DNS verification for custom-domain mappings stuck in pending or dns_failed, and triggers TLS health checks on success.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: intervalSecondsFromEnv('DOMAIN_AUTO_VERIFY_INTERVAL_SECONDS', 300),\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'domain-verification',\n sourceType: 'module',\n sourceModule: 'customer_accounts',\n isEnabled: true,\n })\n\n await schedulerService.register({\n id: DOMAIN_TLS_RETRY_SCHEDULE_ID,\n name: 'Custom domain \u2014 TLS retry',\n description:\n 'Retries TLS certificate provisioning for verified or tls_failed custom-domain mappings, with batch cap and adaptive backoff on Let\\'s Encrypt rate limits.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: intervalSecondsFromEnv('DOMAIN_TLS_RETRY_INTERVAL_SECONDS', 1800),\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'domain-tls-retry',\n sourceType: 'module',\n sourceModule: 'customer_accounts',\n isEnabled: true,\n })\n}\n\nconst DEFAULT_ROLES = [\n {\n name: 'Portal Admin',\n slug: 'portal_admin',\n description: 'Full portal administration access',\n isSystem: true,\n customerAssignable: false,\n isDefault: false,\n acl: {\n isPortalAdmin: true,\n features: ['portal.*'],\n },\n },\n {\n name: 'Buyer',\n slug: 'buyer',\n description: 'Standard buyer access with ordering capabilities',\n isSystem: true,\n customerAssignable: true,\n isDefault: true,\n acl: {\n isPortalAdmin: false,\n features: [\n 'portal.account.manage',\n 'portal.orders.view',\n 'portal.orders.create',\n 'portal.quotes.view',\n 'portal.quotes.request',\n 'portal.invoices.view',\n 'portal.catalog.view',\n ],\n },\n },\n {\n name: 'Viewer',\n slug: 'viewer',\n description: 'Read-only portal access',\n isSystem: true,\n customerAssignable: true,\n isDefault: false,\n acl: {\n isPortalAdmin: false,\n features: [\n 'portal.account.manage',\n 'portal.orders.view',\n 'portal.invoices.view',\n 'portal.catalog.view',\n ],\n },\n },\n]\n\nconst DEFAULT_CUSTOMER_ROLE_FEATURES = Object.fromEntries(\n DEFAULT_ROLES.map((role) => [role.slug, [...role.acl.features]]),\n)\n\n/**\n * Collect defaultCustomerRoleFeatures from all enabled modules and merge\n * them into the corresponding CustomerRoleAcl records.\n */\nasync function ensureDefaultCustomerRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules: Module[],\n): Promise<void> {\n const featuresByRole: Record<string, string[]> = {}\n\n for (const mod of modules) {\n const customerRoleFeatures = mod.setup?.defaultCustomerRoleFeatures\n if (!customerRoleFeatures) continue\n for (const [roleSlug, features] of Object.entries(customerRoleFeatures)) {\n if (!features || !features.length) continue\n if (!featuresByRole[roleSlug]) featuresByRole[roleSlug] = []\n featuresByRole[roleSlug].push(...features)\n }\n }\n\n const roleSlugs = Object.keys(featuresByRole)\n if (!roleSlugs.length) return\n\n for (const roleSlug of roleSlugs) {\n const role = await em.findOne(CustomerRole, { tenantId, slug: roleSlug, deletedAt: null })\n if (!role) continue\n\n const acl = await em.findOne(CustomerRoleAcl, { role: role.id as any, tenantId })\n if (!acl) continue\n\n const currentFeatures = Array.isArray(acl.featuresJson) ? acl.featuresJson : []\n const merged = Array.from(new Set([...currentFeatures, ...featuresByRole[roleSlug]]))\n const changed =\n merged.length !== currentFeatures.length ||\n merged.some((value, index) => value !== currentFeatures[index])\n if (changed) {\n acl.featuresJson = merged\n em.persist(acl)\n }\n }\n await em.flush()\n}\n\nasync function seedDefaultRoles(em: EntityManager, scope: SeedScope): Promise<void> {\n for (const roleDef of DEFAULT_ROLES) {\n const existing = await em.findOne(CustomerRole, {\n tenantId: scope.tenantId,\n slug: roleDef.slug,\n deletedAt: null,\n })\n if (existing) continue\n\n const role = em.create(CustomerRole, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: roleDef.name,\n slug: roleDef.slug,\n description: roleDef.description,\n isSystem: roleDef.isSystem,\n customerAssignable: roleDef.customerAssignable,\n isDefault: roleDef.isDefault,\n createdAt: new Date(),\n } as any)\n em.persist(role)\n\n const acl = em.create(CustomerRoleAcl, {\n role,\n tenantId: scope.tenantId,\n featuresJson: roleDef.acl.features,\n isPortalAdmin: roleDef.acl.isPortalAdmin,\n createdAt: new Date(),\n } as any)\n em.persist(acl)\n }\n await em.flush()\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['customer_accounts.*'],\n admin: ['customer_accounts.*'],\n },\n // Keep the portal feature catalog declarative so the shared feature policy\n // can project portal.* into concrete capabilities without importing core.\n defaultCustomerRoleFeatures: DEFAULT_CUSTOMER_ROLE_FEATURES,\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n await seedDefaultRoles(em, { tenantId, organizationId })\n },\n\n async seedDefaults({ em, tenantId, organizationId, container }) {\n await seedDefaultRoles(em, { tenantId, organizationId })\n // Merge defaultCustomerRoleFeatures from all enabled modules\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const allModules = getModules()\n await ensureDefaultCustomerRoleAcls(em, tenantId, allModules)\n } catch {\n // Modules may not be registered yet during initial setup\n }\n\n // System-scoped schedules: register is idempotent, so re-running setup\n // (or running it for additional tenants) just upserts the same two rows.\n try {\n await registerDomainSchedules(\n container as { hasRegistration?: (name: string) => boolean; resolve: (name: string) => unknown },\n )\n } catch {\n // Scheduler may not be installed in this deployment; ignore.\n }\n },\n\n async seedExamples({ em, tenantId, organizationId }) {\n const BCRYPT_COST = 10\n\n for (const entry of EXAMPLE_PORTAL_ACCOUNTS) {\n const emailHash = hashForLookup(entry.email)\n const existing = await em.findOne(CustomerUser, { emailHash, tenantId, deletedAt: null })\n if (existing) continue\n\n const passwordHash = await hash(entry.password, BCRYPT_COST)\n const user = em.create(CustomerUser, {\n email: entry.email,\n emailHash,\n passwordHash,\n displayName: entry.displayName,\n tenantId,\n organizationId,\n isActive: true,\n failedLoginAttempts: 0,\n emailVerifiedAt: new Date(),\n createdAt: new Date(),\n } as any)\n em.persist(user)\n\n const role = await em.findOne(CustomerRole, { tenantId, slug: entry.roleSlug, deletedAt: null })\n if (role) {\n const userRole = em.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n em.persist(userRole)\n }\n }\n await em.flush()\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -10,9 +10,20 @@ import { Input } from "@open-mercato/ui/primitives/input";
10
10
  import { EmailInput } from "@open-mercato/ui/primitives/email-input";
11
11
  import { StatusBadge } from "@open-mercato/ui/primitives/status-badge";
12
12
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
13
- function InviteForm({ personEntityId, onSuccess }) {
13
+ async function fetchPendingInvitation(filter) {
14
+ const result = await apiCall(`/api/customer_accounts/admin/users-invite?${filter}&pageSize=1`);
15
+ if (!result.ok) return null;
16
+ const json = result.result;
17
+ const items = json?.items;
18
+ return items?.[0] || null;
19
+ }
20
+ function InviteForm({
21
+ personEntityId,
22
+ personData,
23
+ isLoadingPerson,
24
+ onSuccess
25
+ }) {
14
26
  const t = useT();
15
- const [isLoadingPerson, setIsLoadingPerson] = React.useState(true);
16
27
  const [email, setEmail] = React.useState("");
17
28
  const [displayName, setDisplayName] = React.useState("");
18
29
  const [selectedRoleIds, setSelectedRoleIds] = React.useState([]);
@@ -23,36 +34,19 @@ function InviteForm({ personEntityId, onSuccess }) {
23
34
  contextId: "customer_accounts:account-status-invite"
24
35
  });
25
36
  React.useEffect(() => {
26
- let cancelled = false;
27
- async function loadPerson() {
28
- try {
29
- const call = await apiCall(
30
- `/api/customers/people/${encodeURIComponent(personEntityId)}`
31
- );
32
- if (cancelled) return;
33
- if (call.ok && call.result) {
34
- const person = call.result.person;
35
- const profile = call.result.profile;
36
- if (person?.primaryEmail) {
37
- setEmail(person.primaryEmail);
38
- }
39
- const nameParts = [profile?.firstName, profile?.lastName].filter(Boolean);
40
- if (nameParts.length > 0) {
41
- setDisplayName(nameParts.join(" "));
42
- } else if (person?.displayName) {
43
- setDisplayName(person.displayName);
44
- }
45
- }
46
- } catch {
47
- } finally {
48
- if (!cancelled) setIsLoadingPerson(false);
49
- }
37
+ if (!personData) return;
38
+ const person = personData.person;
39
+ const profile = personData.profile;
40
+ if (person?.primaryEmail) {
41
+ setEmail(person.primaryEmail);
50
42
  }
51
- loadPerson();
52
- return () => {
53
- cancelled = true;
54
- };
55
- }, [personEntityId]);
43
+ const nameParts = [profile?.firstName, profile?.lastName].filter(Boolean);
44
+ if (nameParts.length > 0) {
45
+ setDisplayName(nameParts.join(" "));
46
+ } else if (person?.displayName) {
47
+ setDisplayName(person.displayName);
48
+ }
49
+ }, [personData]);
56
50
  React.useEffect(() => {
57
51
  let cancelled = false;
58
52
  async function loadRoles() {
@@ -202,20 +196,33 @@ function AccountStatusWidget({ context }) {
202
196
  },
203
197
  enabled: !!personEntityId
204
198
  });
205
- const { data: pendingInvitation, isLoading: isLoadingInvitation } = useQuery({
206
- queryKey: ["customer-account-pending-invitation", personEntityId],
199
+ const { data: personData, isLoading: isLoadingPerson } = useQuery({
200
+ queryKey: ["customer-account-person", personEntityId],
207
201
  queryFn: async () => {
208
202
  if (!personEntityId) return null;
209
203
  const result = await apiCall(
210
- `/api/customer_accounts/admin/users-invite?personEntityId=${encodeURIComponent(personEntityId)}&pageSize=1`
204
+ `/api/customers/people/${encodeURIComponent(personEntityId)}`
211
205
  );
212
206
  if (!result.ok) return null;
213
- const json = result.result;
214
- const items = json?.items;
215
- return items?.[0] || null;
207
+ return result.result || null;
216
208
  },
217
209
  enabled: !!personEntityId && !isLoading && !data
218
210
  });
211
+ const personEmail = personData?.person?.primaryEmail?.trim() || null;
212
+ const { data: pendingInvitation, isLoading: isLoadingInvitation } = useQuery({
213
+ queryKey: ["customer-account-pending-invitation", personEntityId, personEmail],
214
+ queryFn: async () => {
215
+ if (!personEntityId) return null;
216
+ const byPerson = await fetchPendingInvitation(
217
+ `personEntityId=${encodeURIComponent(personEntityId)}`
218
+ );
219
+ if (byPerson) return byPerson;
220
+ if (!personEmail) return null;
221
+ return fetchPendingInvitation(`email=${encodeURIComponent(personEmail)}`);
222
+ },
223
+ enabled: !!personEntityId && !isLoading && !data && !isLoadingPerson
224
+ });
225
+ const isResolvingInvitation = isLoadingPerson || isLoadingInvitation;
219
226
  function handleInviteSuccess() {
220
227
  setShowInviteForm(false);
221
228
  queryClient.invalidateQueries({ queryKey: ["customer-account-status", personEntityId] });
@@ -227,7 +234,7 @@ function AccountStatusWidget({ context }) {
227
234
  if (!data) {
228
235
  return /* @__PURE__ */ jsxs("div", { className: "rounded-md border p-3", children: [
229
236
  /* @__PURE__ */ jsx("div", { className: "text-sm font-medium mb-1", children: t("customer_accounts.widgets.accountStatus", "Portal Account") }),
230
- isLoadingInvitation ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("common.loading", "Loading...") }) : pendingInvitation ? /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
237
+ isResolvingInvitation ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("common.loading", "Loading...") }) : pendingInvitation ? /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
231
238
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
232
239
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("common.status", "Status") }),
233
240
  /* @__PURE__ */ jsx(StatusBadge, { variant: "warning", dot: true, children: t("customer_accounts.widgets.invitationPending", "Invitation pending") })
@@ -241,7 +248,7 @@ function AccountStatusWidget({ context }) {
241
248
  /* @__PURE__ */ jsx("span", { children: new Date(pendingInvitation.expiresAt).toLocaleDateString() })
242
249
  ] })
243
250
  ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("customer_accounts.widgets.noAccount", "No portal account linked") }),
244
- !showInviteForm && personEntityId && !isLoadingInvitation && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
251
+ !showInviteForm && personEntityId && !isResolvingInvitation && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
245
252
  Button,
246
253
  {
247
254
  type: "button",
@@ -251,7 +258,15 @@ function AccountStatusWidget({ context }) {
251
258
  children: pendingInvitation ? t("customer_accounts.widgets.invite.resend", "Resend invitation") : t("customer_accounts.widgets.invite.button", "Invite to Portal")
252
259
  }
253
260
  ) }),
254
- showInviteForm && personEntityId && /* @__PURE__ */ jsx(InviteForm, { personEntityId, onSuccess: handleInviteSuccess })
261
+ showInviteForm && personEntityId && /* @__PURE__ */ jsx(
262
+ InviteForm,
263
+ {
264
+ personEntityId,
265
+ personData: personData ?? null,
266
+ isLoadingPerson,
267
+ onSuccess: handleInviteSuccess
268
+ }
269
+ )
255
270
  ] });
256
271
  }
257
272
  return /* @__PURE__ */ jsxs("div", { className: "rounded-md border p-3", children: [