@open-mercato/core 0.6.1-develop.3276.1.1fcd145078 → 0.6.1-develop.3278.1.a4d26475e4

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 (212) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/domain_mapping/index.js +31 -0
  3. package/dist/generated/entities/domain_mapping/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +2 -1
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +16 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/helpers/integration/salesUi.js +18 -17
  9. package/dist/helpers/integration/salesUi.js.map +2 -2
  10. package/dist/modules/customer_accounts/acl.js +2 -1
  11. package/dist/modules/customer_accounts/acl.js.map +2 -2
  12. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
  13. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
  14. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
  15. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
  16. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
  17. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
  18. package/dist/modules/customer_accounts/api/domain-check.js +70 -0
  19. package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
  20. package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
  21. package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
  22. package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
  23. package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
  24. package/dist/modules/customer_accounts/api/login.js +14 -3
  25. package/dist/modules/customer_accounts/api/login.js.map +2 -2
  26. package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
  27. package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
  28. package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
  29. package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
  30. package/dist/modules/customer_accounts/api/signup.js +32 -5
  31. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  32. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
  33. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
  34. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
  35. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
  36. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
  37. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
  38. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
  39. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
  40. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
  41. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
  42. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
  43. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
  44. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
  45. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
  46. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
  47. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
  48. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
  49. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
  50. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
  51. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
  52. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
  53. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
  54. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
  55. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
  56. package/dist/modules/customer_accounts/data/enrichers.js +78 -12
  57. package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
  58. package/dist/modules/customer_accounts/data/entities.js +85 -2
  59. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  60. package/dist/modules/customer_accounts/data/guards.js +130 -0
  61. package/dist/modules/customer_accounts/data/guards.js.map +7 -0
  62. package/dist/modules/customer_accounts/data/validators.js +19 -0
  63. package/dist/modules/customer_accounts/data/validators.js.map +2 -2
  64. package/dist/modules/customer_accounts/di.js +23 -1
  65. package/dist/modules/customer_accounts/di.js.map +2 -2
  66. package/dist/modules/customer_accounts/events.js +9 -2
  67. package/dist/modules/customer_accounts/events.js.map +2 -2
  68. package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
  69. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
  70. package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
  71. package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
  72. package/dist/modules/customer_accounts/lib/hostname.js +55 -0
  73. package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
  74. package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
  75. package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
  76. package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
  77. package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
  78. package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
  79. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
  80. package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
  81. package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
  82. package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
  83. package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
  84. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
  85. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
  86. package/dist/modules/customer_accounts/notifications.client.js +76 -0
  87. package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
  88. package/dist/modules/customer_accounts/notifications.js +76 -0
  89. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  90. package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
  91. package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
  92. package/dist/modules/customer_accounts/setup.js +48 -1
  93. package/dist/modules/customer_accounts/setup.js.map +2 -2
  94. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
  95. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
  96. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
  97. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
  98. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
  99. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
  100. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
  101. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
  102. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
  103. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
  104. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
  105. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
  106. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
  107. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
  108. package/dist/modules/customers/api/companies/route.js +4 -9
  109. package/dist/modules/customers/api/companies/route.js.map +2 -2
  110. package/dist/modules/customers/api/deals/route.js +4 -9
  111. package/dist/modules/customers/api/deals/route.js.map +2 -2
  112. package/dist/modules/customers/api/people/route.js +4 -9
  113. package/dist/modules/customers/api/people/route.js.map +2 -2
  114. package/dist/modules/customers/api/utils.js +0 -12
  115. package/dist/modules/customers/api/utils.js.map +2 -2
  116. package/dist/modules/customers/backend/customers/companies/page.js +361 -284
  117. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  118. package/dist/modules/customers/backend/customers/deals/page.js +378 -473
  119. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  120. package/dist/modules/customers/backend/customers/people/page.js +368 -286
  121. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  122. package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
  123. package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
  124. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
  125. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  126. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
  127. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
  128. package/dist/modules/customers/widgets/injection-table.js +6 -0
  129. package/dist/modules/customers/widgets/injection-table.js.map +2 -2
  130. package/dist/modules/perspectives/services/perspectiveService.js +16 -2
  131. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  132. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +1 -0
  133. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  134. package/dist/modules/query_index/lib/engine.js +31 -8
  135. package/dist/modules/query_index/lib/engine.js.map +2 -2
  136. package/generated/entities/domain_mapping/index.ts +14 -0
  137. package/generated/entities.ids.generated.ts +2 -1
  138. package/generated/entity-fields-registry.ts +16 -0
  139. package/package.json +5 -5
  140. package/src/helpers/integration/salesUi.ts +27 -23
  141. package/src/modules/customer_accounts/acl.ts +1 -0
  142. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
  143. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
  144. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
  145. package/src/modules/customer_accounts/api/domain-check.ts +83 -0
  146. package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
  147. package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
  148. package/src/modules/customer_accounts/api/login.ts +14 -3
  149. package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
  150. package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
  151. package/src/modules/customer_accounts/api/signup.ts +36 -5
  152. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
  153. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
  154. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
  155. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
  156. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
  157. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
  158. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
  159. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
  160. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
  161. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
  162. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
  163. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
  164. package/src/modules/customer_accounts/data/enrichers.ts +95 -12
  165. package/src/modules/customer_accounts/data/entities.ts +80 -1
  166. package/src/modules/customer_accounts/data/guards.ts +155 -0
  167. package/src/modules/customer_accounts/data/validators.ts +26 -0
  168. package/src/modules/customer_accounts/di.ts +35 -1
  169. package/src/modules/customer_accounts/events.ts +8 -1
  170. package/src/modules/customer_accounts/i18n/de.json +78 -0
  171. package/src/modules/customer_accounts/i18n/en.json +78 -0
  172. package/src/modules/customer_accounts/i18n/es.json +78 -0
  173. package/src/modules/customer_accounts/i18n/pl.json +78 -0
  174. package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
  175. package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
  176. package/src/modules/customer_accounts/lib/hostname.ts +76 -0
  177. package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
  178. package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
  179. package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
  180. package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
  181. package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
  182. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
  183. package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
  184. package/src/modules/customer_accounts/notifications.client.ts +76 -0
  185. package/src/modules/customer_accounts/notifications.ts +76 -0
  186. package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
  187. package/src/modules/customer_accounts/setup.ts +81 -1
  188. package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
  189. package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
  190. package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
  191. package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
  192. package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
  193. package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
  194. package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
  195. package/src/modules/customers/api/companies/route.ts +4 -9
  196. package/src/modules/customers/api/deals/route.ts +4 -9
  197. package/src/modules/customers/api/people/route.ts +4 -9
  198. package/src/modules/customers/api/utils.ts +0 -14
  199. package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
  200. package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
  201. package/src/modules/customers/backend/customers/people/page.tsx +331 -240
  202. package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
  203. package/src/modules/customers/i18n/de.json +31 -0
  204. package/src/modules/customers/i18n/en.json +31 -0
  205. package/src/modules/customers/i18n/es.json +31 -0
  206. package/src/modules/customers/i18n/pl.json +31 -0
  207. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
  208. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
  209. package/src/modules/customers/widgets/injection-table.ts +6 -0
  210. package/src/modules/perspectives/services/perspectiveService.ts +30 -2
  211. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +1 -0
  212. package/src/modules/query_index/lib/engine.ts +46 -8
