@open-mercato/core 0.4.8-develop-28cee031d6 → 0.4.8-develop-15259be22b

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 (333) hide show
  1. package/agentic/standalone-guide.md +235 -0
  2. package/dist/generated/entities/customer_role/index.js +27 -0
  3. package/dist/generated/entities/customer_role/index.js.map +7 -0
  4. package/dist/generated/entities/customer_role_acl/index.js +19 -0
  5. package/dist/generated/entities/customer_role_acl/index.js.map +7 -0
  6. package/dist/generated/entities/customer_user/index.js +37 -0
  7. package/dist/generated/entities/customer_user/index.js.map +7 -0
  8. package/dist/generated/entities/customer_user_acl/index.js +19 -0
  9. package/dist/generated/entities/customer_user_acl/index.js.map +7 -0
  10. package/dist/generated/entities/customer_user_email_verification/index.js +17 -0
  11. package/dist/generated/entities/customer_user_email_verification/index.js.map +7 -0
  12. package/dist/generated/entities/customer_user_invitation/index.js +33 -0
  13. package/dist/generated/entities/customer_user_invitation/index.js.map +7 -0
  14. package/dist/generated/entities/customer_user_password_reset/index.js +15 -0
  15. package/dist/generated/entities/customer_user_password_reset/index.js.map +7 -0
  16. package/dist/generated/entities/customer_user_role/index.js +13 -0
  17. package/dist/generated/entities/customer_user_role/index.js.map +7 -0
  18. package/dist/generated/entities/customer_user_session/index.js +21 -0
  19. package/dist/generated/entities/customer_user_session/index.js.map +7 -0
  20. package/dist/generated/entities/organization/index.js +2 -0
  21. package/dist/generated/entities/organization/index.js.map +2 -2
  22. package/dist/generated/entities.ids.generated.js +14 -1
  23. package/dist/generated/entities.ids.generated.js.map +2 -2
  24. package/dist/generated/entity-fields-registry.js +18 -0
  25. package/dist/generated/entity-fields-registry.js.map +2 -2
  26. package/dist/modules/auth/services/rbacService.js +3 -9
  27. package/dist/modules/auth/services/rbacService.js.map +2 -2
  28. package/dist/modules/customer_accounts/acl.js +12 -0
  29. package/dist/modules/customer_accounts/acl.js.map +7 -0
  30. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +87 -0
  31. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +7 -0
  32. package/dist/modules/customer_accounts/api/admin/roles/[id].js +216 -0
  33. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +7 -0
  34. package/dist/modules/customer_accounts/api/admin/roles.js +189 -0
  35. package/dist/modules/customer_accounts/api/admin/roles.js.map +7 -0
  36. package/dist/modules/customer_accounts/api/admin/users/[id]/reset-password.js +69 -0
  37. package/dist/modules/customer_accounts/api/admin/users/[id]/reset-password.js.map +7 -0
  38. package/dist/modules/customer_accounts/api/admin/users/[id]/verify-email.js +64 -0
  39. package/dist/modules/customer_accounts/api/admin/users/[id]/verify-email.js.map +7 -0
  40. package/dist/modules/customer_accounts/api/admin/users/[id].js +253 -0
  41. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +7 -0
  42. package/dist/modules/customer_accounts/api/admin/users-invite.js +78 -0
  43. package/dist/modules/customer_accounts/api/admin/users-invite.js.map +7 -0
  44. package/dist/modules/customer_accounts/api/admin/users.js +251 -0
  45. package/dist/modules/customer_accounts/api/admin/users.js.map +7 -0
  46. package/dist/modules/customer_accounts/api/email/verify.js +59 -0
  47. package/dist/modules/customer_accounts/api/email/verify.js.map +7 -0
  48. package/dist/modules/customer_accounts/api/interceptors.js +5 -0
  49. package/dist/modules/customer_accounts/api/interceptors.js.map +7 -0
  50. package/dist/modules/customer_accounts/api/invitations/accept.js +114 -0
  51. package/dist/modules/customer_accounts/api/invitations/accept.js.map +7 -0
  52. package/dist/modules/customer_accounts/api/login.js +143 -0
  53. package/dist/modules/customer_accounts/api/login.js.map +7 -0
  54. package/dist/modules/customer_accounts/api/magic-link/request.js +78 -0
  55. package/dist/modules/customer_accounts/api/magic-link/request.js.map +7 -0
  56. package/dist/modules/customer_accounts/api/magic-link/verify.js +114 -0
  57. package/dist/modules/customer_accounts/api/magic-link/verify.js.map +7 -0
  58. package/dist/modules/customer_accounts/api/password/reset-confirm.js +59 -0
  59. package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +7 -0
  60. package/dist/modules/customer_accounts/api/password/reset-request.js +77 -0
  61. package/dist/modules/customer_accounts/api/password/reset-request.js.map +7 -0
  62. package/dist/modules/customer_accounts/api/portal/events/stream.js +163 -0
  63. package/dist/modules/customer_accounts/api/portal/events/stream.js.map +7 -0
  64. package/dist/modules/customer_accounts/api/portal/feature-check.js +57 -0
  65. package/dist/modules/customer_accounts/api/portal/feature-check.js.map +7 -0
  66. package/dist/modules/customer_accounts/api/portal/logout.js +64 -0
  67. package/dist/modules/customer_accounts/api/portal/logout.js.map +7 -0
  68. package/dist/modules/customer_accounts/api/portal/notifications/[id]/dismiss.js +49 -0
  69. package/dist/modules/customer_accounts/api/portal/notifications/[id]/dismiss.js.map +7 -0
  70. package/dist/modules/customer_accounts/api/portal/notifications/[id]/read.js +49 -0
  71. package/dist/modules/customer_accounts/api/portal/notifications/[id]/read.js.map +7 -0
  72. package/dist/modules/customer_accounts/api/portal/notifications/mark-all-read.js +46 -0
  73. package/dist/modules/customer_accounts/api/portal/notifications/mark-all-read.js.map +7 -0
  74. package/dist/modules/customer_accounts/api/portal/notifications/unread-count.js +42 -0
  75. package/dist/modules/customer_accounts/api/portal/notifications/unread-count.js.map +7 -0
  76. package/dist/modules/customer_accounts/api/portal/notifications.js +105 -0
  77. package/dist/modules/customer_accounts/api/portal/notifications.js.map +7 -0
  78. package/dist/modules/customer_accounts/api/portal/password-change.js +57 -0
  79. package/dist/modules/customer_accounts/api/portal/password-change.js.map +7 -0
  80. package/dist/modules/customer_accounts/api/portal/profile.js +135 -0
  81. package/dist/modules/customer_accounts/api/portal/profile.js.map +7 -0
  82. package/dist/modules/customer_accounts/api/portal/sessions/[id].js +62 -0
  83. package/dist/modules/customer_accounts/api/portal/sessions/[id].js.map +7 -0
  84. package/dist/modules/customer_accounts/api/portal/sessions-refresh.js +75 -0
  85. package/dist/modules/customer_accounts/api/portal/sessions-refresh.js.map +7 -0
  86. package/dist/modules/customer_accounts/api/portal/sessions.js +77 -0
  87. package/dist/modules/customer_accounts/api/portal/sessions.js.map +7 -0
  88. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +90 -0
  89. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +7 -0
  90. package/dist/modules/customer_accounts/api/portal/users/[id].js +71 -0
  91. package/dist/modules/customer_accounts/api/portal/users/[id].js.map +7 -0
  92. package/dist/modules/customer_accounts/api/portal/users-invite.js +92 -0
  93. package/dist/modules/customer_accounts/api/portal/users-invite.js.map +7 -0
  94. package/dist/modules/customer_accounts/api/portal/users.js +79 -0
  95. package/dist/modules/customer_accounts/api/portal/users.js.map +7 -0
  96. package/dist/modules/customer_accounts/api/signup.js +121 -0
  97. package/dist/modules/customer_accounts/api/signup.js.map +7 -0
  98. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.js +491 -0
  99. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.js.map +7 -0
  100. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.meta.js +15 -0
  101. package/dist/modules/customer_accounts/backend/customer_accounts/[id]/page.meta.js.map +7 -0
  102. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +343 -0
  103. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +7 -0
  104. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.meta.js +16 -0
  105. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.meta.js.map +7 -0
  106. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.js +180 -0
  107. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.js.map +7 -0
  108. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.meta.js +16 -0
  109. package/dist/modules/customer_accounts/backend/customer_accounts/roles/create/page.meta.js.map +7 -0
  110. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +176 -0
  111. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +7 -0
  112. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.meta.js +33 -0
  113. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.meta.js.map +7 -0
  114. package/dist/modules/customer_accounts/backend/page.js +466 -0
  115. package/dist/modules/customer_accounts/backend/page.js.map +7 -0
  116. package/dist/modules/customer_accounts/backend/page.meta.js +35 -0
  117. package/dist/modules/customer_accounts/backend/page.meta.js.map +7 -0
  118. package/dist/modules/customer_accounts/ce.js +26 -0
  119. package/dist/modules/customer_accounts/ce.js.map +7 -0
  120. package/dist/modules/customer_accounts/data/enrichers.js +85 -0
  121. package/dist/modules/customer_accounts/data/enrichers.js.map +7 -0
  122. package/dist/modules/customer_accounts/data/entities.js +377 -0
  123. package/dist/modules/customer_accounts/data/entities.js.map +7 -0
  124. package/dist/modules/customer_accounts/data/extensions.js +8 -0
  125. package/dist/modules/customer_accounts/data/extensions.js.map +7 -0
  126. package/dist/modules/customer_accounts/data/validators.js +111 -0
  127. package/dist/modules/customer_accounts/data/validators.js.map +7 -0
  128. package/dist/modules/customer_accounts/di.js +17 -0
  129. package/dist/modules/customer_accounts/di.js.map +7 -0
  130. package/dist/modules/customer_accounts/events.js +28 -0
  131. package/dist/modules/customer_accounts/events.js.map +7 -0
  132. package/dist/modules/customer_accounts/index.js +15 -0
  133. package/dist/modules/customer_accounts/index.js.map +7 -0
  134. package/dist/modules/customer_accounts/lib/customerAuth.js +71 -0
  135. package/dist/modules/customer_accounts/lib/customerAuth.js.map +7 -0
  136. package/dist/modules/customer_accounts/lib/customerAuthServer.js +29 -0
  137. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +7 -0
  138. package/dist/modules/customer_accounts/lib/rateLimiter.js +63 -0
  139. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +7 -0
  140. package/dist/modules/customer_accounts/lib/tokenGenerator.js +12 -0
  141. package/dist/modules/customer_accounts/lib/tokenGenerator.js.map +7 -0
  142. package/dist/modules/customer_accounts/migrations/Migration20260313222043.js +49 -0
  143. package/dist/modules/customer_accounts/migrations/Migration20260313222043.js.map +7 -0
  144. package/dist/modules/customer_accounts/notifications.client.js +47 -0
  145. package/dist/modules/customer_accounts/notifications.client.js.map +7 -0
  146. package/dist/modules/customer_accounts/notifications.js +46 -0
  147. package/dist/modules/customer_accounts/notifications.js.map +7 -0
  148. package/dist/modules/customer_accounts/search.js +120 -0
  149. package/dist/modules/customer_accounts/search.js.map +7 -0
  150. package/dist/modules/customer_accounts/services/customerInvitationService.js +87 -0
  151. package/dist/modules/customer_accounts/services/customerInvitationService.js.map +7 -0
  152. package/dist/modules/customer_accounts/services/customerRbacService.js +109 -0
  153. package/dist/modules/customer_accounts/services/customerRbacService.js.map +7 -0
  154. package/dist/modules/customer_accounts/services/customerSessionService.js +75 -0
  155. package/dist/modules/customer_accounts/services/customerSessionService.js.map +7 -0
  156. package/dist/modules/customer_accounts/services/customerTokenService.js +91 -0
  157. package/dist/modules/customer_accounts/services/customerTokenService.js.map +7 -0
  158. package/dist/modules/customer_accounts/services/customerUserService.js +92 -0
  159. package/dist/modules/customer_accounts/services/customerUserService.js.map +7 -0
  160. package/dist/modules/customer_accounts/setup.js +179 -0
  161. package/dist/modules/customer_accounts/setup.js.map +7 -0
  162. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +54 -0
  163. package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +7 -0
  164. package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js +68 -0
  165. package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js.map +7 -0
  166. package/dist/modules/customer_accounts/subscribers/notifyStaffOnSignup.js +29 -0
  167. package/dist/modules/customer_accounts/subscribers/notifyStaffOnSignup.js.map +7 -0
  168. package/dist/modules/customer_accounts/translations.js +9 -0
  169. package/dist/modules/customer_accounts/translations.js.map +7 -0
  170. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +63 -0
  171. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +7 -0
  172. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.js +17 -0
  173. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.js.map +7 -0
  174. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.client.js +55 -0
  175. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.client.js.map +7 -0
  176. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.js +17 -0
  177. package/dist/modules/customer_accounts/widgets/injection/company-users/widget.js.map +7 -0
  178. package/dist/modules/customer_accounts/widgets/injection-table.js +26 -0
  179. package/dist/modules/customer_accounts/widgets/injection-table.js.map +7 -0
  180. package/dist/modules/customer_accounts/workers/cleanupExpiredSessions.js +23 -0
  181. package/dist/modules/customer_accounts/workers/cleanupExpiredSessions.js.map +7 -0
  182. package/dist/modules/customer_accounts/workers/cleanupExpiredTokens.js +38 -0
  183. package/dist/modules/customer_accounts/workers/cleanupExpiredTokens.js.map +7 -0
  184. package/dist/modules/customers/components/AddressTiles.js +1 -1
  185. package/dist/modules/customers/components/AddressTiles.js.map +2 -2
  186. package/dist/modules/directory/api/get/organizations/lookup.js +83 -0
  187. package/dist/modules/directory/api/get/organizations/lookup.js.map +7 -0
  188. package/dist/modules/directory/commands/organizations.js +32 -1
  189. package/dist/modules/directory/commands/organizations.js.map +2 -2
  190. package/dist/modules/directory/data/entities.js +6 -2
  191. package/dist/modules/directory/data/entities.js.map +2 -2
  192. package/dist/modules/directory/data/validators.js +3 -0
  193. package/dist/modules/directory/data/validators.js.map +2 -2
  194. package/dist/modules/directory/migrations/Migration20260314143323.js +15 -0
  195. package/dist/modules/directory/migrations/Migration20260314143323.js.map +7 -0
  196. package/dist/modules/directory/setup.js +36 -0
  197. package/dist/modules/directory/setup.js.map +2 -2
  198. package/dist/modules/payment_gateways/migrations/Migration20260313222043.js +15 -0
  199. package/dist/modules/payment_gateways/migrations/Migration20260313222043.js.map +7 -0
  200. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +131 -0
  201. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +7 -0
  202. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +96 -0
  203. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +7 -0
  204. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +94 -0
  205. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +7 -0
  206. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +89 -0
  207. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +7 -0
  208. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +104 -0
  209. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +7 -0
  210. package/dist/modules/portal/index.js +11 -0
  211. package/dist/modules/portal/index.js.map +7 -0
  212. package/dist/modules/portal/setup.js +23 -0
  213. package/dist/modules/portal/setup.js.map +7 -0
  214. package/generated/entities/customer_role/index.ts +12 -0
  215. package/generated/entities/customer_role_acl/index.ts +8 -0
  216. package/generated/entities/customer_user/index.ts +17 -0
  217. package/generated/entities/customer_user_acl/index.ts +8 -0
  218. package/generated/entities/customer_user_email_verification/index.ts +7 -0
  219. package/generated/entities/customer_user_invitation/index.ts +15 -0
  220. package/generated/entities/customer_user_password_reset/index.ts +6 -0
  221. package/generated/entities/customer_user_role/index.ts +5 -0
  222. package/generated/entities/customer_user_session/index.ts +9 -0
  223. package/generated/entities/organization/index.ts +1 -0
  224. package/generated/entities.ids.generated.ts +14 -1
  225. package/generated/entity-fields-registry.ts +18 -0
  226. package/package.json +3 -3
  227. package/src/modules/auth/services/rbacService.ts +3 -9
  228. package/src/modules/customer_accounts/AGENTS.md +377 -0
  229. package/src/modules/customer_accounts/acl.ts +8 -0
  230. package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +98 -0
  231. package/src/modules/customer_accounts/api/admin/roles/[id].ts +246 -0
  232. package/src/modules/customer_accounts/api/admin/roles.ts +212 -0
  233. package/src/modules/customer_accounts/api/admin/users/[id]/reset-password.ts +78 -0
  234. package/src/modules/customer_accounts/api/admin/users/[id]/verify-email.ts +72 -0
  235. package/src/modules/customer_accounts/api/admin/users/[id].ts +289 -0
  236. package/src/modules/customer_accounts/api/admin/users-invite.ts +86 -0
  237. package/src/modules/customer_accounts/api/admin/users.ts +280 -0
  238. package/src/modules/customer_accounts/api/email/verify.ts +66 -0
  239. package/src/modules/customer_accounts/api/interceptors.ts +3 -0
  240. package/src/modules/customer_accounts/api/invitations/accept.ts +128 -0
  241. package/src/modules/customer_accounts/api/login.ts +163 -0
  242. package/src/modules/customer_accounts/api/magic-link/request.ts +87 -0
  243. package/src/modules/customer_accounts/api/magic-link/verify.ts +132 -0
  244. package/src/modules/customer_accounts/api/password/reset-confirm.ts +69 -0
  245. package/src/modules/customer_accounts/api/password/reset-request.ts +87 -0
  246. package/src/modules/customer_accounts/api/portal/events/stream.ts +209 -0
  247. package/src/modules/customer_accounts/api/portal/feature-check.ts +60 -0
  248. package/src/modules/customer_accounts/api/portal/logout.ts +71 -0
  249. package/src/modules/customer_accounts/api/portal/notifications/[id]/dismiss.ts +54 -0
  250. package/src/modules/customer_accounts/api/portal/notifications/[id]/read.ts +54 -0
  251. package/src/modules/customer_accounts/api/portal/notifications/mark-all-read.ts +49 -0
  252. package/src/modules/customer_accounts/api/portal/notifications/unread-count.ts +45 -0
  253. package/src/modules/customer_accounts/api/portal/notifications.ts +115 -0
  254. package/src/modules/customer_accounts/api/portal/password-change.ts +65 -0
  255. package/src/modules/customer_accounts/api/portal/profile.ts +151 -0
  256. package/src/modules/customer_accounts/api/portal/sessions/[id].ts +70 -0
  257. package/src/modules/customer_accounts/api/portal/sessions-refresh.ts +87 -0
  258. package/src/modules/customer_accounts/api/portal/sessions.ts +84 -0
  259. package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +106 -0
  260. package/src/modules/customer_accounts/api/portal/users/[id].ts +81 -0
  261. package/src/modules/customer_accounts/api/portal/users-invite.ts +103 -0
  262. package/src/modules/customer_accounts/api/portal/users.ts +86 -0
  263. package/src/modules/customer_accounts/api/signup.ts +136 -0
  264. package/src/modules/customer_accounts/backend/customer_accounts/[id]/page.meta.ts +11 -0
  265. package/src/modules/customer_accounts/backend/customer_accounts/[id]/page.tsx +607 -0
  266. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.meta.ts +12 -0
  267. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +385 -0
  268. package/src/modules/customer_accounts/backend/customer_accounts/roles/create/page.meta.ts +12 -0
  269. package/src/modules/customer_accounts/backend/customer_accounts/roles/create/page.tsx +203 -0
  270. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.meta.ts +31 -0
  271. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +217 -0
  272. package/src/modules/customer_accounts/backend/page.meta.ts +33 -0
  273. package/src/modules/customer_accounts/backend/page.tsx +535 -0
  274. package/src/modules/customer_accounts/ce.ts +22 -0
  275. package/src/modules/customer_accounts/data/enrichers.ts +117 -0
  276. package/src/modules/customer_accounts/data/entities.ts +302 -0
  277. package/src/modules/customer_accounts/data/extensions.ts +4 -0
  278. package/src/modules/customer_accounts/data/validators.ts +128 -0
  279. package/src/modules/customer_accounts/di.ts +15 -0
  280. package/src/modules/customer_accounts/events.ts +28 -0
  281. package/src/modules/customer_accounts/i18n/de.json +176 -0
  282. package/src/modules/customer_accounts/i18n/en.json +176 -0
  283. package/src/modules/customer_accounts/i18n/es.json +176 -0
  284. package/src/modules/customer_accounts/i18n/pl.json +176 -0
  285. package/src/modules/customer_accounts/index.ts +13 -0
  286. package/src/modules/customer_accounts/lib/customerAuth.ts +85 -0
  287. package/src/modules/customer_accounts/lib/customerAuthServer.ts +54 -0
  288. package/src/modules/customer_accounts/lib/rateLimiter.ts +36 -0
  289. package/src/modules/customer_accounts/lib/tokenGenerator.ts +9 -0
  290. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +1255 -0
  291. package/src/modules/customer_accounts/migrations/Migration20260313222043.ts +62 -0
  292. package/src/modules/customer_accounts/notifications.client.ts +46 -0
  293. package/src/modules/customer_accounts/notifications.ts +44 -0
  294. package/src/modules/customer_accounts/search.ts +134 -0
  295. package/src/modules/customer_accounts/services/customerInvitationService.ts +109 -0
  296. package/src/modules/customer_accounts/services/customerRbacService.ts +144 -0
  297. package/src/modules/customer_accounts/services/customerSessionService.ts +90 -0
  298. package/src/modules/customer_accounts/services/customerTokenService.ts +98 -0
  299. package/src/modules/customer_accounts/services/customerUserService.ts +105 -0
  300. package/src/modules/customer_accounts/setup.ts +212 -0
  301. package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +65 -0
  302. package/src/modules/customer_accounts/subscribers/autoLinkCrmReverse.ts +78 -0
  303. package/src/modules/customer_accounts/subscribers/notifyStaffOnSignup.ts +32 -0
  304. package/src/modules/customer_accounts/translations.ts +5 -0
  305. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +89 -0
  306. package/src/modules/customer_accounts/widgets/injection/account-status/widget.ts +16 -0
  307. package/src/modules/customer_accounts/widgets/injection/company-users/widget.client.tsx +78 -0
  308. package/src/modules/customer_accounts/widgets/injection/company-users/widget.ts +16 -0
  309. package/src/modules/customer_accounts/widgets/injection-table.ts +24 -0
  310. package/src/modules/customer_accounts/workers/cleanupExpiredSessions.ts +33 -0
  311. package/src/modules/customer_accounts/workers/cleanupExpiredTokens.ts +51 -0
  312. package/src/modules/customers/components/AddressTiles.tsx +1 -1
  313. package/src/modules/directory/api/get/organizations/lookup.ts +92 -0
  314. package/src/modules/directory/commands/organizations.ts +34 -1
  315. package/src/modules/directory/data/entities.ts +5 -1
  316. package/src/modules/directory/data/validators.ts +4 -0
  317. package/src/modules/directory/migrations/.snapshot-open-mercato.json +20 -1
  318. package/src/modules/directory/migrations/Migration20260314143323.ts +15 -0
  319. package/src/modules/directory/setup.ts +41 -0
  320. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +4 -1
  321. package/src/modules/payment_gateways/migrations/Migration20260313222043.ts +17 -0
  322. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +158 -0
  323. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +120 -0
  324. package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +118 -0
  325. package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +112 -0
  326. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +138 -0
  327. package/src/modules/portal/i18n/de.json +93 -0
  328. package/src/modules/portal/i18n/en.json +93 -0
  329. package/src/modules/portal/i18n/es.json +93 -0
  330. package/src/modules/portal/i18n/pl.json +93 -0
  331. package/src/modules/portal/index.ts +9 -0
  332. package/src/modules/portal/setup.ts +23 -0
  333. package/src/modules/shipping_carriers/migrations/.snapshot-open-mercato.json +226 -0