@@ -0,0 +1,14 @@
1
+ export const id = "id";
2
+ export const hostname = "hostname";
3
+ export const tenant_id = "tenant_id";
4
+ export const organization_id = "organization_id";
5
+ export const replaces_domain = "replaces_domain";
6
+ export const provider = "provider";
7
+ export const status = "status";
8
+ export const verified_at = "verified_at";
9
+ export const last_dns_check_at = "last_dns_check_at";
10
+ export const dns_failure_reason = "dns_failure_reason";
11
+ export const tls_failure_reason = "tls_failure_reason";
12
+ export const tls_retry_count = "tls_retry_count";
13
+ export const created_at = "created_at";
14
+ export const updated_at = "updated_at";
@@ -262,7 +262,8 @@ export const M = {
262
262
  "customer_user_session": "customer_accounts:customer_user_session",
263
263
  "customer_user_email_verification": "customer_accounts:customer_user_email_verification",
264
264
  "customer_user_password_reset": "customer_accounts:customer_user_password_reset",
265
- "customer_user_invitation": "customer_accounts:customer_user_invitation"
265
+ "customer_user_invitation": "customer_accounts:customer_user_invitation",
266
+ "domain_mapping": "customer_accounts:domain_mapping"
266
267
  }
267
268
  } as const;