@@ -0,0 +1,216 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { CustomerRole, CustomerRoleAcl, CustomerUserRole } from "@open-mercato/core/modules/customer_accounts/data/entities";
6
+ import { updateRoleSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
7
+ import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
8
+ const metadata = {};
9
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+ async function GET(req, { params }) {
11
+ if (!UUID_RE.test(params.id)) {
12
+ return NextResponse.json({ ok: false, error: "Invalid role ID" }, { status: 400 });
13
+ }
14
+ const auth = await getAuthFromRequest(req);
15
+ if (!auth) {
16
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
17
+ }
18
+ const container = await createRequestContainer();
19
+ const rbacService = container.resolve("rbacService");
20
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.view"], { tenantId: auth.tenantId, organizationId: auth.orgId });
21
+ if (!hasAccess) {
22
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
23
+ }
24
+ const em = container.resolve("em");
25
+ const role = await em.findOne(CustomerRole, {
26
+ id: params.id,
27
+ tenantId: auth.tenantId,
28
+ deletedAt: null
29
+ });
30
+ if (!role) {
31
+ return NextResponse.json({ ok: false, error: "Role not found" }, { status: 404 });
32
+ }
33
+ const acl = await em.findOne(CustomerRoleAcl, {
34
+ role: role.id,
35
+ tenantId: auth.tenantId
36
+ });
37
+ const features = acl && Array.isArray(acl.featuresJson) ? acl.featuresJson : [];
38
+ return NextResponse.json({
39
+ ok: true,
40
+ id: role.id,
41
+ name: role.name,
42
+ slug: role.slug,
43
+ description: role.description || null,
44
+ isDefault: role.isDefault,
45
+ isSystem: role.isSystem,
46
+ customerAssignable: role.customerAssignable,
47
+ createdAt: role.createdAt,
48
+ updatedAt: role.updatedAt || null,
49
+ features
50
+ });
51
+ }
52
+ async function PUT(req, { params }) {
53
+ const auth = await getAuthFromRequest(req);
54
+ if (!auth) {
55
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
56
+ }
57
+ const container = await createRequestContainer();
58
+ const rbacService = container.resolve("rbacService");
59
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.roles.manage"], { tenantId: auth.tenantId, organizationId: auth.orgId });
60
+ if (!hasAccess) {
61
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
62
+ }
63
+ let body;
64
+ try {
65
+ body = await req.json();
66
+ } catch {
67
+ return NextResponse.json({ ok: false, error: "Invalid request body" }, { status: 400 });
68
+ }
69
+ const parsed = updateRoleSchema.safeParse(body);
70
+ if (!parsed.success) {
71
+ return NextResponse.json({ ok: false, error: "Validation failed", details: parsed.error.flatten().fieldErrors }, { status: 400 });
72
+ }
73
+ const em = container.resolve("em");
74
+ const role = await em.findOne(CustomerRole, {
75
+ id: params.id,
76
+ tenantId: auth.tenantId,
77
+ deletedAt: null
78
+ });
79
+ if (!role) {
80
+ return NextResponse.json({ ok: false, error: "Role not found" }, { status: 404 });
81
+ }
82
+ if (role.isSystem && parsed.data.name !== void 0) {
83
+ return NextResponse.json({ ok: false, error: "Cannot change name of a system role" }, { status: 400 });
84
+ }
85
+ const updates = {};
86
+ if (parsed.data.name !== void 0) updates.name = parsed.data.name;
87
+ if (parsed.data.description !== void 0) updates.description = parsed.data.description;
88
+ if (parsed.data.isDefault !== void 0) updates.isDefault = parsed.data.isDefault;
89
+ if (parsed.data.customerAssignable !== void 0) updates.customerAssignable = parsed.data.customerAssignable;
90
+ if (Object.keys(updates).length > 0) {
91
+ await em.nativeUpdate(CustomerRole, { id: role.id }, updates);
92
+ }
93
+ void emitCustomerAccountsEvent("customer_accounts.role.updated", {
94
+ id: role.id,
95
+ name: updates.name || role.name,
96
+ tenantId: auth.tenantId,
97
+ organizationId: auth.orgId
98
+ }).catch(() => void 0);
99
+ return NextResponse.json({ ok: true });
100
+ }
101
+ async function DELETE(req, { params }) {
102
+ const auth = await getAuthFromRequest(req);
103
+ if (!auth) {
104
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
105
+ }
106
+ const container = await createRequestContainer();
107
+ const rbacService = container.resolve("rbacService");
108
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.roles.manage"], { tenantId: auth.tenantId, organizationId: auth.orgId });
109
+ if (!hasAccess) {
110
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
111
+ }
112
+ const em = container.resolve("em");
113
+ const role = await em.findOne(CustomerRole, {
114
+ id: params.id,
115
+ tenantId: auth.tenantId,
116
+ deletedAt: null
117
+ });
118
+ if (!role) {
119
+ return NextResponse.json({ ok: false, error: "Role not found" }, { status: 404 });
120
+ }
121
+ if (role.isSystem) {
122
+ return NextResponse.json({ ok: false, error: "Cannot delete a system role" }, { status: 400 });
123
+ }
124
+ const assignedUsersCount = await em.count(CustomerUserRole, {
125
+ role: role.id,
126
+ deletedAt: null
127
+ });
128
+ if (assignedUsersCount > 0) {
129
+ return NextResponse.json({ ok: false, error: `Cannot delete role with ${assignedUsersCount} assigned user(s). Reassign users first.` }, { status: 400 });
130
+ }
131
+ await em.nativeUpdate(CustomerRole, { id: role.id }, { deletedAt: /* @__PURE__ */ new Date() });
132
+ await em.nativeUpdate(CustomerRoleAcl, { role: role.id }, { deletedAt: /* @__PURE__ */ new Date() });
133
+ void emitCustomerAccountsEvent("customer_accounts.role.deleted", {
134
+ id: role.id,
135
+ name: role.name,
136
+ slug: role.slug,
137
+ tenantId: auth.tenantId,
138
+ organizationId: auth.orgId
139
+ }).catch(() => void 0);
140
+ return NextResponse.json({ ok: true });
141
+ }
142
+ const aclSchema = z.object({
143
+ features: z.array(z.string()),
144
+ isPortalAdmin: z.boolean()
145
+ });
146
+ const roleDetailSchema = z.object({
147
+ id: z.string().uuid(),
148
+ name: z.string(),
149
+ slug: z.string(),
150
+ description: z.string().nullable(),
151
+ isDefault: z.boolean(),
152
+ isSystem: z.boolean(),
153
+ customerAssignable: z.boolean(),
154
+ createdAt: z.string().datetime(),
155
+ updatedAt: z.string().datetime().nullable(),
156
+ acl: aclSchema
157
+ });
158
+ const successSchema = z.object({ ok: z.literal(true) });
159
+ const errorSchema = z.object({ ok: z.literal(false), error: z.string() });
160
+ const getMethodDoc = {
161
+ summary: "Get customer role detail (admin)",
162
+ description: "Returns full customer role details including ACL features.",
163
+ tags: ["Customer Accounts Admin"],
164
+ responses: [{
165
+ status: 200,
166
+ description: "Role detail with ACL",
167
+ schema: z.object({ ok: z.literal(true), role: roleDetailSchema })
168
+ }],
169
+ errors: [
170
+ { status: 401, description: "Not authenticated", schema: errorSchema },
171
+ { status: 403, description: "Insufficient permissions", schema: errorSchema },
172
+ { status: 404, description: "Role not found", schema: errorSchema }
173
+ ]
174
+ };
175
+ const putMethodDoc = {
176
+ summary: "Update customer role (admin)",
177
+ description: "Updates a customer role. System roles are protected from name changes.",
178
+ tags: ["Customer Accounts Admin"],
179
+ requestBody: { schema: updateRoleSchema },
180
+ responses: [{ status: 200, description: "Role updated", schema: successSchema }],
181
+ errors: [
182
+ { status: 400, description: "Validation failed or system role restriction", schema: errorSchema },
183
+ { status: 401, description: "Not authenticated", schema: errorSchema },
184
+ { status: 403, description: "Insufficient permissions", schema: errorSchema },
185
+ { status: 404, description: "Role not found", schema: errorSchema }
186
+ ]
187
+ };
188
+ const deleteMethodDoc = {
189
+ summary: "Delete customer role (admin)",
190
+ description: "Soft deletes a customer role and its ACL. System roles and roles with assigned users cannot be deleted.",
191
+ tags: ["Customer Accounts Admin"],
192
+ responses: [{ status: 200, description: "Role deleted", schema: successSchema }],
193
+ errors: [
194
+ { status: 400, description: "System role or has assigned users", schema: errorSchema },
195
+ { status: 401, description: "Not authenticated", schema: errorSchema },
196
+ { status: 403, description: "Insufficient permissions", schema: errorSchema },
197
+ { status: 404, description: "Role not found", schema: errorSchema }
198
+ ]
199
+ };
200
+ const openApi = {
201
+ summary: "Customer role detail management (admin)",
202
+ pathParams: z.object({ id: z.string().uuid() }),
203
+ methods: {
204
+ GET: getMethodDoc,
205
+ PUT: putMethodDoc,
206
+ DELETE: deleteMethodDoc
207
+ }
208
+ };
209
+ export {
210
+ DELETE,
211
+ GET,
212
+ PUT,
213
+ metadata,
214
+ openApi
215
+ };
216
+ //# sourceMappingURL=%5Bid%5D.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customer_accounts/api/admin/roles/%5Bid%5D.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerRole, CustomerRoleAcl, CustomerUserRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { updateRoleSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\n\nexport const metadata = {}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport async function GET(req: Request, { params }: { params: { id: string } }) {\n if (!UUID_RE.test(params.id)) {\n return NextResponse.json({ ok: false, error: 'Invalid role ID' }, { status: 400 })\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.view'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n\n const role = await em.findOne(CustomerRole, {\n id: params.id,\n tenantId: auth.tenantId,\n deletedAt: null,\n })\n if (!role) {\n return NextResponse.json({ ok: false, error: 'Role not found' }, { status: 404 })\n }\n\n const acl = await em.findOne(CustomerRoleAcl, {\n role: role.id as any,\n tenantId: auth.tenantId,\n })\n\n const features = acl && Array.isArray(acl.featuresJson) ? acl.featuresJson : []\n\n return NextResponse.json({\n ok: true,\n id: role.id,\n name: role.name,\n slug: role.slug,\n description: role.description || null,\n isDefault: role.isDefault,\n isSystem: role.isSystem,\n customerAssignable: role.customerAssignable,\n createdAt: role.createdAt,\n updatedAt: role.updatedAt || null,\n features,\n })\n}\n\nexport async function PUT(req: Request, { params }: { params: { id: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.roles.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = updateRoleSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n\n const role = await em.findOne(CustomerRole, {\n id: params.id,\n tenantId: auth.tenantId,\n deletedAt: null,\n })\n if (!role) {\n return NextResponse.json({ ok: false, error: 'Role not found' }, { status: 404 })\n }\n\n if (role.isSystem && parsed.data.name !== undefined) {\n return NextResponse.json({ ok: false, error: 'Cannot change name of a system role' }, { status: 400 })\n }\n\n const updates: Record<string, unknown> = {}\n if (parsed.data.name !== undefined) updates.name = parsed.data.name\n if (parsed.data.description !== undefined) updates.description = parsed.data.description\n if (parsed.data.isDefault !== undefined) updates.isDefault = parsed.data.isDefault\n if (parsed.data.customerAssignable !== undefined) updates.customerAssignable = parsed.data.customerAssignable\n\n if (Object.keys(updates).length > 0) {\n await em.nativeUpdate(CustomerRole, { id: role.id }, updates)\n }\n\n void emitCustomerAccountsEvent('customer_accounts.role.updated', {\n id: role.id,\n name: updates.name as string || role.name,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n }).catch(() => undefined)\n\n return NextResponse.json({ ok: true })\n}\n\nexport async function DELETE(req: Request, { params }: { params: { id: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.roles.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n\n const role = await em.findOne(CustomerRole, {\n id: params.id,\n tenantId: auth.tenantId,\n deletedAt: null,\n })\n if (!role) {\n return NextResponse.json({ ok: false, error: 'Role not found' }, { status: 404 })\n }\n\n if (role.isSystem) {\n return NextResponse.json({ ok: false, error: 'Cannot delete a system role' }, { status: 400 })\n }\n\n const assignedUsersCount = await em.count(CustomerUserRole, {\n role: role.id as any,\n deletedAt: null,\n })\n if (assignedUsersCount > 0) {\n return NextResponse.json({ ok: false, error: `Cannot delete role with ${assignedUsersCount} assigned user(s). Reassign users first.` }, { status: 400 })\n }\n\n await em.nativeUpdate(CustomerRole, { id: role.id }, { deletedAt: new Date() })\n await em.nativeUpdate(CustomerRoleAcl, { role: role.id as any }, { deletedAt: new Date() })\n\n void emitCustomerAccountsEvent('customer_accounts.role.deleted', {\n id: role.id,\n name: role.name,\n slug: role.slug,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n }).catch(() => undefined)\n\n return NextResponse.json({ ok: true })\n}\n\nconst aclSchema = z.object({\n features: z.array(z.string()),\n isPortalAdmin: z.boolean(),\n})\nconst roleDetailSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n slug: z.string(),\n description: z.string().nullable(),\n isDefault: z.boolean(),\n isSystem: z.boolean(),\n customerAssignable: z.boolean(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime().nullable(),\n acl: aclSchema,\n})\n\nconst successSchema = z.object({ ok: z.literal(true) })\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nconst getMethodDoc: OpenApiMethodDoc = {\n summary: 'Get customer role detail (admin)',\n description: 'Returns full customer role details including ACL features.',\n tags: ['Customer Accounts Admin'],\n responses: [{\n status: 200,\n description: 'Role detail with ACL',\n schema: z.object({ ok: z.literal(true), role: roleDetailSchema }),\n }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n}\n\nconst putMethodDoc: OpenApiMethodDoc = {\n summary: 'Update customer role (admin)',\n description: 'Updates a customer role. System roles are protected from name changes.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: updateRoleSchema },\n responses: [{ status: 200, description: 'Role updated', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed or system role restriction', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n}\n\nconst deleteMethodDoc: OpenApiMethodDoc = {\n summary: 'Delete customer role (admin)',\n description: 'Soft deletes a customer role and its ACL. System roles and roles with assigned users cannot be deleted.',\n tags: ['Customer Accounts Admin'],\n responses: [{ status: 200, description: 'Role deleted', schema: successSchema }],\n errors: [\n { status: 400, description: 'System role or has assigned users', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer role detail management (admin)',\n pathParams: z.object({ id: z.string().uuid() }),\n methods: {\n GET: getMethodDoc,\n PUT: putMethodDoc,\n DELETE: deleteMethodDoc,\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,cAAc,iBAAiB,wBAAwB;AAChE,SAAS,wBAAwB;AACjC,SAAS,iCAAiC;AAEnC,MAAM,WAAW,CAAC;AAEzB,MAAM,UAAU;AAEhB,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA+B;AAC9E,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAE,GAAG;AAC5B,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kBAAkB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnF;AAEA,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3F;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,QAAM,YAAY,MAAM,YAAY,mBAAmB,KAAK,KAAK,CAAC,wBAAwB,GAAG,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,MAAM,CAAC;AACpJ,MAAI,CAAC,WAAW;AACd,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC5F;AAEA,QAAM,KAAK,UAAU,QAAQ,IAAI;AAEjC,QAAM,OAAO,MAAM,GAAG,QAAQ,cAAc;AAAA,IAC1C,IAAI,OAAO;AAAA,IACX,UAAU,KAAK;AAAA,IACf,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AAEA,QAAM,MAAM,MAAM,GAAG,QAAQ,iBAAiB;AAAA,IAC5C,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,EACjB,CAAC;AAED,QAAM,WAAW,OAAO,MAAM,QAAQ,IAAI,YAAY,IAAI,IAAI,eAAe,CAAC;AAE9E,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,aAAa,KAAK,eAAe;AAAA,IACjC,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,oBAAoB,KAAK;AAAA,IACzB,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK,aAAa;AAAA,IAC7B;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA+B;AAC9E,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3F;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,QAAM,YAAY,MAAM,YAAY,mBAAmB,KAAK,KAAK,CAAC,gCAAgC,GAAG,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,MAAM,CAAC;AAC5J,MAAI,CAAC,WAAW;AACd,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC5F;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,uBAAuB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACxF;AAEA,QAAM,SAAS,iBAAiB,UAAU,IAAI;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,SAAS,OAAO,MAAM,QAAQ,EAAE,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClI;AAEA,QAAM,KAAK,UAAU,QAAQ,IAAI;AAEjC,QAAM,OAAO,MAAM,GAAG,QAAQ,cAAc;AAAA,IAC1C,IAAI,OAAO;AAAA,IACX,UAAU,KAAK;AAAA,IACf,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AAEA,MAAI,KAAK,YAAY,OAAO,KAAK,SAAS,QAAW;AACnD,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,sCAAsC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvG;AAEA,QAAM,UAAmC,CAAC;AAC1C,MAAI,OAAO,KAAK,SAAS,OAAW,SAAQ,OAAO,OAAO,KAAK;AAC/D,MAAI,OAAO,KAAK,gBAAgB,OAAW,SAAQ,cAAc,OAAO,KAAK;AAC7E,MAAI,OAAO,KAAK,cAAc,OAAW,SAAQ,YAAY,OAAO,KAAK;AACzE,MAAI,OAAO,KAAK,uBAAuB,OAAW,SAAQ,qBAAqB,OAAO,KAAK;AAE3F,MAAI,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AACnC,UAAM,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,OAAO;AAAA,EAC9D;AAEA,OAAK,0BAA0B,kCAAkC;AAAA,IAC/D,IAAI,KAAK;AAAA,IACT,MAAM,QAAQ,QAAkB,KAAK;AAAA,IACrC,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,EACvB,CAAC,EAAE,MAAM,MAAM,MAAS;AAExB,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;AAEA,eAAsB,OAAO,KAAc,EAAE,OAAO,GAA+B;AACjF,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3F;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,QAAM,YAAY,MAAM,YAAY,mBAAmB,KAAK,KAAK,CAAC,gCAAgC,GAAG,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,MAAM,CAAC;AAC5J,MAAI,CAAC,WAAW;AACd,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC5F;AAEA,QAAM,KAAK,UAAU,QAAQ,IAAI;AAEjC,QAAM,OAAO,MAAM,GAAG,QAAQ,cAAc;AAAA,IAC1C,IAAI,OAAO;AAAA,IACX,UAAU,KAAK;AAAA,IACf,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AAEA,MAAI,KAAK,UAAU;AACjB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,8BAA8B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/F;AAEA,QAAM,qBAAqB,MAAM,GAAG,MAAM,kBAAkB;AAAA,IAC1D,MAAM,KAAK;AAAA,IACX,WAAW;AAAA,EACb,CAAC;AACD,MAAI,qBAAqB,GAAG;AAC1B,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,kBAAkB,2CAA2C,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzJ;AAEA,QAAM,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,EAAE,WAAW,oBAAI,KAAK,EAAE,CAAC;AAC9E,QAAM,GAAG,aAAa,iBAAiB,EAAE,MAAM,KAAK,GAAU,GAAG,EAAE,WAAW,oBAAI,KAAK,EAAE,CAAC;AAE1F,OAAK,0BAA0B,kCAAkC;AAAA,IAC/D,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,EACvB,CAAC,EAAE,MAAM,MAAM,MAAS;AAExB,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;AAEA,MAAM,YAAY,EAAE,OAAO;AAAA,EACzB,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC5B,eAAe,EAAE,QAAQ;AAC3B,CAAC;AACD,MAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,OAAO;AAAA,EACf,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,WAAW,EAAE,QAAQ;AAAA,EACrB,UAAU,EAAE,QAAQ;AAAA,EACpB,oBAAoB,EAAE,QAAQ;AAAA,EAC9B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1C,KAAK;AACP,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;AACtD,MAAM,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,KAAK,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;AAExE,MAAM,eAAiC;AAAA,EACrC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,yBAAyB;AAAA,EAChC,WAAW,CAAC;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,GAAG,MAAM,iBAAiB,CAAC;AAAA,EAClE,CAAC;AAAA,EACD,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,IACrE,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,YAAY;AAAA,IAC5E,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,YAAY;AAAA,EACpE;AACF;AAEA,MAAM,eAAiC;AAAA,EACrC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,yBAAyB;AAAA,EAChC,aAAa,EAAE,QAAQ,iBAAiB;AAAA,EACxC,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,cAAc,CAAC;AAAA,EAC/E,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,gDAAgD,QAAQ,YAAY;AAAA,IAChG,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,IACrE,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,YAAY;AAAA,IAC5E,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,YAAY;AAAA,EACpE;AACF;AAEA,MAAM,kBAAoC;AAAA,EACxC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,yBAAyB;AAAA,EAChC,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,cAAc,CAAC;AAAA,EAC/E,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qCAAqC,QAAQ,YAAY;AAAA,IACrF,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,IACrE,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,YAAY;AAAA,IAC5E,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,YAAY;AAAA,EACpE;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,SAAS;AAAA,EACT,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,EAC9C,SAAS;AAAA,IACP,KAAK;AAAA,IACL,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,189 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { CustomerRole, CustomerRoleAcl } from "@open-mercato/core/modules/customer_accounts/data/entities";
6
+ import { createRoleSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
7
+ import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
8
+ const metadata = {};
9
+ async function GET(req) {
10
+ const auth = await getAuthFromRequest(req);
11
+ if (!auth) {
12
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
13
+ }
14
+ const container = await createRequestContainer();
15
+ const rbacService = container.resolve("rbacService");
16
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.view"], { tenantId: auth.tenantId, organizationId: auth.orgId });
17
+ if (!hasAccess) {
18
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
19
+ }
20
+ const url = new URL(req.url);
21
+ const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10) || 1);
22
+ const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get("pageSize") || "50", 10) || 50));
23
+ const search = (url.searchParams.get("search") || "").trim();
24
+ const em = container.resolve("em");
25
+ const where = {
26
+ tenantId: auth.tenantId,
27
+ organizationId: auth.orgId,
28
+ deletedAt: null
29
+ };
30
+ if (search) {
31
+ const escapedSearch = search.replace(/[%_\\]/g, "\\$&");
32
+ where.$or = [
33
+ { name: { $ilike: `%${escapedSearch}%` } },
34
+ { slug: { $ilike: `%${escapedSearch}%` } }
35
+ ];
36
+ }
37
+ const offset = (page - 1) * pageSize;
38
+ const [paged, total] = await em.findAndCount(CustomerRole, where, {
39
+ orderBy: { createdAt: "ASC" },
40
+ limit: pageSize,
41
+ offset
42
+ });
43
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
44
+ const items = paged.map((role) => ({
45
+ id: role.id,
46
+ name: role.name,
47
+ slug: role.slug,
48
+ description: role.description || null,
49
+ isDefault: role.isDefault,
50
+ isSystem: role.isSystem,
51
+ customerAssignable: role.customerAssignable,
52
+ createdAt: role.createdAt
53
+ }));
54
+ return NextResponse.json({ ok: true, items, total, totalPages, page });
55
+ }
56
+ async function POST(req) {
57
+ const auth = await getAuthFromRequest(req);
58
+ if (!auth) {
59
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
60
+ }
61
+ const container = await createRequestContainer();
62
+ const rbacService = container.resolve("rbacService");
63
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.roles.manage"], { tenantId: auth.tenantId, organizationId: auth.orgId });
64
+ if (!hasAccess) {
65
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
66
+ }
67
+ let body;
68
+ try {
69
+ body = await req.json();
70
+ } catch {
71
+ return NextResponse.json({ ok: false, error: "Invalid request body" }, { status: 400 });
72
+ }
73
+ const parsed = createRoleSchema.safeParse(body);
74
+ if (!parsed.success) {
75
+ return NextResponse.json({ ok: false, error: "Validation failed", details: parsed.error.flatten().fieldErrors }, { status: 400 });
76
+ }
77
+ const em = container.resolve("em");
78
+ const existing = await em.findOne(CustomerRole, {
79
+ tenantId: auth.tenantId,
80
+ slug: parsed.data.slug,
81
+ deletedAt: null
82
+ });
83
+ if (existing) {
84
+ return NextResponse.json({ ok: false, error: "A role with this slug already exists" }, { status: 409 });
85
+ }
86
+ const role = em.create(CustomerRole, {
87
+ tenantId: auth.tenantId,
88
+ organizationId: auth.orgId,
89
+ name: parsed.data.name,
90
+ slug: parsed.data.slug,
91
+ description: parsed.data.description || null,
92
+ isDefault: parsed.data.isDefault ?? false,
93
+ customerAssignable: parsed.data.customerAssignable ?? false,
94
+ isSystem: false,
95
+ createdAt: /* @__PURE__ */ new Date()
96
+ });
97
+ em.persist(role);
98
+ const acl = em.create(CustomerRoleAcl, {
99
+ role,
100
+ tenantId: auth.tenantId,
101
+ featuresJson: [],
102
+ isPortalAdmin: false,
103
+ createdAt: /* @__PURE__ */ new Date()
104
+ });
105
+ em.persist(acl);
106
+ await em.flush();
107
+ void emitCustomerAccountsEvent("customer_accounts.role.created", {
108
+ id: role.id,
109
+ name: role.name,
110
+ slug: role.slug,
111
+ tenantId: auth.tenantId,
112
+ organizationId: auth.orgId
113
+ }).catch(() => void 0);
114
+ return NextResponse.json({
115
+ ok: true,
116
+ role: {
117
+ id: role.id,
118
+ name: role.name,
119
+ slug: role.slug,
120
+ description: role.description || null,
121
+ isDefault: role.isDefault,
122
+ isSystem: role.isSystem,
123
+ customerAssignable: role.customerAssignable,
124
+ createdAt: role.createdAt
125
+ }
126
+ }, { status: 201 });
127
+ }
128
+ const roleSchema = z.object({
129
+ id: z.string().uuid(),
130
+ name: z.string(),
131
+ slug: z.string(),
132
+ description: z.string().nullable(),
133
+ isDefault: z.boolean(),
134
+ isSystem: z.boolean(),
135
+ customerAssignable: z.boolean(),
136
+ createdAt: z.string().datetime()
137
+ });
138
+ const roleResponseSchema = z.object({
139
+ id: z.string().uuid(),
140
+ name: z.string(),
141
+ slug: z.string(),
142
+ description: z.string().nullable(),
143
+ isDefault: z.boolean(),
144
+ isSystem: z.boolean(),
145
+ customerAssignable: z.boolean(),
146
+ createdAt: z.string().datetime()
147
+ });
148
+ const errorSchema = z.object({ ok: z.literal(false), error: z.string() });
149
+ const getMethodDoc = {
150
+ summary: "List customer roles (admin)",
151
+ description: "Returns all customer roles for the tenant.",
152
+ tags: ["Customer Accounts Admin"],
153
+ responses: [{
154
+ status: 200,
155
+ description: "Role list",
156
+ schema: z.object({ ok: z.literal(true), items: z.array(roleSchema), total: z.number(), totalPages: z.number(), page: z.number() })
157
+ }],
158
+ errors: [
159
+ { status: 401, description: "Not authenticated", schema: errorSchema },
160
+ { status: 403, description: "Insufficient permissions", schema: errorSchema }
161
+ ]
162
+ };
163
+ const postMethodDoc = {
164
+ summary: "Create customer role (admin)",
165
+ description: "Creates a new customer role with an empty ACL.",
166
+ tags: ["Customer Accounts Admin"],
167
+ requestBody: { schema: createRoleSchema },
168
+ responses: [{ status: 201, description: "Role created", schema: z.object({ ok: z.literal(true), role: roleResponseSchema }) }],
169
+ errors: [
170
+ { status: 400, description: "Validation failed", schema: errorSchema },
171
+ { status: 401, description: "Not authenticated", schema: errorSchema },
172
+ { status: 403, description: "Insufficient permissions", schema: errorSchema },
173
+ { status: 409, description: "Slug already exists", schema: errorSchema }
174
+ ]
175
+ };
176
+ const openApi = {
177
+ summary: "Customer role management (admin)",
178
+ methods: {
179
+ GET: getMethodDoc,
180
+ POST: postMethodDoc
181
+ }
182
+ };
183
+ export {
184
+ GET,
185
+ POST,
186
+ metadata,
187
+ openApi
188
+ };
189
+ //# sourceMappingURL=roles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/customer_accounts/api/admin/roles.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerRole, CustomerRoleAcl } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { createRoleSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\n\nexport const metadata = {}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.view'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const url = new URL(req.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1', 10) || 1)\n const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get('pageSize') || '50', 10) || 50))\n const search = (url.searchParams.get('search') || '').trim()\n\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n\n const where: Record<string, unknown> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (search) {\n const escapedSearch = search.replace(/[%_\\\\]/g, '\\\\$&')\n where.$or = [\n { name: { $ilike: `%${escapedSearch}%` } },\n { slug: { $ilike: `%${escapedSearch}%` } },\n ]\n }\n\n const offset = (page - 1) * pageSize\n const [paged, total] = await em.findAndCount(CustomerRole, where as any, {\n orderBy: { createdAt: 'ASC' },\n limit: pageSize,\n offset,\n })\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n const items = paged.map((role) => ({\n id: role.id,\n name: role.name,\n slug: role.slug,\n description: role.description || null,\n isDefault: role.isDefault,\n isSystem: role.isSystem,\n customerAssignable: role.customerAssignable,\n createdAt: role.createdAt,\n }))\n\n return NextResponse.json({ ok: true, items, total, totalPages, page })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.roles.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = createRoleSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n\n const existing = await em.findOne(CustomerRole, {\n tenantId: auth.tenantId,\n slug: parsed.data.slug,\n deletedAt: null,\n })\n if (existing) {\n return NextResponse.json({ ok: false, error: 'A role with this slug already exists' }, { status: 409 })\n }\n\n const role = em.create(CustomerRole, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n name: parsed.data.name,\n slug: parsed.data.slug,\n description: parsed.data.description || null,\n isDefault: parsed.data.isDefault ?? false,\n customerAssignable: parsed.data.customerAssignable ?? false,\n isSystem: false,\n createdAt: new Date(),\n } as any) as CustomerRole\n em.persist(role)\n\n const acl = em.create(CustomerRoleAcl, {\n role,\n tenantId: auth.tenantId,\n featuresJson: [],\n isPortalAdmin: false,\n createdAt: new Date(),\n } as any)\n em.persist(acl)\n\n await em.flush()\n\n void emitCustomerAccountsEvent('customer_accounts.role.created', {\n id: role.id,\n name: role.name,\n slug: role.slug,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n }).catch(() => undefined)\n\n return NextResponse.json({\n ok: true,\n role: {\n id: role.id,\n name: role.name,\n slug: role.slug,\n description: role.description || null,\n isDefault: role.isDefault,\n isSystem: role.isSystem,\n customerAssignable: role.customerAssignable,\n createdAt: role.createdAt,\n },\n }, { status: 201 })\n}\n\nconst roleSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n slug: z.string(),\n description: z.string().nullable(),\n isDefault: z.boolean(),\n isSystem: z.boolean(),\n customerAssignable: z.boolean(),\n createdAt: z.string().datetime(),\n})\n\nconst roleResponseSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n slug: z.string(),\n description: z.string().nullable(),\n isDefault: z.boolean(),\n isSystem: z.boolean(),\n customerAssignable: z.boolean(),\n createdAt: z.string().datetime(),\n})\n\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nconst getMethodDoc: OpenApiMethodDoc = {\n summary: 'List customer roles (admin)',\n description: 'Returns all customer roles for the tenant.',\n tags: ['Customer Accounts Admin'],\n responses: [{\n status: 200,\n description: 'Role list',\n schema: z.object({ ok: z.literal(true), items: z.array(roleSchema), total: z.number(), totalPages: z.number(), page: z.number() }),\n }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n ],\n}\n\nconst postMethodDoc: OpenApiMethodDoc = {\n summary: 'Create customer role (admin)',\n description: 'Creates a new customer role with an empty ACL.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: createRoleSchema },\n responses: [{ status: 201, description: 'Role created', schema: z.object({ ok: z.literal(true), role: roleResponseSchema }) }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 409, description: 'Slug already exists', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer role management (admin)',\n methods: {\n GET: getMethodDoc,\n POST: postMethodDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,69 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { adminResetPasswordSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
6
+ import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
7
+ const metadata = {};
8
+ async function POST(req, { params }) {
9
+ const auth = await getAuthFromRequest(req);
10
+ if (!auth) {
11
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
12
+ }
13
+ const container = await createRequestContainer();
14
+ const rbacService = container.resolve("rbacService");
15
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.manage"], { tenantId: auth.tenantId, organizationId: auth.orgId });
16
+ if (!hasAccess) {
17
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
18
+ }
19
+ let body;
20
+ try {
21
+ body = await req.json();
22
+ } catch {
23
+ return NextResponse.json({ ok: false, error: "Invalid request body" }, { status: 400 });
24
+ }
25
+ const parsed = adminResetPasswordSchema.safeParse(body);
26
+ if (!parsed.success) {
27
+ return NextResponse.json({ ok: false, error: "Validation failed", details: parsed.error.flatten().fieldErrors }, { status: 400 });
28
+ }
29
+ const customerUserService = container.resolve("customerUserService");
30
+ const user = await customerUserService.findById(params.id, auth.tenantId);
31
+ if (!user) {
32
+ return NextResponse.json({ ok: false, error: "User not found" }, { status: 404 });
33
+ }
34
+ await customerUserService.updatePassword(user, parsed.data.newPassword);
35
+ void emitCustomerAccountsEvent("customer_accounts.password.reset", {
36
+ id: user.id,
37
+ email: user.email,
38
+ tenantId: auth.tenantId,
39
+ organizationId: auth.orgId,
40
+ resetBy: auth.sub
41
+ }).catch(() => void 0);
42
+ return NextResponse.json({ ok: true });
43
+ }
44
+ const successSchema = z.object({ ok: z.literal(true) });
45
+ const errorSchema = z.object({ ok: z.literal(false), error: z.string() });
46
+ const methodDoc = {
47
+ summary: "Reset customer user password (admin)",
48
+ description: "Allows staff to set a new password for a customer user.",
49
+ tags: ["Customer Accounts Admin"],
50
+ requestBody: { schema: adminResetPasswordSchema },
51
+ responses: [{ status: 200, description: "Password reset", schema: successSchema }],
52
+ errors: [
53
+ { status: 400, description: "Validation failed", schema: errorSchema },
54
+ { status: 401, description: "Not authenticated", schema: errorSchema },
55
+ { status: 403, description: "Insufficient permissions", schema: errorSchema },
56
+ { status: 404, description: "User not found", schema: errorSchema }
57
+ ]
58
+ };
59
+ const openApi = {
60
+ summary: "Reset customer user password (admin)",
61
+ pathParams: z.object({ id: z.string().uuid() }),
62
+ methods: { POST: methodDoc }
63
+ };
64
+ export {
65
+ POST,
66
+ metadata,
67
+ openApi
68
+ };
69
+ //# sourceMappingURL=reset-password.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/customer_accounts/api/admin/users/%5Bid%5D/reset-password.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { adminResetPasswordSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\n\nexport const metadata = {}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = adminResetPasswordSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n const user = await customerUserService.findById(params.id, auth.tenantId!)\n if (!user) {\n return NextResponse.json({ ok: false, error: 'User not found' }, { status: 404 })\n }\n\n await customerUserService.updatePassword(user, parsed.data.newPassword)\n\n void emitCustomerAccountsEvent('customer_accounts.password.reset', {\n id: user.id,\n email: user.email,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n resetBy: auth.sub,\n }).catch(() => undefined)\n\n return NextResponse.json({ ok: true })\n}\n\nconst successSchema = z.object({ ok: z.literal(true) })\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nconst methodDoc: OpenApiMethodDoc = {\n summary: 'Reset customer user password (admin)',\n description: 'Allows staff to set a new password for a customer user.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: adminResetPasswordSchema },\n responses: [{ status: 200, description: 'Password reset', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 404, description: 'User not found', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Reset customer user password (admin)',\n pathParams: z.object({ id: z.string().uuid() }),\n methods: { POST: methodDoc },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAGvC,SAAS,gCAAgC;AACzC,SAAS,iCAAiC;AAEnC,MAAM,WAAW,CAAC;AAEzB,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA+B;AAC/E,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3F;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,QAAM,YAAY,MAAM,YAAY,mBAAmB,KAAK,KAAK,CAAC,0BAA0B,GAAG,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,MAAM,CAAC;AACtJ,MAAI,CAAC,WAAW;AACd,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC5F;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,uBAAuB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACxF;AAEA,QAAM,SAAS,yBAAyB,UAAU,IAAI;AACtD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,SAAS,OAAO,MAAM,QAAQ,EAAE,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClI;AAEA,QAAM,sBAAsB,UAAU,QAAQ,qBAAqB;AACnE,QAAM,OAAO,MAAM,oBAAoB,SAAS,OAAO,IAAI,KAAK,QAAS;AACzE,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AAEA,QAAM,oBAAoB,eAAe,MAAM,OAAO,KAAK,WAAW;AAEtE,OAAK,0BAA0B,oCAAoC;AAAA,IACjE,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,IACrB,SAAS,KAAK;AAAA,EAChB,CAAC,EAAE,MAAM,MAAM,MAAS;AAExB,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;AAEA,MAAM,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;AACtD,MAAM,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,KAAK,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;AAExE,MAAM,YAA8B;AAAA,EAClC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,yBAAyB;AAAA,EAChC,aAAa,EAAE,QAAQ,yBAAyB;AAAA,EAChD,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,cAAc,CAAC;AAAA,EACjF,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,IACrE,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,IACrE,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,YAAY;AAAA,IAC5E,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,YAAY;AAAA,EACpE;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,SAAS;AAAA,EACT,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,EAC9C,SAAS,EAAE,MAAM,UAAU;AAC7B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,64 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { CustomerUser } from "@open-mercato/core/modules/customer_accounts/data/entities";
6
+ import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
7
+ const metadata = {};
8
+ async function POST(req, { params }) {
9
+ const auth = await getAuthFromRequest(req);
10
+ if (!auth) {
11
+ return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
12
+ }
13
+ const container = await createRequestContainer();
14
+ const rbacService = container.resolve("rbacService");
15
+ const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.manage"], { tenantId: auth.tenantId, organizationId: auth.orgId });
16
+ if (!hasAccess) {
17
+ return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
18
+ }
19
+ const em = container.resolve("em");
20
+ const user = await em.findOne(CustomerUser, {
21
+ id: params.id,
22
+ tenantId: auth.tenantId,
23
+ deletedAt: null
24
+ });
25
+ if (!user) {
26
+ return NextResponse.json({ ok: false, error: "User not found" }, { status: 404 });
27
+ }
28
+ if (user.emailVerifiedAt) {
29
+ return NextResponse.json({ ok: true, alreadyVerified: true });
30
+ }
31
+ await em.nativeUpdate(CustomerUser, { id: user.id }, { emailVerifiedAt: /* @__PURE__ */ new Date() });
32
+ void emitCustomerAccountsEvent("customer_accounts.email.verified", {
33
+ id: user.id,
34
+ email: user.email,
35
+ tenantId: auth.tenantId,
36
+ organizationId: auth.orgId,
37
+ verifiedBy: auth.sub
38
+ }).catch(() => void 0);
39
+ return NextResponse.json({ ok: true });
40
+ }
41
+ const successSchema = z.object({ ok: z.literal(true), alreadyVerified: z.boolean().optional() });
42
+ const errorSchema = z.object({ ok: z.literal(false), error: z.string() });
43
+ const methodDoc = {
44
+ summary: "Verify customer user email (admin)",
45
+ description: "Allows staff to manually mark a customer user email as verified.",
46
+ tags: ["Customer Accounts Admin"],
47
+ responses: [{ status: 200, description: "Email verified", schema: successSchema }],
48
+ errors: [
49
+ { status: 401, description: "Not authenticated", schema: errorSchema },
50
+ { status: 403, description: "Insufficient permissions", schema: errorSchema },
51
+ { status: 404, description: "User not found", schema: errorSchema }
52
+ ]
53
+ };
54
+ const openApi = {
55
+ summary: "Verify customer user email (admin)",
56
+ pathParams: z.object({ id: z.string().uuid() }),
57
+ methods: { POST: methodDoc }
58
+ };
59
+ export {
60
+ POST,
61
+ metadata,
62
+ openApi
63
+ };
64
+ //# sourceMappingURL=verify-email.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/customer_accounts/api/admin/users/%5Bid%5D/verify-email.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerUser } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\n\nexport const metadata = {}\n\nexport async function POST(req: Request, { params }: { params: { id: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n\n const user = await em.findOne(CustomerUser, {\n id: params.id,\n tenantId: auth.tenantId,\n deletedAt: null,\n })\n if (!user) {\n return NextResponse.json({ ok: false, error: 'User not found' }, { status: 404 })\n }\n\n if (user.emailVerifiedAt) {\n return NextResponse.json({ ok: true, alreadyVerified: true })\n }\n\n await em.nativeUpdate(CustomerUser, { id: user.id }, { emailVerifiedAt: new Date() })\n\n void emitCustomerAccountsEvent('customer_accounts.email.verified', {\n id: user.id,\n email: user.email,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n verifiedBy: auth.sub,\n }).catch(() => undefined)\n\n return NextResponse.json({ ok: true })\n}\n\nconst successSchema = z.object({ ok: z.literal(true), alreadyVerified: z.boolean().optional() })\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nconst methodDoc: OpenApiMethodDoc = {\n summary: 'Verify customer user email (admin)',\n description: 'Allows staff to manually mark a customer user email as verified.',\n tags: ['Customer Accounts Admin'],\n responses: [{ status: 200, description: 'Email verified', schema: successSchema }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 404, description: 'User not found', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Verify customer user email (admin)',\n pathParams: z.object({ id: z.string().uuid() }),\n methods: { POST: methodDoc },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,oBAAoB;AAC7B,SAAS,iCAAiC;AAEnC,MAAM,WAAW,CAAC;AAEzB,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA+B;AAC/E,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3F;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,QAAM,YAAY,MAAM,YAAY,mBAAmB,KAAK,KAAK,CAAC,0BAA0B,GAAG,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,MAAM,CAAC;AACtJ,MAAI,CAAC,WAAW;AACd,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC5F;AAEA,QAAM,KAAK,UAAU,QAAQ,IAAI;AAEjC,QAAM,OAAO,MAAM,GAAG,QAAQ,cAAc;AAAA,IAC1C,IAAI,OAAO;AAAA,IACX,UAAU,KAAK;AAAA,IACf,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AAEA,MAAI,KAAK,iBAAiB;AACxB,WAAO,aAAa,KAAK,EAAE,IAAI,MAAM,iBAAiB,KAAK,CAAC;AAAA,EAC9D;AAEA,QAAM,GAAG,aAAa,cAAc,EAAE,IAAI,KAAK,GAAG,GAAG,EAAE,iBAAiB,oBAAI,KAAK,EAAE,CAAC;AAEpF,OAAK,0BAA0B,oCAAoC;AAAA,IACjE,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,IACrB,YAAY,KAAK;AAAA,EACnB,CAAC,EAAE,MAAM,MAAM,MAAS;AAExB,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;AAEA,MAAM,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,GAAG,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAC/F,MAAM,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,KAAK,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;AAExE,MAAM,YAA8B;AAAA,EAClC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,yBAAyB;AAAA,EAChC,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,cAAc,CAAC;AAAA,EACjF,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,IACrE,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,YAAY;AAAA,IAC5E,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,YAAY;AAAA,EACpE;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,SAAS;AAAA,EACT,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,EAC9C,SAAS,EAAE,MAAM,UAAU;AAC7B;",
6
+ "names": []
7
+ }