268
269
 
@@ -985,6 +985,22 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
985
985
  "created_at": "created_at",
986
986
  "updated_at": "updated_at"
987
987
  },
988
+ "domain_mapping": {
989
+ "id": "id",
990
+ "hostname": "hostname",
991
+ "tenant_id": "tenant_id",
992
+ "organization_id": "organization_id",
993
+ "replaces_domain": "replaces_domain",
994
+ "provider": "provider",
995
+ "status": "status",
996
+ "verified_at": "verified_at",
997
+ "last_dns_check_at": "last_dns_check_at",
998
+ "dns_failure_reason": "dns_failure_reason",
999
+ "tls_failure_reason": "tls_failure_reason",
1000
+ "tls_retry_count": "tls_retry_count",
1001
+ "created_at": "created_at",
1002
+ "updated_at": "updated_at"
1003
+ },
988
1004
  "encryption_map": {
989
1005
  "id": "id",
990
1006
  "entity_id": "entity_id",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.1-develop.3276.1.1fcd145078",
3
+ "version": "0.6.1-develop.3278.1.a4d26475e4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -237,12 +237,12 @@
237
237
  "ts-pattern": "^5.0.0"
238
238
  },
239
239
  "peerDependencies": {
240
- "@open-mercato/ai-assistant": "0.6.1-develop.3276.1.1fcd145078",
241
- "@open-mercato/shared": "0.6.1-develop.3276.1.1fcd145078"
240
+ "@open-mercato/ai-assistant": "0.6.1-develop.3278.1.a4d26475e4",
241
+ "@open-mercato/shared": "0.6.1-develop.3278.1.a4d26475e4"
242
242
  },
243
243
  "devDependencies": {
244
- "@open-mercato/ai-assistant": "0.6.1-develop.3276.1.1fcd145078",
245
- "@open-mercato/shared": "0.6.1-develop.3276.1.1fcd145078",
244
+ "@open-mercato/ai-assistant": "0.6.1-develop.3278.1.a4d26475e4",
245
+ "@open-mercato/shared": "0.6.1-develop.3278.1.a4d26475e4",
246
246
  "@testing-library/dom": "^10.4.1",
247
247
  "@testing-library/jest-dom": "^6.9.1",
248
248
  "@testing-library/react": "^16.3.1",
@@ -94,10 +94,6 @@ function parseCurrencyAmount(value: string): number {
94
94
  return Number.parseFloat(lastMatch.replace('$', ''));
95
95
  }
96
96
 
97
- function normalizeAdjustmentKindValue(kindLabel: string): string {
98
- return kindLabel.trim().toLowerCase().replace(/\s+/g, '_');
99
- }
100
-
101
97
  function readId(payload: unknown, keys: string[]): string | null {
102
98
  if (!payload || typeof payload !== 'object') return null;
103
99
  const map = payload as Record<string, unknown>;
@@ -1099,32 +1095,40 @@ export async function addAdjustment(page: Page, options: AddAdjustmentOptions):
1099
1095
  // Radix Select trigger — target by CrudForm field id (kind picker)
1100
1096
  const kindTrigger = dialog.locator('[data-crud-field-id="kind"] [role="combobox"]').first();
1101
1097
  await expect(kindTrigger).toBeVisible({ timeout: TEST_WAIT_TIMEOUT_MS });
1102
-
1103
- const labelInput = dialog.getByPlaceholder(/e\.g\. Shipping fee/i).first();
1104
- await expect(labelInput).toBeVisible({ timeout: TEST_WAIT_TIMEOUT_MS });
1105
- // Bumped toHaveValue timeout 2 s → 5 s — covers the worst-case reset race
1106
- // where the tax-rates fetch resolves *after* the networkidle window above
1107
- // (e.g. when a slow Docker host bundles the requests inconsistently).
1108
- await fillControlledInput(labelInput, options.label, 5_000);
1109
-
1110
- if ((await kindTrigger.count()) > 0) {
1111
- const expectedKindValue = normalizeAdjustmentKindValue(options.kindLabel ?? 'Surcharge');
1112
- const kindLabel = options.kindLabel ?? 'Surcharge';
1113
- await kindTrigger.click();
1114
- const option = page.getByRole('option', { name: new RegExp(`^${escapeRegExp(kindLabel)}$`, 'i') }).first();
1115
- const optionVisible = await option.waitFor({ state: 'visible', timeout: 2_000 }).then(
1116
- () => true,
1117
- () => false,
1098
+ const kindLabel = options.kindLabel ?? 'Surcharge';
1099
+ const selectKindOption = async (): Promise<void> => {
1100
+ if ((await kindTrigger.count()) === 0) return;
1101
+ await waitForOptionalTextToDisappear(
1102
+ dialog,
1103
+ /Loading adjustment kinds…|Loading adjustment kinds\.\.\./i,
1104
+ TEST_WAIT_TIMEOUT_MS,
1118
1105
  );
1106
+ await expect(kindTrigger).toBeEnabled({ timeout: TEST_WAIT_TIMEOUT_MS });
1107
+ await kindTrigger.click();
1108
+ const kindOption = page.getByRole('option', { name: new RegExp(`^${escapeRegExp(kindLabel)}$`, 'i') }).first();
1109
+ const optionVisible = await kindOption
1110
+ .waitFor({ state: 'visible', timeout: TEST_WAIT_TIMEOUT_MS })
1111
+ .then(() => true, () => false);
1119
1112
  if (optionVisible) {
1120
- await option.click({ force: true });
1113
+ await kindOption.click({ force: true });
1121
1114
  } else {
1115
+ // Fallback: drive selection via keyboard when modal-backdrop click interception
1116
+ // hides the rendered option. Brought forward from develop.
1122
1117
  await page.keyboard.type(kindLabel.charAt(0));
1123
1118
  await page.waitForTimeout(150);
1124
1119
  await page.keyboard.press('Enter');
1125
1120
  }
1126
- void expectedKindValue;
1127
- }
1121
+ await expect(kindTrigger).toContainText(kindLabel, { timeout: 2_000 });
1122
+ };
1123
+
1124
+ // Select kind first — the Radix Select interaction can trigger CrudForm re-renders that
1125
+ // clear inputs filled before it, so we run the dropdown sequence up front and then fill
1126
+ // the remaining fields once.
1127
+ await selectKindOption();
1128
+
1129
+ const labelInput = dialog.getByPlaceholder(/e\.g\. Shipping fee/i).first();
1130
+ await expect(labelInput).toBeVisible({ timeout: TEST_WAIT_TIMEOUT_MS });
1131
+ await fillControlledInput(labelInput, options.label);
1128
1132
 
1129
1133
  const fixedAmountButton = dialog.getByRole('button', { name: /^Fixed amount$/i }).first();
1130
1134
  if ((await fixedAmountButton.count()) > 0) {
@@ -3,6 +3,7 @@ export const features = [
3
3
  { id: 'customer_accounts.manage', title: 'Manage customer accounts', module: 'customer_accounts' },
4
4
  { id: 'customer_accounts.roles.manage', title: 'Manage customer roles', module: 'customer_accounts' },
5
5
  { id: 'customer_accounts.invite', title: 'Invite customer users', module: 'customer_accounts' },
6
+ { id: 'customer_accounts.domain.manage', title: 'Manage custom portal domains', module: 'customer_accounts' },
6
7
  ]
7
8
 
8
9
  export default features
@@ -0,0 +1,119 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
+ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
+ import { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
7
+ import {
8
+ validateCrudMutationGuard,
9
+ runCrudMutationGuardAfterSuccess,
10
+ } from '@open-mercato/shared/lib/crud/mutation-guard'
11
+ import { DomainMappingService } from '@open-mercato/core/modules/customer_accounts/services/domainMappingService'
12
+
13
+ const FEATURE = 'customer_accounts.domain.manage'
14
+
15
+ export const metadata = {
16
+ POST: { requireAuth: true, requireFeatures: [FEATURE] },
17
+ }
18
+
19
+ export async function POST(req: Request, ctx: { params: Promise<{ id: string }> }) {
20
+ const auth = await getAuthFromRequest(req)
21
+ if (!auth || !auth.tenantId) return NextResponse.json({ ok: false, error: 'Unauthorized' }, { status: 401 })
22
+
23
+ const container = await createRequestContainer()
24
+ const rbac = container.resolve('rbacService') as RbacService
25
+ const allowed = await rbac.userHasAllFeatures(auth.sub, [FEATURE], {
26
+ tenantId: auth.tenantId,
27
+ organizationId: auth.orgId,
28
+ })
29
+ if (!allowed) return NextResponse.json({ ok: false, error: 'Forbidden' }, { status: 403 })
30
+
31
+ const { id } = await ctx.params
32
+ const service = container.resolve('domainMappingService') as DomainMappingService
33
+ const record = await service.findById(id, { tenantId: auth.tenantId })
34
+ if (!record) return NextResponse.json({ ok: false, error: 'Not found' }, { status: 404 })
35
+
36
+ const guardResult = await validateCrudMutationGuard(container, {
37
+ tenantId: auth.tenantId,
38
+ organizationId: record.organizationId,
39
+ userId: auth.sub,
40
+ resourceKind: 'customer_accounts.domain_mapping',
41
+ resourceId: id,
42
+ operation: 'update',
43
+ requestMethod: req.method,
44
+ requestHeaders: req.headers,
45
+ })
46
+ if (guardResult && !guardResult.ok) {
47
+ return NextResponse.json(guardResult.body, { status: guardResult.status })
48
+ }
49
+
50
+ let updated
51
+ try {
52
+ updated = await service.healthCheck(id)
53
+ } catch (err) {
54
+ return NextResponse.json(
55
+ { ok: false, error: err instanceof Error ? err.message : 'Health check failed' },
56
+ { status: 500 },
57
+ )
58
+ }
59
+
60
+ if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
61
+ await runCrudMutationGuardAfterSuccess(container, {
62
+ tenantId: auth.tenantId,
63
+ organizationId: record.organizationId,
64
+ userId: auth.sub,
65
+ resourceKind: 'customer_accounts.domain_mapping',
66
+ resourceId: id,
67
+ operation: 'update',
68
+ requestMethod: req.method,
69
+ requestHeaders: req.headers,
70
+ metadata: guardResult.metadata ?? null,
71
+ })
72
+ }
73
+
74
+ return NextResponse.json({
75
+ ok: true,
76
+ domainMapping: {
77
+ id: updated.id,
78
+ hostname: updated.hostname,
79
+ status: updated.status,
80
+ tlsRetryCount: updated.tlsRetryCount,
81
+ tlsFailureReason: updated.tlsFailureReason ?? null,
82
+ },
83
+ })
84
+ }
85
+
86
+ const errorSchema = z.object({ ok: z.literal(false), error: z.string() })
87
+
88
+ export const openApi: OpenApiRouteDoc = {
89
+ tag: 'CustomerAccounts',
90
+ summary: 'TLS health check for a custom domain mapping',
91
+ methods: {
92
+ POST: {
93
+ summary: 'Trigger TLS health check',
94
+ description:
95
+ 'Runs an HTTPS probe to verify Traefik has provisioned a certificate. On success: transitions the mapping to active.',
96
+ responses: [
97
+ {
98
+ status: 200,
99
+ description: 'OK',
100
+ schema: z.object({
101
+ ok: z.literal(true),
102
+ domainMapping: z.object({
103
+ id: z.string().uuid(),
104
+ hostname: z.string(),
105
+ status: z.string(),
106
+ tlsRetryCount: z.number().int().nonnegative(),
107
+ tlsFailureReason: z.string().nullable(),
108
+ }),
109
+ }),
110
+ },
111
+ ],
112
+ errors: [
113
+ { status: 401, description: 'Unauthorized', schema: errorSchema },
114
+ { status: 403, description: 'Forbidden', schema: errorSchema },
115
+ { status: 404, description: 'Not found', schema: errorSchema },
116
+ ],
117
+ },
118
+ },
119
+ }
@@ -0,0 +1,123 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
+ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
+ import { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
7
+ import {
8
+ validateCrudMutationGuard,
9
+ runCrudMutationGuardAfterSuccess,
10
+ } from '@open-mercato/shared/lib/crud/mutation-guard'
11
+ import { DomainMappingService } from '@open-mercato/core/modules/customer_accounts/services/domainMappingService'
12
+
13
+ const FEATURE = 'customer_accounts.domain.manage'
14
+
15
+ export const metadata = {
16
+ POST: { requireAuth: true, requireFeatures: [FEATURE] },
17
+ }
18
+
19
+ export async function POST(req: Request, ctx: { params: Promise<{ id: string }> }) {
20
+ const auth = await getAuthFromRequest(req)
21
+ if (!auth || !auth.tenantId) return NextResponse.json({ ok: false, error: 'Unauthorized' }, { status: 401 })
22
+
23
+ const container = await createRequestContainer()
24
+ const rbac = container.resolve('rbacService') as RbacService
25
+ const allowed = await rbac.userHasAllFeatures(auth.sub, [FEATURE], {
26
+ tenantId: auth.tenantId,
27
+ organizationId: auth.orgId,
28
+ })
29
+ if (!allowed) return NextResponse.json({ ok: false, error: 'Forbidden' }, { status: 403 })
30
+
31
+ const { id } = await ctx.params
32
+ const service = container.resolve('domainMappingService') as DomainMappingService
33
+ const record = await service.findById(id, { tenantId: auth.tenantId })
34
+ if (!record) return NextResponse.json({ ok: false, error: 'Not found' }, { status: 404 })
35
+
36
+ const guardResult = await validateCrudMutationGuard(container, {
37
+ tenantId: auth.tenantId,
38
+ organizationId: record.organizationId,
39
+ userId: auth.sub,
40
+ resourceKind: 'customer_accounts.domain_mapping',
41
+ resourceId: id,
42
+ operation: 'update',
43
+ requestMethod: req.method,
44
+ requestHeaders: req.headers,
45
+ })
46
+ if (guardResult && !guardResult.ok) {
47
+ return NextResponse.json(guardResult.body, { status: guardResult.status })
48
+ }
49
+
50
+ let result
51
+ try {
52
+ result = await service.verify(id)
53
+ } catch (err) {
54
+ return NextResponse.json(
55
+ { ok: false, error: err instanceof Error ? err.message : 'DNS verification failed' },
56
+ { status: 500 },
57
+ )
58
+ }
59
+
60
+ if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
61
+ await runCrudMutationGuardAfterSuccess(container, {
62
+ tenantId: auth.tenantId,
63
+ organizationId: record.organizationId,
64
+ userId: auth.sub,
65
+ resourceKind: 'customer_accounts.domain_mapping',
66
+ resourceId: id,
67
+ operation: 'update',
68
+ requestMethod: req.method,
69
+ requestHeaders: req.headers,
70
+ metadata: guardResult.metadata ?? null,
71
+ })
72
+ }
73
+
74
+ return NextResponse.json({
75
+ ok: true,
76
+ domainMapping: {
77
+ id: result.domainMapping.id,
78
+ hostname: result.domainMapping.hostname,
79
+ status: result.domainMapping.status,
80
+ verifiedAt: result.domainMapping.verifiedAt?.toISOString() ?? null,
81
+ lastDnsCheckAt: result.domainMapping.lastDnsCheckAt?.toISOString() ?? null,
82
+ dnsFailureReason: result.domainMapping.dnsFailureReason ?? null,
83
+ },
84
+ diagnostics: result.diagnostics ?? null,
85
+ })
86
+ }
87
+
88
+ const errorSchema = z.object({ ok: z.literal(false), error: z.string() })
89
+
90
+ export const openApi: OpenApiRouteDoc = {
91
+ tag: 'CustomerAccounts',
92
+ summary: 'Verify a custom domain mapping (DNS check)',
93
+ methods: {
94
+ POST: {
95
+ summary: 'Trigger DNS verification',
96
+ description:
97
+ 'Runs DNS verification (CNAME → A → reverse-resolve fallback) for the domain mapping. Returns diagnostics on failure.',
98
+ responses: [
99
+ {
100
+ status: 200,
101
+ description: 'OK — see status',
102
+ schema: z.object({
103
+ ok: z.literal(true),
104
+ domainMapping: z.object({
105
+ id: z.string().uuid(),
106
+ hostname: z.string(),
107
+ status: z.string(),
108
+ verifiedAt: z.string().nullable(),
109
+ lastDnsCheckAt: z.string().nullable(),
110
+ dnsFailureReason: z.string().nullable(),
111
+ }),
112
+ diagnostics: z.unknown().nullable(),
113
+ }),
114
+ },
115
+ ],
116
+ errors: [
117
+ { status: 401, description: 'Unauthorized', schema: errorSchema },
118
+ { status: 403, description: 'Forbidden', schema: errorSchema },
119
+ { status: 404, description: 'Not found', schema: errorSchema },
120
+ ],
121
+ },
122
+ },
123
+ }