@open-mercato/core 0.4.7-develop-0a657b411f → 0.4.7-develop-11728c8558

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 (275) hide show
  1. package/dist/generated/entities/carrier_shipment/index.js +37 -0
  2. package/dist/generated/entities/carrier_shipment/index.js.map +7 -0
  3. package/dist/generated/entities/gateway_transaction/index.js +47 -0
  4. package/dist/generated/entities/gateway_transaction/index.js.map +7 -0
  5. package/dist/generated/entities/webhook_processed_event/index.js +17 -0
  6. package/dist/generated/entities/webhook_processed_event/index.js.map +7 -0
  7. package/dist/generated/entities.ids.generated.js +10 -1
  8. package/dist/generated/entities.ids.generated.js.map +2 -2
  9. package/dist/generated/entity-fields-registry.js +6 -0
  10. package/dist/generated/entity-fields-registry.js.map +2 -2
  11. package/dist/modules/data_sync/api/runs/[id]/cancel.js +14 -5
  12. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  13. package/dist/modules/data_sync/backend/data-sync/page.meta.js +2 -2
  14. package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +1 -1
  15. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +37 -12
  16. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  17. package/dist/modules/directory/api/get/tenants/lookup.js +1 -0
  18. package/dist/modules/directory/api/get/tenants/lookup.js.map +2 -2
  19. package/dist/modules/integrations/api/[id]/route.js +38 -11
  20. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  21. package/dist/modules/integrations/api/logs/route.js +52 -26
  22. package/dist/modules/integrations/api/logs/route.js.map +2 -2
  23. package/dist/modules/integrations/api/route.js +37 -7
  24. package/dist/modules/integrations/api/route.js.map +2 -2
  25. package/dist/modules/integrations/api/umes-read.js +121 -0
  26. package/dist/modules/integrations/api/umes-read.js.map +7 -0
  27. package/dist/modules/integrations/backend/integrations/[id]/page.js +715 -183
  28. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  29. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +30 -9
  30. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  31. package/dist/modules/integrations/backend/integrations/detail-page-widgets.js +46 -0
  32. package/dist/modules/integrations/backend/integrations/detail-page-widgets.js.map +7 -0
  33. package/dist/modules/integrations/backend/integrations/page.js +78 -62
  34. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  35. package/dist/modules/integrations/backend/integrations/page.meta.js +2 -2
  36. package/dist/modules/integrations/backend/integrations/page.meta.js.map +1 -1
  37. package/dist/modules/integrations/setup.js +2 -2
  38. package/dist/modules/integrations/setup.js.map +2 -2
  39. package/dist/modules/payment_gateways/acl.js +12 -0
  40. package/dist/modules/payment_gateways/acl.js.map +7 -0
  41. package/dist/modules/payment_gateways/api/cancel/route.js +55 -0
  42. package/dist/modules/payment_gateways/api/cancel/route.js.map +7 -0
  43. package/dist/modules/payment_gateways/api/capture/route.js +55 -0
  44. package/dist/modules/payment_gateways/api/capture/route.js.map +7 -0
  45. package/dist/modules/payment_gateways/api/interceptors.js +24 -0
  46. package/dist/modules/payment_gateways/api/interceptors.js.map +7 -0
  47. package/dist/modules/payment_gateways/api/openapi.js +5 -0
  48. package/dist/modules/payment_gateways/api/openapi.js.map +7 -0
  49. package/dist/modules/payment_gateways/api/refund/route.js +56 -0
  50. package/dist/modules/payment_gateways/api/refund/route.js.map +7 -0
  51. package/dist/modules/payment_gateways/api/sessions/route.js +74 -0
  52. package/dist/modules/payment_gateways/api/sessions/route.js.map +7 -0
  53. package/dist/modules/payment_gateways/api/status/route.js +66 -0
  54. package/dist/modules/payment_gateways/api/status/route.js.map +7 -0
  55. package/dist/modules/payment_gateways/api/transactions/[id]/route.js +118 -0
  56. package/dist/modules/payment_gateways/api/transactions/[id]/route.js.map +7 -0
  57. package/dist/modules/payment_gateways/api/transactions/route.js +113 -0
  58. package/dist/modules/payment_gateways/api/transactions/route.js.map +7 -0
  59. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +136 -0
  60. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +7 -0
  61. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +496 -0
  62. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +7 -0
  63. package/dist/modules/payment_gateways/backend/payment-gateways/page.meta.js +23 -0
  64. package/dist/modules/payment_gateways/backend/payment-gateways/page.meta.js.map +7 -0
  65. package/dist/modules/payment_gateways/data/enrichers.js +5 -0
  66. package/dist/modules/payment_gateways/data/enrichers.js.map +7 -0
  67. package/dist/modules/payment_gateways/data/entities.js +131 -0
  68. package/dist/modules/payment_gateways/data/entities.js.map +7 -0
  69. package/dist/modules/payment_gateways/data/validators.js +57 -0
  70. package/dist/modules/payment_gateways/data/validators.js.map +7 -0
  71. package/dist/modules/payment_gateways/di.js +16 -0
  72. package/dist/modules/payment_gateways/di.js.map +7 -0
  73. package/dist/modules/payment_gateways/events.js +21 -0
  74. package/dist/modules/payment_gateways/events.js.map +7 -0
  75. package/dist/modules/payment_gateways/i18n/en.js +6 -0
  76. package/dist/modules/payment_gateways/i18n/en.js.map +7 -0
  77. package/dist/modules/payment_gateways/i18n/pl.js +6 -0
  78. package/dist/modules/payment_gateways/i18n/pl.js.map +7 -0
  79. package/dist/modules/payment_gateways/index.js +9 -0
  80. package/dist/modules/payment_gateways/index.js.map +7 -0
  81. package/dist/modules/payment_gateways/lib/gateway-service.js +378 -0
  82. package/dist/modules/payment_gateways/lib/gateway-service.js.map +7 -0
  83. package/dist/modules/payment_gateways/lib/queue.js +17 -0
  84. package/dist/modules/payment_gateways/lib/queue.js.map +7 -0
  85. package/dist/modules/payment_gateways/lib/status-machine.js +29 -0
  86. package/dist/modules/payment_gateways/lib/status-machine.js.map +7 -0
  87. package/dist/modules/payment_gateways/lib/webhook-processor.js +88 -0
  88. package/dist/modules/payment_gateways/lib/webhook-processor.js.map +7 -0
  89. package/dist/modules/payment_gateways/lib/webhook-utils.js +42 -0
  90. package/dist/modules/payment_gateways/lib/webhook-utils.js.map +7 -0
  91. package/dist/modules/payment_gateways/migrations/Migration20260305122155.js +19 -0
  92. package/dist/modules/payment_gateways/migrations/Migration20260305122155.js.map +7 -0
  93. package/dist/modules/payment_gateways/setup.js +13 -0
  94. package/dist/modules/payment_gateways/setup.js.map +7 -0
  95. package/dist/modules/payment_gateways/widgets/injection-table.js +7 -0
  96. package/dist/modules/payment_gateways/widgets/injection-table.js.map +7 -0
  97. package/dist/modules/payment_gateways/workers/status-poller.js +44 -0
  98. package/dist/modules/payment_gateways/workers/status-poller.js.map +7 -0
  99. package/dist/modules/payment_gateways/workers/webhook-processor.js +20 -0
  100. package/dist/modules/payment_gateways/workers/webhook-processor.js.map +7 -0
  101. package/dist/modules/sales/data/enrichers.js +80 -0
  102. package/dist/modules/sales/data/enrichers.js.map +7 -0
  103. package/dist/modules/sales/widgets/injection/payment-gateway-config-field/widget.js +29 -0
  104. package/dist/modules/sales/widgets/injection/payment-gateway-config-field/widget.js.map +7 -0
  105. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +23 -0
  106. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +7 -0
  107. package/dist/modules/sales/widgets/injection-table.js +13 -1
  108. package/dist/modules/sales/widgets/injection-table.js.map +2 -2
  109. package/dist/modules/shipping_carriers/acl.js +10 -0
  110. package/dist/modules/shipping_carriers/acl.js.map +7 -0
  111. package/dist/modules/shipping_carriers/api/cancel/route.js +55 -0
  112. package/dist/modules/shipping_carriers/api/cancel/route.js.map +7 -0
  113. package/dist/modules/shipping_carriers/api/interceptors.js +21 -0
  114. package/dist/modules/shipping_carriers/api/interceptors.js.map +7 -0
  115. package/dist/modules/shipping_carriers/api/openapi.js +5 -0
  116. package/dist/modules/shipping_carriers/api/openapi.js.map +7 -0
  117. package/dist/modules/shipping_carriers/api/rates/route.js +55 -0
  118. package/dist/modules/shipping_carriers/api/rates/route.js.map +7 -0
  119. package/dist/modules/shipping_carriers/api/shipments/route.js +61 -0
  120. package/dist/modules/shipping_carriers/api/shipments/route.js.map +7 -0
  121. package/dist/modules/shipping_carriers/api/tracking/route.js +58 -0
  122. package/dist/modules/shipping_carriers/api/tracking/route.js.map +7 -0
  123. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +119 -0
  124. package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +7 -0
  125. package/dist/modules/shipping_carriers/data/enrichers.js +82 -0
  126. package/dist/modules/shipping_carriers/data/enrichers.js.map +7 -0
  127. package/dist/modules/shipping_carriers/data/entities.js +80 -0
  128. package/dist/modules/shipping_carriers/data/entities.js.map +7 -0
  129. package/dist/modules/shipping_carriers/data/validators.js +49 -0
  130. package/dist/modules/shipping_carriers/data/validators.js.map +7 -0
  131. package/dist/modules/shipping_carriers/di.js +15 -0
  132. package/dist/modules/shipping_carriers/di.js.map +7 -0
  133. package/dist/modules/shipping_carriers/events.js +19 -0
  134. package/dist/modules/shipping_carriers/events.js.map +7 -0
  135. package/dist/modules/shipping_carriers/i18n/en.js +11 -0
  136. package/dist/modules/shipping_carriers/i18n/en.js.map +7 -0
  137. package/dist/modules/shipping_carriers/i18n/pl.js +11 -0
  138. package/dist/modules/shipping_carriers/i18n/pl.js.map +7 -0
  139. package/dist/modules/shipping_carriers/index.js +9 -0
  140. package/dist/modules/shipping_carriers/index.js.map +7 -0
  141. package/dist/modules/shipping_carriers/lib/adapter-registry.js +29 -0
  142. package/dist/modules/shipping_carriers/lib/adapter-registry.js.map +7 -0
  143. package/dist/modules/shipping_carriers/lib/adapter.js +1 -0
  144. package/dist/modules/shipping_carriers/lib/adapter.js.map +7 -0
  145. package/dist/modules/shipping_carriers/lib/queue.js +17 -0
  146. package/dist/modules/shipping_carriers/lib/queue.js.map +7 -0
  147. package/dist/modules/shipping_carriers/lib/shipping-service.js +155 -0
  148. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +7 -0
  149. package/dist/modules/shipping_carriers/lib/status-sync.js +37 -0
  150. package/dist/modules/shipping_carriers/lib/status-sync.js.map +7 -0
  151. package/dist/modules/shipping_carriers/migrations/Migration20260305170000.js +16 -0
  152. package/dist/modules/shipping_carriers/migrations/Migration20260305170000.js.map +7 -0
  153. package/dist/modules/shipping_carriers/setup.js +13 -0
  154. package/dist/modules/shipping_carriers/setup.js.map +7 -0
  155. package/dist/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.js +25 -0
  156. package/dist/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.js.map +7 -0
  157. package/dist/modules/shipping_carriers/widgets/injection/tracking-column/widget.js +23 -0
  158. package/dist/modules/shipping_carriers/widgets/injection/tracking-column/widget.js.map +7 -0
  159. package/dist/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.js +40 -0
  160. package/dist/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.js.map +7 -0
  161. package/dist/modules/shipping_carriers/widgets/injection-table.js +24 -0
  162. package/dist/modules/shipping_carriers/widgets/injection-table.js.map +7 -0
  163. package/dist/modules/shipping_carriers/workers/status-poller.js +21 -0
  164. package/dist/modules/shipping_carriers/workers/status-poller.js.map +7 -0
  165. package/dist/modules/shipping_carriers/workers/webhook-processor.js +54 -0
  166. package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +7 -0
  167. package/dist/modules/translations/api/get/locales.js +1 -0
  168. package/dist/modules/translations/api/get/locales.js.map +2 -2
  169. package/dist/modules/translations/api/put/locales.js +1 -0
  170. package/dist/modules/translations/api/put/locales.js.map +2 -2
  171. package/generated/entities/carrier_shipment/index.ts +17 -0
  172. package/generated/entities/gateway_transaction/index.ts +22 -0
  173. package/generated/entities/webhook_processed_event/index.ts +7 -0
  174. package/generated/entities.ids.generated.ts +10 -1
  175. package/generated/entity-fields-registry.ts +6 -0
  176. package/jest.config.cjs +1 -0
  177. package/package.json +5 -2
  178. package/src/modules/auth/i18n/de.json +1 -0
  179. package/src/modules/auth/i18n/en.json +1 -0
  180. package/src/modules/auth/i18n/es.json +1 -0
  181. package/src/modules/auth/i18n/pl.json +1 -0
  182. package/src/modules/data_sync/api/runs/[id]/cancel.ts +18 -5
  183. package/src/modules/data_sync/backend/data-sync/page.meta.ts +2 -2
  184. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +50 -12
  185. package/src/modules/directory/api/get/tenants/lookup.ts +1 -0
  186. package/src/modules/integrations/AGENTS.md +31 -0
  187. package/src/modules/integrations/api/[id]/route.ts +38 -11
  188. package/src/modules/integrations/api/logs/route.ts +53 -27
  189. package/src/modules/integrations/api/route.ts +31 -1
  190. package/src/modules/integrations/api/umes-read.ts +177 -0
  191. package/src/modules/integrations/backend/integrations/[id]/page.tsx +902 -202
  192. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +43 -9
  193. package/src/modules/integrations/backend/integrations/detail-page-widgets.ts +74 -0
  194. package/src/modules/integrations/backend/integrations/page.meta.ts +2 -2
  195. package/src/modules/integrations/backend/integrations/page.tsx +65 -54
  196. package/src/modules/integrations/i18n/de.json +15 -0
  197. package/src/modules/integrations/i18n/en.json +15 -0
  198. package/src/modules/integrations/i18n/es.json +15 -0
  199. package/src/modules/integrations/i18n/pl.json +15 -0
  200. package/src/modules/integrations/setup.ts +2 -2
  201. package/src/modules/payment_gateways/acl.ts +8 -0
  202. package/src/modules/payment_gateways/api/cancel/route.ts +56 -0
  203. package/src/modules/payment_gateways/api/capture/route.ts +56 -0
  204. package/src/modules/payment_gateways/api/interceptors.ts +22 -0
  205. package/src/modules/payment_gateways/api/openapi.ts +1 -0
  206. package/src/modules/payment_gateways/api/refund/route.ts +57 -0
  207. package/src/modules/payment_gateways/api/sessions/route.ts +76 -0
  208. package/src/modules/payment_gateways/api/status/route.ts +69 -0
  209. package/src/modules/payment_gateways/api/transactions/[id]/route.ts +123 -0
  210. package/src/modules/payment_gateways/api/transactions/route.ts +120 -0
  211. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +161 -0
  212. package/src/modules/payment_gateways/backend/payment-gateways/page.meta.ts +19 -0
  213. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +660 -0
  214. package/src/modules/payment_gateways/data/enrichers.ts +8 -0
  215. package/src/modules/payment_gateways/data/entities.ts +106 -0
  216. package/src/modules/payment_gateways/data/validators.ts +67 -0
  217. package/src/modules/payment_gateways/di.ts +26 -0
  218. package/src/modules/payment_gateways/events.ts +17 -0
  219. package/src/modules/payment_gateways/i18n/de.json +77 -0
  220. package/src/modules/payment_gateways/i18n/en.json +77 -0
  221. package/src/modules/payment_gateways/i18n/en.ts +4 -0
  222. package/src/modules/payment_gateways/i18n/es.json +77 -0
  223. package/src/modules/payment_gateways/i18n/pl.json +77 -0
  224. package/src/modules/payment_gateways/i18n/pl.ts +4 -0
  225. package/src/modules/payment_gateways/index.ts +5 -0
  226. package/src/modules/payment_gateways/lib/gateway-service.ts +486 -0
  227. package/src/modules/payment_gateways/lib/queue.ts +19 -0
  228. package/src/modules/payment_gateways/lib/status-machine.ts +28 -0
  229. package/src/modules/payment_gateways/lib/webhook-processor.ts +133 -0
  230. package/src/modules/payment_gateways/lib/webhook-utils.ts +52 -0
  231. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +373 -0
  232. package/src/modules/payment_gateways/migrations/Migration20260305122155.ts +20 -0
  233. package/src/modules/payment_gateways/setup.ts +11 -0
  234. package/src/modules/payment_gateways/widgets/injection-table.ts +9 -0
  235. package/src/modules/payment_gateways/workers/status-poller.ts +58 -0
  236. package/src/modules/payment_gateways/workers/webhook-processor.ts +30 -0
  237. package/src/modules/sales/data/enrichers.ts +94 -0
  238. package/src/modules/sales/widgets/injection/payment-gateway-config-field/widget.ts +28 -0
  239. package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +22 -0
  240. package/src/modules/sales/widgets/injection-table.ts +12 -0
  241. package/src/modules/shipping_carriers/acl.ts +6 -0
  242. package/src/modules/shipping_carriers/api/cancel/route.ts +53 -0
  243. package/src/modules/shipping_carriers/api/interceptors.ts +19 -0
  244. package/src/modules/shipping_carriers/api/openapi.ts +1 -0
  245. package/src/modules/shipping_carriers/api/rates/route.ts +53 -0
  246. package/src/modules/shipping_carriers/api/shipments/route.ts +59 -0
  247. package/src/modules/shipping_carriers/api/tracking/route.ts +56 -0
  248. package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +134 -0
  249. package/src/modules/shipping_carriers/data/enrichers.ts +89 -0
  250. package/src/modules/shipping_carriers/data/entities.ts +60 -0
  251. package/src/modules/shipping_carriers/data/validators.ts +48 -0
  252. package/src/modules/shipping_carriers/di.ts +20 -0
  253. package/src/modules/shipping_carriers/events.ts +16 -0
  254. package/src/modules/shipping_carriers/i18n/de.json +7 -0
  255. package/src/modules/shipping_carriers/i18n/en.json +7 -0
  256. package/src/modules/shipping_carriers/i18n/en.ts +7 -0
  257. package/src/modules/shipping_carriers/i18n/es.json +7 -0
  258. package/src/modules/shipping_carriers/i18n/pl.json +7 -0
  259. package/src/modules/shipping_carriers/i18n/pl.ts +7 -0
  260. package/src/modules/shipping_carriers/index.ts +5 -0
  261. package/src/modules/shipping_carriers/lib/adapter-registry.ts +33 -0
  262. package/src/modules/shipping_carriers/lib/adapter.ts +93 -0
  263. package/src/modules/shipping_carriers/lib/queue.ts +19 -0
  264. package/src/modules/shipping_carriers/lib/shipping-service.ts +204 -0
  265. package/src/modules/shipping_carriers/lib/status-sync.ts +38 -0
  266. package/src/modules/shipping_carriers/migrations/Migration20260305170000.ts +14 -0
  267. package/src/modules/shipping_carriers/setup.ts +11 -0
  268. package/src/modules/shipping_carriers/widgets/injection/create-shipment-button/widget.ts +24 -0
  269. package/src/modules/shipping_carriers/widgets/injection/tracking-column/widget.ts +22 -0
  270. package/src/modules/shipping_carriers/widgets/injection/tracking-status-badge/widget.tsx +44 -0
  271. package/src/modules/shipping_carriers/widgets/injection-table.ts +22 -0
  272. package/src/modules/shipping_carriers/workers/status-poller.ts +33 -0
  273. package/src/modules/shipping_carriers/workers/webhook-processor.ts +79 -0
  274. package/src/modules/translations/api/get/locales.ts +1 -0
  275. package/src/modules/translations/api/put/locales.ts +1 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/data_sync/backend/data-sync/runs/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useParams, useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Card, CardHeader, CardTitle, CardContent } from '@open-mercato/ui/primitives/card'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\n\ntype SyncRunDetail = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n skippedCount: number\n failedCount: number\n batchesCompleted: number\n lastError: string | null\n progressJobId: string | null\n progressJob: {\n id: string\n status: string\n progressPercent: number\n processedCount: number\n totalCount: number | null\n etaSeconds: number | null\n } | null\n triggeredBy: string | null\n createdAt: string\n updatedAt: string\n}\n\ntype LogEntry = {\n id: string\n level: 'info' | 'warn' | 'error'\n message: string\n createdAt: string\n}\n\nconst STATUS_STYLES: Record<string, string> = {\n pending: 'bg-gray-100 text-gray-800',\n running: 'bg-blue-100 text-blue-800',\n completed: 'bg-green-100 text-green-800',\n failed: 'bg-red-100 text-red-800',\n cancelled: 'bg-yellow-100 text-yellow-800',\n paused: 'bg-orange-100 text-orange-800',\n}\n\nconst LOG_LEVEL_STYLES: Record<string, string> = {\n info: 'bg-blue-100 text-blue-800',\n warn: 'bg-yellow-100 text-yellow-800',\n error: 'bg-red-100 text-red-800',\n}\n\nexport default function SyncRunDetailPage() {\n const params = useParams<{ id: string }>()\n const router = useRouter()\n const runId = params.id\n const t = useT()\n\n const [run, setRun] = React.useState<SyncRunDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [logs, setLogs] = React.useState<LogEntry[]>([])\n const [isLoadingLogs, setIsLoadingLogs] = React.useState(false)\n\n const loadRun = React.useCallback(async () => {\n const call = await apiCall<SyncRunDetail>(\n `/api/data_sync/runs/${encodeURIComponent(runId)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n setError(t('data_sync.runs.detail.loadError'))\n setIsLoading(false)\n return\n }\n setRun(call.result)\n setIsLoading(false)\n }, [runId, t])\n\n const loadLogs = React.useCallback(async () => {\n setIsLoadingLogs(true)\n const params = new URLSearchParams({ runId, pageSize: '50' })\n const call = await apiCall<{ items: LogEntry[] }>(\n `/api/integrations/logs?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n if (call.ok && call.result) {\n setLogs(call.result.items)\n }\n setIsLoadingLogs(false)\n }, [runId])\n\n React.useEffect(() => {\n void loadRun()\n void loadLogs()\n }, [loadRun, loadLogs])\n\n React.useEffect(() => {\n if (!run || (run.status !== 'running' && run.status !== 'pending')) return\n const interval = setInterval(() => { void loadRun() }, 4000)\n return () => clearInterval(interval)\n }, [run?.status, loadRun])\n\n const handleCancel = React.useCallback(async () => {\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(runId)}/cancel`, {\n method: 'POST',\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.cancelSuccess'), 'success')\n void loadRun()\n } else {\n flash(t('data_sync.runs.detail.cancelError'), 'error')\n }\n }, [runId, t, loadRun])\n\n const handleRetry = React.useCallback(async () => {\n const call = await apiCall<{ id: string }>(`/api/data_sync/runs/${encodeURIComponent(runId)}/retry`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromBeginning: false }),\n }, { fallback: null })\n if (call.ok && call.result) {\n flash(t('data_sync.runs.detail.retrySuccess'), 'success')\n router.push(`/backend/data-sync/runs/${encodeURIComponent(call.result.id)}`)\n } else {\n flash(t('data_sync.runs.detail.retryError'), 'error')\n }\n }, [router, runId, t])\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('data_sync.runs.detail.title')} /></PageBody></Page>\n if (error || !run) return <Page><PageBody><ErrorMessage label={error ?? t('data_sync.runs.detail.loadError')} /></PageBody></Page>\n\n const totalProcessed = run.createdCount + run.updatedCount + run.skippedCount + run.failedCount\n const progressPercent = run.progressJob?.progressPercent ?? (run.status === 'completed' ? 100 : 0)\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <div>\n <Link href=\"/backend/data-sync\" className=\"text-sm text-muted-foreground hover:underline\">\n {t('data_sync.runs.detail.back')}\n </Link>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{run.integrationId} \u2014 {run.entityType}</h1>\n <div className=\"flex gap-2 mt-2\">\n <Badge variant=\"outline\">{t(`data_sync.dashboard.direction.${run.direction}`)}</Badge>\n <Badge variant=\"secondary\" className={STATUS_STYLES[run.status] ?? ''}>\n {t(`data_sync.dashboard.status.${run.status}`)}\n </Badge>\n {run.triggeredBy && <Badge variant=\"outline\">{run.triggeredBy}</Badge>}\n </div>\n </div>\n <div className=\"flex gap-2\">\n {(run.status === 'running' || run.status === 'pending') && (\n <Button type=\"button\" variant=\"destructive\" size=\"sm\" onClick={() => void handleCancel()}>\n {t('data_sync.runs.detail.cancel')}\n </Button>\n )}\n {run.status === 'failed' && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => void handleRetry()}>\n {t('data_sync.runs.detail.retry')}\n </Button>\n )}\n </div>\n </div>\n\n {(run.status === 'running' || run.status === 'pending') && (\n <Card>\n <CardHeader>\n <CardTitle>{t('data_sync.runs.detail.progress')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <Progress value={progressPercent} className=\"h-3\" />\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>{t('data_sync.runs.detail.progress.itemsProcessed', { count: totalProcessed })}</span>\n <span>{t('data_sync.runs.detail.progress.batches', { count: run.batchesCompleted })}</span>\n </div>\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-green-600\">{run.createdCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.created')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-blue-600\">{run.updatedCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.updated')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-gray-600\">{run.skippedCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.skipped')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-red-600\">{run.failedCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.failed')}</p>\n </CardContent>\n </Card>\n </div>\n\n {run.lastError && (\n <Card className=\"border-red-200 bg-red-50\">\n <CardHeader>\n <CardTitle className=\"text-red-800\">{t('data_sync.runs.detail.error')}</CardTitle>\n </CardHeader>\n <CardContent>\n <pre className=\"text-sm text-red-700 whitespace-pre-wrap\">{run.lastError}</pre>\n </CardContent>\n </Card>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>{t('data_sync.runs.detail.logs')}</CardTitle>\n </CardHeader>\n <CardContent>\n {isLoadingLogs ? (\n <div className=\"flex justify-center py-4\"><Spinner /></div>\n ) : logs.length === 0 ? (\n <p className=\"text-muted-foreground text-sm\">{t('data_sync.runs.detail.noLogs')}</p>\n ) : (\n <div className=\"rounded-lg border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/50\">\n <th className=\"px-4 py-2 text-left font-medium\">{t('data_sync.runs.detail.logs.time')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('data_sync.runs.detail.logs.level')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('data_sync.runs.detail.logs.message')}</th>\n </tr>\n </thead>\n <tbody>\n {logs.map((log) => (\n <tr key={log.id} className=\"border-b last:border-0\">\n <td className=\"px-4 py-2 text-muted-foreground whitespace-nowrap\">\n {new Date(log.createdAt).toLocaleString()}\n </td>\n <td className=\"px-4 py-2\">\n <Badge variant=\"secondary\" className={LOG_LEVEL_STYLES[log.level] ?? ''}>\n {log.level}\n </Badge>\n </td>\n <td className=\"px-4 py-2\">{log.message}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { usePathname, useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Card, CardHeader, CardTitle, CardContent } from '@open-mercato/ui/primitives/card'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\n\ntype SyncRunDetail = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n skippedCount: number\n failedCount: number\n batchesCompleted: number\n lastError: string | null\n progressJobId: string | null\n progressJob: {\n id: string\n status: string\n progressPercent: number\n processedCount: number\n totalCount: number | null\n etaSeconds: number | null\n } | null\n triggeredBy: string | null\n createdAt: string\n updatedAt: string\n}\n\ntype LogEntry = {\n id: string\n level: 'info' | 'warn' | 'error'\n message: string\n createdAt: string\n}\n\nconst STATUS_STYLES: Record<string, string> = {\n pending: 'bg-gray-100 text-gray-800',\n running: 'bg-blue-100 text-blue-800',\n completed: 'bg-green-100 text-green-800',\n failed: 'bg-red-100 text-red-800',\n cancelled: 'bg-yellow-100 text-yellow-800',\n paused: 'bg-orange-100 text-orange-800',\n}\n\nconst LOG_LEVEL_STYLES: Record<string, string> = {\n info: 'bg-blue-100 text-blue-800',\n warn: 'bg-yellow-100 text-yellow-800',\n error: 'bg-red-100 text-red-800',\n}\n\ntype SyncRunDetailPageProps = {\n params?: {\n id?: string | string[]\n }\n}\n\nfunction resolveRouteId(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction resolvePathnameId(pathname: string): string | undefined {\n const parts = pathname.split('/').filter(Boolean)\n const runId = parts.at(-1)\n if (!runId || runId === 'runs' || runId === 'data-sync') return undefined\n return decodeURIComponent(runId)\n}\n\nexport default function SyncRunDetailPage({ params }: SyncRunDetailPageProps) {\n const pathname = usePathname()\n const router = useRouter()\n const runId = resolveRouteId(params?.id) ?? resolvePathnameId(pathname)\n const t = useT()\n\n const [run, setRun] = React.useState<SyncRunDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [logs, setLogs] = React.useState<LogEntry[]>([])\n const [isLoadingLogs, setIsLoadingLogs] = React.useState(false)\n\n const resolveCurrentRunId = React.useCallback(() => {\n return runId ?? (\n typeof window !== 'undefined'\n ? resolvePathnameId(window.location.pathname)\n : undefined\n )\n }, [runId])\n\n const loadRun = React.useCallback(async () => {\n const currentRunId = resolveCurrentRunId()\n if (!currentRunId) {\n setError(t('data_sync.runs.detail.loadError'))\n setIsLoading(false)\n return\n }\n const call = await apiCall<SyncRunDetail>(\n `/api/data_sync/runs/${encodeURIComponent(currentRunId)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n setError(t('data_sync.runs.detail.loadError'))\n setIsLoading(false)\n return\n }\n setRun(call.result)\n setIsLoading(false)\n }, [resolveCurrentRunId, t])\n\n const loadLogs = React.useCallback(async () => {\n const currentRunId = resolveCurrentRunId()\n if (!currentRunId) return\n setIsLoadingLogs(true)\n const params = new URLSearchParams({ runId: currentRunId, pageSize: '50' })\n const call = await apiCall<{ items: LogEntry[] }>(\n `/api/integrations/logs?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n if (call.ok && call.result) {\n setLogs(call.result.items)\n }\n setIsLoadingLogs(false)\n }, [resolveCurrentRunId])\n\n React.useEffect(() => {\n void loadRun()\n void loadLogs()\n }, [loadRun, loadLogs])\n\n React.useEffect(() => {\n if (!run || (run.status !== 'running' && run.status !== 'pending')) return\n const interval = setInterval(() => { void loadRun() }, 4000)\n return () => clearInterval(interval)\n }, [run?.status, loadRun])\n\n const handleCancel = React.useCallback(async () => {\n const currentRunId = resolveCurrentRunId()\n if (!currentRunId) return\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(currentRunId)}/cancel`, {\n method: 'POST',\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.cancelSuccess'), 'success')\n void loadRun()\n } else {\n flash(t('data_sync.runs.detail.cancelError'), 'error')\n }\n }, [resolveCurrentRunId, t, loadRun])\n\n const handleRetry = React.useCallback(async () => {\n const currentRunId = resolveCurrentRunId()\n if (!currentRunId) return\n const call = await apiCall<{ id: string }>(`/api/data_sync/runs/${encodeURIComponent(currentRunId)}/retry`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromBeginning: false }),\n }, { fallback: null })\n if (call.ok && call.result) {\n flash(t('data_sync.runs.detail.retrySuccess'), 'success')\n router.push(`/backend/data-sync/runs/${encodeURIComponent(call.result.id)}`)\n } else {\n flash(t('data_sync.runs.detail.retryError'), 'error')\n }\n }, [resolveCurrentRunId, router, t])\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('data_sync.runs.detail.title')} /></PageBody></Page>\n if (error || !run) return <Page><PageBody><ErrorMessage label={error ?? t('data_sync.runs.detail.loadError')} /></PageBody></Page>\n\n const totalProcessed = run.createdCount + run.updatedCount + run.skippedCount + run.failedCount\n const progressPercent = run.progressJob?.progressPercent ?? (run.status === 'completed' ? 100 : 0)\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <div>\n <Link href=\"/backend/data-sync\" className=\"text-sm text-muted-foreground hover:underline\">\n {t('data_sync.runs.detail.back')}\n </Link>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{run.integrationId} \u2014 {run.entityType}</h1>\n <div className=\"flex gap-2 mt-2\">\n <Badge variant=\"outline\">{t(`data_sync.dashboard.direction.${run.direction}`)}</Badge>\n <Badge variant=\"secondary\" className={STATUS_STYLES[run.status] ?? ''}>\n {t(`data_sync.dashboard.status.${run.status}`)}\n </Badge>\n {run.triggeredBy && <Badge variant=\"outline\">{run.triggeredBy}</Badge>}\n </div>\n </div>\n <div className=\"flex gap-2\">\n {(run.status === 'running' || run.status === 'pending') && (\n <Button type=\"button\" variant=\"destructive\" size=\"sm\" onClick={() => void handleCancel()}>\n {t('data_sync.runs.detail.cancel')}\n </Button>\n )}\n {run.status === 'failed' && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => void handleRetry()}>\n {t('data_sync.runs.detail.retry')}\n </Button>\n )}\n </div>\n </div>\n\n {(run.status === 'running' || run.status === 'pending') && (\n <Card>\n <CardHeader>\n <CardTitle>{t('data_sync.runs.detail.progress')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <Progress value={progressPercent} className=\"h-3\" />\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>{t('data_sync.runs.detail.progress.itemsProcessed', { count: totalProcessed })}</span>\n <span>{t('data_sync.runs.detail.progress.batches', { count: run.batchesCompleted })}</span>\n </div>\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-green-600\">{run.createdCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.created')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-blue-600\">{run.updatedCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.updated')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-gray-600\">{run.skippedCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.skipped')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <div className=\"text-2xl font-bold text-red-600\">{run.failedCount}</div>\n <p className=\"text-sm text-muted-foreground\">{t('data_sync.runs.detail.counters.failed')}</p>\n </CardContent>\n </Card>\n </div>\n\n {run.lastError && (\n <Card className=\"border-red-200 bg-red-50\">\n <CardHeader>\n <CardTitle className=\"text-red-800\">{t('data_sync.runs.detail.error')}</CardTitle>\n </CardHeader>\n <CardContent>\n <pre className=\"text-sm text-red-700 whitespace-pre-wrap\">{run.lastError}</pre>\n </CardContent>\n </Card>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>{t('data_sync.runs.detail.logs')}</CardTitle>\n </CardHeader>\n <CardContent>\n {isLoadingLogs ? (\n <div className=\"flex justify-center py-4\"><Spinner /></div>\n ) : logs.length === 0 ? (\n <p className=\"text-muted-foreground text-sm\">{t('data_sync.runs.detail.noLogs')}</p>\n ) : (\n <div className=\"rounded-lg border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/50\">\n <th className=\"px-4 py-2 text-left font-medium\">{t('data_sync.runs.detail.logs.time')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('data_sync.runs.detail.logs.level')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('data_sync.runs.detail.logs.message')}</th>\n </tr>\n </thead>\n <tbody>\n {logs.map((log) => (\n <tr key={log.id} className=\"border-b last:border-0\">\n <td className=\"px-4 py-2 text-muted-foreground whitespace-nowrap\">\n {new Date(log.createdAt).toLocaleString()}\n </td>\n <td className=\"px-4 py-2\">\n <Badge variant=\"secondary\" className={LOG_LEVEL_STYLES[log.level] ?? ''}>\n {log.level}\n </Badge>\n </td>\n <td className=\"px-4 py-2\">{log.message}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["params"]
7
7
  }
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { Tenant } from "@open-mercato/core/modules/directory/data/entities";
5
5
  const metadata = {
6
+ path: "/directory/tenants/lookup",
6
7
  GET: {
7
8
  requireAuth: false
8
9
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/directory/api/get/tenants/lookup.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: {\n requireAuth: false,\n },\n}\n\nconst tenantLookupQuerySchema = z.object({\n tenantId: z.string().uuid(),\n})\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const tenantId = url.searchParams.get('tenantId') || url.searchParams.get('tenant') || ''\n const parsed = tenantLookupQuerySchema.safeParse({ tenantId })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Invalid tenant id.' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const tenant = await em.findOne(Tenant, { id: parsed.data.tenantId, deletedAt: null })\n if (!tenant) {\n return NextResponse.json({ ok: false, error: 'Tenant not found.' }, { status: 404 })\n }\n return NextResponse.json({\n ok: true,\n tenant: { id: String(tenant.id), name: tenant.name },\n })\n}\n\nconst lookupTag = 'Directory'\n\nconst tenantLookupSuccessSchema = z.object({\n ok: z.literal(true),\n tenant: z.object({\n id: z.string().uuid(),\n name: z.string(),\n }),\n})\n\nconst tenantLookupErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst tenantLookupDoc: OpenApiMethodDoc = {\n summary: 'Public tenant lookup',\n description: 'Resolves tenant metadata for login/activation flows.',\n tags: [lookupTag],\n query: tenantLookupQuerySchema,\n responses: [\n { status: 200, description: 'Tenant resolved.', schema: tenantLookupSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid tenant id', schema: tenantLookupErrorSchema },\n { status: 404, description: 'Tenant not found', schema: tenantLookupErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: lookupTag,\n summary: 'Public tenant lookup',\n methods: {\n GET: tenantLookupDoc,\n },\n}\n\nexport default GET\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,cAAc;AAGhB,MAAM,WAAW;AAAA,EACtB,KAAK;AAAA,IACH,aAAa;AAAA,EACf;AACF;AAEA,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,UAAU,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,WAAW,IAAI,aAAa,IAAI,UAAU,KAAK,IAAI,aAAa,IAAI,QAAQ,KAAK;AACvF,QAAM,SAAS,wBAAwB,UAAU,EAAE,SAAS,CAAC;AAC7D,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,OAAO,KAAK,UAAU,WAAW,KAAK,CAAC;AACrF,MAAI,CAAC,QAAQ;AACX,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrF;AACA,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK;AAAA,EACrD,CAAC;AACH;AAEA,MAAM,YAAY;AAElB,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,IACf,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,EACjB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,IAAI,EAAE,QAAQ,KAAK;AAAA,EACnB,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,kBAAoC;AAAA,EACxC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,SAAS;AAAA,EAChB,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,0BAA0B;AAAA,EACpF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,wBAAwB;AAAA,IACjF,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,EAClF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;AAEA,IAAO,iBAAQ;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n path: '/directory/tenants/lookup',\n GET: {\n requireAuth: false,\n },\n}\n\nconst tenantLookupQuerySchema = z.object({\n tenantId: z.string().uuid(),\n})\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const tenantId = url.searchParams.get('tenantId') || url.searchParams.get('tenant') || ''\n const parsed = tenantLookupQuerySchema.safeParse({ tenantId })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Invalid tenant id.' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const tenant = await em.findOne(Tenant, { id: parsed.data.tenantId, deletedAt: null })\n if (!tenant) {\n return NextResponse.json({ ok: false, error: 'Tenant not found.' }, { status: 404 })\n }\n return NextResponse.json({\n ok: true,\n tenant: { id: String(tenant.id), name: tenant.name },\n })\n}\n\nconst lookupTag = 'Directory'\n\nconst tenantLookupSuccessSchema = z.object({\n ok: z.literal(true),\n tenant: z.object({\n id: z.string().uuid(),\n name: z.string(),\n }),\n})\n\nconst tenantLookupErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst tenantLookupDoc: OpenApiMethodDoc = {\n summary: 'Public tenant lookup',\n description: 'Resolves tenant metadata for login/activation flows.',\n tags: [lookupTag],\n query: tenantLookupQuerySchema,\n responses: [\n { status: 200, description: 'Tenant resolved.', schema: tenantLookupSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid tenant id', schema: tenantLookupErrorSchema },\n { status: 404, description: 'Tenant not found', schema: tenantLookupErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: lookupTag,\n summary: 'Public tenant lookup',\n methods: {\n GET: tenantLookupDoc,\n },\n}\n\nexport default GET\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,cAAc;AAGhB,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,aAAa;AAAA,EACf;AACF;AAEA,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,UAAU,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,WAAW,IAAI,aAAa,IAAI,UAAU,KAAK,IAAI,aAAa,IAAI,QAAQ,KAAK;AACvF,QAAM,SAAS,wBAAwB,UAAU,EAAE,SAAS,CAAC;AAC7D,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,OAAO,KAAK,UAAU,WAAW,KAAK,CAAC;AACrF,MAAI,CAAC,QAAQ;AACX,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrF;AACA,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK;AAAA,EACrD,CAAC;AACH;AAEA,MAAM,YAAY;AAElB,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,IACf,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,EACjB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,IAAI,EAAE,QAAQ,KAAK;AAAA,EACnB,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,kBAAoC;AAAA,EACxC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,SAAS;AAAA,EAChB,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,0BAA0B;AAAA,EACpF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,wBAAwB;AAAA,IACjF,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,EAClF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;AAEA,IAAO,iBAAQ;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,11 @@ import { z } from "zod";
3
3
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { getBundle, getBundleIntegrations, getIntegration } from "@open-mercato/shared/modules/integrations/types";
6
+ import {
7
+ finalizeIntegrationsReadResponse,
8
+ integrationApiRoutePaths,
9
+ runIntegrationsReadBeforeInterceptors
10
+ } from "../umes-read.js";
6
11
  const idParamsSchema = z.object({ id: z.string().min(1) });
7
12
  const metadata = {
8
13
  GET: { requireAuth: true, requireFeatures: ["integrations.view"] }
@@ -26,6 +31,15 @@ async function GET(req, ctx) {
26
31
  return NextResponse.json({ error: "Integration not found" }, { status: 404 });
27
32
  }
28
33
  const container = await createRequestContainer();
34
+ const beforeInterceptors = await runIntegrationsReadBeforeInterceptors({
35
+ routePath: integrationApiRoutePaths.detail,
36
+ request: req,
37
+ auth,
38
+ container
39
+ });
40
+ if (!beforeInterceptors.ok) {
41
+ return NextResponse.json(beforeInterceptors.body, { status: beforeInterceptors.statusCode });
42
+ }
29
43
  const credentialsService = container.resolve("integrationCredentialsService");
30
44
  const stateService = container.resolve("integrationStateService");
31
45
  const scope = { organizationId: auth.orgId, tenantId: auth.tenantId };
@@ -51,18 +65,31 @@ async function GET(req, ctx) {
51
65
  };
52
66
  })
53
67
  ) : [];
54
- return NextResponse.json({
55
- integration,
56
- bundle,
57
- bundleIntegrations,
58
- state: {
59
- isEnabled: state?.isEnabled ?? true,
60
- apiVersion: state?.apiVersion ?? null,
61
- reauthRequired: state?.reauthRequired ?? false,
62
- lastHealthStatus: state?.lastHealthStatus ?? null,
63
- lastHealthCheckedAt: state?.lastHealthCheckedAt?.toISOString() ?? null
68
+ return finalizeIntegrationsReadResponse({
69
+ routePath: integrationApiRoutePaths.detail,
70
+ request: req,
71
+ auth,
72
+ container,
73
+ interceptorRequest: beforeInterceptors.request,
74
+ beforeMetadata: beforeInterceptors.metadataByInterceptor,
75
+ enrich: {
76
+ targetEntity: "integrations.integration",
77
+ recordKeys: ["integration"],
78
+ listKeys: ["bundleIntegrations"]
64
79
  },
65
- hasCredentials: Boolean(credentials)
80
+ body: {
81
+ integration,
82
+ bundle,
83
+ bundleIntegrations,
84
+ state: {
85
+ isEnabled: state?.isEnabled ?? true,
86
+ apiVersion: state?.apiVersion ?? null,
87
+ reauthRequired: state?.reauthRequired ?? false,
88
+ lastHealthStatus: state?.lastHealthStatus ?? null,
89
+ lastHealthCheckedAt: state?.lastHealthCheckedAt?.toISOString() ?? null
90
+ },
91
+ hasCredentials: Boolean(credentials)
92
+ }
66
93
  });
67
94
  }
68
95
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/integrations/api/%5Bid%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getBundle, getBundleIntegrations, getIntegration } from '@open-mercato/shared/modules/integrations/types'\nimport type { CredentialsService } from '../../lib/credentials-service'\nimport type { IntegrationStateService } from '../../lib/state-service'\n\nconst idParamsSchema = z.object({ id: z.string().min(1) })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.view'] },\n}\n\nexport const openApi = {\n tags: ['Integrations'],\n summary: 'Get integration detail',\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = idParamsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid integration id' }, { status: 400 })\n }\n\n const integration = getIntegration(parsedParams.data.id)\n if (!integration) {\n return NextResponse.json({ error: 'Integration not found' }, { status: 404 })\n }\n\n const container = await createRequestContainer()\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const stateService = container.resolve('integrationStateService') as IntegrationStateService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const [credentials, state] = await Promise.all([\n credentialsService.resolve(integration.id, scope),\n stateService.get(integration.id, scope),\n ])\n\n const bundle = integration.bundleId ? getBundle(integration.bundleId) : undefined\n const bundleIntegrations = integration.bundleId\n ? await Promise.all(\n getBundleIntegrations(integration.bundleId).map(async (item) => {\n const itemState = await stateService.get(item.id, scope)\n const resolvedState = {\n isEnabled: itemState?.isEnabled ?? true,\n apiVersion: itemState?.apiVersion ?? null,\n reauthRequired: itemState?.reauthRequired ?? false,\n lastHealthStatus: itemState?.lastHealthStatus ?? null,\n lastHealthCheckedAt: itemState?.lastHealthCheckedAt?.toISOString() ?? null,\n }\n return {\n ...item,\n isEnabled: resolvedState.isEnabled,\n state: resolvedState,\n }\n }),\n )\n : []\n\n return NextResponse.json({\n integration,\n bundle,\n bundleIntegrations,\n state: {\n isEnabled: state?.isEnabled ?? true,\n apiVersion: state?.apiVersion ?? null,\n reauthRequired: state?.reauthRequired ?? false,\n lastHealthStatus: state?.lastHealthStatus ?? null,\n lastHealthCheckedAt: state?.lastHealthCheckedAt?.toISOString() ?? null,\n },\n hasCredentials: Boolean(credentials),\n })\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,WAAW,uBAAuB,sBAAsB;AAIjE,MAAM,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAElD,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACnE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,cAAc;AAAA,EACrB,SAAS;AACX;AAEA,eAAsB,IAAI,KAAc,KAA8D;AACpG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,YAAa,IAAI,UAAU,OAAQ,IAAI,OAA4B,SAAS,aAC9E,MAAO,IAAI,SACV,IAAI;AAET,QAAM,eAAe,eAAe,UAAU,SAAS;AACvD,MAAI,CAAC,aAAa,SAAS;AACzB,WAAO,aAAa,KAAK,EAAE,OAAO,yBAAyB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/E;AAEA,QAAM,cAAc,eAAe,aAAa,KAAK,EAAE;AACvD,MAAI,CAAC,aAAa;AAChB,WAAO,aAAa,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9E;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,qBAAqB,UAAU,QAAQ,+BAA+B;AAC5E,QAAM,eAAe,UAAU,QAAQ,yBAAyB;AAChE,QAAM,QAAQ,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAS;AAE9E,QAAM,CAAC,aAAa,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7C,mBAAmB,QAAQ,YAAY,IAAI,KAAK;AAAA,IAChD,aAAa,IAAI,YAAY,IAAI,KAAK;AAAA,EACxC,CAAC;AAED,QAAM,SAAS,YAAY,WAAW,UAAU,YAAY,QAAQ,IAAI;AACxE,QAAM,qBAAqB,YAAY,WACnC,MAAM,QAAQ;AAAA,IACd,sBAAsB,YAAY,QAAQ,EAAE,IAAI,OAAO,SAAS;AAC9D,YAAM,YAAY,MAAM,aAAa,IAAI,KAAK,IAAI,KAAK;AACvD,YAAM,gBAAgB;AAAA,QACpB,WAAW,WAAW,aAAa;AAAA,QACnC,YAAY,WAAW,cAAc;AAAA,QACrC,gBAAgB,WAAW,kBAAkB;AAAA,QAC7C,kBAAkB,WAAW,oBAAoB;AAAA,QACjD,qBAAqB,WAAW,qBAAqB,YAAY,KAAK;AAAA,MACxE;AACA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,WAAW,cAAc;AAAA,QACzB,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,IACE,CAAC;AAEL,SAAO,aAAa,KAAK;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,MACL,WAAW,OAAO,aAAa;AAAA,MAC/B,YAAY,OAAO,cAAc;AAAA,MACjC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,kBAAkB,OAAO,oBAAoB;AAAA,MAC7C,qBAAqB,OAAO,qBAAqB,YAAY,KAAK;AAAA,IACpE;AAAA,IACA,gBAAgB,QAAQ,WAAW;AAAA,EACrC,CAAC;AACH;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getBundle, getBundleIntegrations, getIntegration } from '@open-mercato/shared/modules/integrations/types'\nimport type { CredentialsService } from '../../lib/credentials-service'\nimport type { IntegrationStateService } from '../../lib/state-service'\nimport {\n finalizeIntegrationsReadResponse,\n integrationApiRoutePaths,\n runIntegrationsReadBeforeInterceptors,\n} from '../umes-read'\n\nconst idParamsSchema = z.object({ id: z.string().min(1) })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.view'] },\n}\n\nexport const openApi = {\n tags: ['Integrations'],\n summary: 'Get integration detail',\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = idParamsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid integration id' }, { status: 400 })\n }\n\n const integration = getIntegration(parsedParams.data.id)\n if (!integration) {\n return NextResponse.json({ error: 'Integration not found' }, { status: 404 })\n }\n\n const container = await createRequestContainer()\n const beforeInterceptors = await runIntegrationsReadBeforeInterceptors({\n routePath: integrationApiRoutePaths.detail,\n request: req,\n auth,\n container,\n })\n if (!beforeInterceptors.ok) {\n return NextResponse.json(beforeInterceptors.body, { status: beforeInterceptors.statusCode })\n }\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const stateService = container.resolve('integrationStateService') as IntegrationStateService\n const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }\n\n const [credentials, state] = await Promise.all([\n credentialsService.resolve(integration.id, scope),\n stateService.get(integration.id, scope),\n ])\n\n const bundle = integration.bundleId ? getBundle(integration.bundleId) : undefined\n const bundleIntegrations = integration.bundleId\n ? await Promise.all(\n getBundleIntegrations(integration.bundleId).map(async (item) => {\n const itemState = await stateService.get(item.id, scope)\n const resolvedState = {\n isEnabled: itemState?.isEnabled ?? true,\n apiVersion: itemState?.apiVersion ?? null,\n reauthRequired: itemState?.reauthRequired ?? false,\n lastHealthStatus: itemState?.lastHealthStatus ?? null,\n lastHealthCheckedAt: itemState?.lastHealthCheckedAt?.toISOString() ?? null,\n }\n return {\n ...item,\n isEnabled: resolvedState.isEnabled,\n state: resolvedState,\n }\n }),\n )\n : []\n\n return finalizeIntegrationsReadResponse({\n routePath: integrationApiRoutePaths.detail,\n request: req,\n auth,\n container,\n interceptorRequest: beforeInterceptors.request,\n beforeMetadata: beforeInterceptors.metadataByInterceptor,\n enrich: {\n targetEntity: 'integrations.integration',\n recordKeys: ['integration'],\n listKeys: ['bundleIntegrations'],\n },\n body: {\n integration,\n bundle,\n bundleIntegrations,\n state: {\n isEnabled: state?.isEnabled ?? true,\n apiVersion: state?.apiVersion ?? null,\n reauthRequired: state?.reauthRequired ?? false,\n lastHealthStatus: state?.lastHealthStatus ?? null,\n lastHealthCheckedAt: state?.lastHealthCheckedAt?.toISOString() ?? null,\n },\n hasCredentials: Boolean(credentials),\n },\n })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,11 @@ import { NextResponse } from "next/server";
2
2
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { listIntegrationLogsQuerySchema } from "@open-mercato/core/modules/integrations/data/validators";
5
+ import {
6
+ finalizeIntegrationsReadResponse,
7
+ integrationApiRoutePaths,
8
+ runIntegrationsReadBeforeInterceptors
9
+ } from "../umes-read.js";
5
10
  const metadata = {
6
11
  GET: { requireAuth: true, requireFeatures: ["integrations.manage"] }
7
12
  };
@@ -14,42 +19,63 @@ async function GET(req) {
14
19
  if (!auth?.tenantId || !auth.orgId) {
15
20
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
16
21
  }
17
- const url = new URL(req.url);
22
+ const container = await createRequestContainer();
23
+ const beforeInterceptors = await runIntegrationsReadBeforeInterceptors({
24
+ routePath: integrationApiRoutePaths.logs,
25
+ request: req,
26
+ auth,
27
+ container
28
+ });
29
+ if (!beforeInterceptors.ok) {
30
+ return NextResponse.json(beforeInterceptors.body, { status: beforeInterceptors.statusCode });
31
+ }
32
+ const query = beforeInterceptors.request.query ?? {};
18
33
  const parsed = listIntegrationLogsQuerySchema.safeParse({
19
- integrationId: url.searchParams.get("integrationId") ?? void 0,
20
- level: url.searchParams.get("level") ?? void 0,
21
- runId: url.searchParams.get("runId") ?? void 0,
22
- entityType: url.searchParams.get("entityType") ?? void 0,
23
- entityId: url.searchParams.get("entityId") ?? void 0,
24
- page: url.searchParams.get("page") ?? void 0,
25
- pageSize: url.searchParams.get("pageSize") ?? void 0
34
+ integrationId: typeof query.integrationId === "string" ? query.integrationId : void 0,
35
+ level: typeof query.level === "string" ? query.level : void 0,
36
+ runId: typeof query.runId === "string" ? query.runId : void 0,
37
+ entityType: typeof query.entityType === "string" ? query.entityType : void 0,
38
+ entityId: typeof query.entityId === "string" ? query.entityId : void 0,
39
+ page: typeof query.page === "string" ? query.page : void 0,
40
+ pageSize: typeof query.pageSize === "string" ? query.pageSize : void 0
26
41
  });
27
42
  if (!parsed.success) {
28
43
  return NextResponse.json({ error: "Invalid query", details: parsed.error.flatten() }, { status: 400 });
29
44
  }
30
- const container = await createRequestContainer();
31
45
  const logService = container.resolve("integrationLogService");
32
46
  const { items, total } = await logService.query(parsed.data, {
33
47
  organizationId: auth.orgId,
34
48
  tenantId: auth.tenantId
35
49
  });
36
- return NextResponse.json({
37
- items: items.map((item) => ({
38
- id: item.id,
39
- integrationId: item.integrationId,
40
- runId: item.runId ?? null,
41
- scopeEntityType: item.scopeEntityType ?? null,
42
- scopeEntityId: item.scopeEntityId ?? null,
43
- level: item.level,
44
- message: item.message,
45
- code: item.code ?? null,
46
- payload: item.payload ?? null,
47
- createdAt: item.createdAt.toISOString()
48
- })),
49
- total,
50
- page: parsed.data.page,
51
- pageSize: parsed.data.pageSize,
52
- totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize))
50
+ return finalizeIntegrationsReadResponse({
51
+ routePath: integrationApiRoutePaths.logs,
52
+ request: req,
53
+ auth,
54
+ container,
55
+ interceptorRequest: beforeInterceptors.request,
56
+ beforeMetadata: beforeInterceptors.metadataByInterceptor,
57
+ enrich: {
58
+ targetEntity: "integrations.log",
59
+ listKeys: ["items"]
60
+ },
61
+ body: {
62
+ items: items.map((item) => ({
63
+ id: item.id,
64
+ integrationId: item.integrationId,
65
+ runId: item.runId ?? null,
66
+ scopeEntityType: item.scopeEntityType ?? null,
67
+ scopeEntityId: item.scopeEntityId ?? null,
68
+ level: item.level,
69
+ message: item.message,
70
+ code: item.code ?? null,
71
+ payload: item.payload ?? null,
72
+ createdAt: item.createdAt.toISOString()
73
+ })),
74
+ total,
75
+ page: parsed.data.page,
76
+ pageSize: parsed.data.pageSize,
77
+ totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize))
78
+ }
53
79
  });
54
80
  }
55
81
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/integrations/api/logs/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { listIntegrationLogsQuerySchema } from '@open-mercato/core/modules/integrations/data/validators'\nimport type { IntegrationLogService } from '@open-mercato/core/modules/integrations/lib/log-service'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.manage'] },\n}\n\nexport const openApi = {\n tags: ['Integrations'],\n summary: 'List integration logs',\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = listIntegrationLogsQuerySchema.safeParse({\n integrationId: url.searchParams.get('integrationId') ?? undefined,\n level: url.searchParams.get('level') ?? undefined,\n runId: url.searchParams.get('runId') ?? undefined,\n entityType: url.searchParams.get('entityType') ?? undefined,\n entityId: url.searchParams.get('entityId') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const logService = container.resolve('integrationLogService') as IntegrationLogService\n\n const { items, total } = await logService.query(parsed.data, {\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n\n return NextResponse.json({\n items: items.map((item) => ({\n id: item.id,\n integrationId: item.integrationId,\n runId: item.runId ?? null,\n scopeEntityType: item.scopeEntityType ?? null,\n scopeEntityId: item.scopeEntityId ?? null,\n level: item.level,\n message: item.message,\n code: item.code ?? null,\n payload: item.payload ?? null,\n createdAt: item.createdAt.toISOString(),\n })),\n total,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize)),\n })\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,sCAAsC;AAGxC,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACrE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,cAAc;AAAA,EACrB,SAAS;AACX;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,SAAS,+BAA+B,UAAU;AAAA,IACtD,eAAe,IAAI,aAAa,IAAI,eAAe,KAAK;AAAA,IACxD,OAAO,IAAI,aAAa,IAAI,OAAO,KAAK;AAAA,IACxC,OAAO,IAAI,aAAa,IAAI,OAAO,KAAK;AAAA,IACxC,YAAY,IAAI,aAAa,IAAI,YAAY,KAAK;AAAA,IAClD,UAAU,IAAI,aAAa,IAAI,UAAU,KAAK;AAAA,IAC9C,MAAM,IAAI,aAAa,IAAI,MAAM,KAAK;AAAA,IACtC,UAAU,IAAI,aAAa,IAAI,UAAU,KAAK;AAAA,EAChD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,aAAa,UAAU,QAAQ,uBAAuB;AAE5D,QAAM,EAAE,OAAO,MAAM,IAAI,MAAM,WAAW,MAAM,OAAO,MAAM;AAAA,IAC3D,gBAAgB,KAAK;AAAA,IACrB,UAAU,KAAK;AAAA,EACjB,CAAC;AAED,SAAO,aAAa,KAAK;AAAA,IACvB,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,MAC1B,IAAI,KAAK;AAAA,MACT,eAAe,KAAK;AAAA,MACpB,OAAO,KAAK,SAAS;AAAA,MACrB,iBAAiB,KAAK,mBAAmB;AAAA,MACzC,eAAe,KAAK,iBAAiB;AAAA,MACrC,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,MAAM,KAAK,QAAQ;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,MACzB,WAAW,KAAK,UAAU,YAAY;AAAA,IACxC,EAAE;AAAA,IACF;AAAA,IACA,MAAM,OAAO,KAAK;AAAA,IAClB,UAAU,OAAO,KAAK;AAAA,IACtB,YAAY,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,OAAO,KAAK,QAAQ,CAAC;AAAA,EACjE,CAAC;AACH;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { listIntegrationLogsQuerySchema } from '@open-mercato/core/modules/integrations/data/validators'\nimport type { IntegrationLogService } from '@open-mercato/core/modules/integrations/lib/log-service'\nimport {\n finalizeIntegrationsReadResponse,\n integrationApiRoutePaths,\n runIntegrationsReadBeforeInterceptors,\n} from '../umes-read'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.manage'] },\n}\n\nexport const openApi = {\n tags: ['Integrations'],\n summary: 'List integration logs',\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const beforeInterceptors = await runIntegrationsReadBeforeInterceptors({\n routePath: integrationApiRoutePaths.logs,\n request: req,\n auth,\n container,\n })\n if (!beforeInterceptors.ok) {\n return NextResponse.json(beforeInterceptors.body, { status: beforeInterceptors.statusCode })\n }\n\n const query = beforeInterceptors.request.query ?? {}\n const parsed = listIntegrationLogsQuerySchema.safeParse({\n integrationId: typeof query.integrationId === 'string' ? query.integrationId : undefined,\n level: typeof query.level === 'string' ? query.level : undefined,\n runId: typeof query.runId === 'string' ? query.runId : undefined,\n entityType: typeof query.entityType === 'string' ? query.entityType : undefined,\n entityId: typeof query.entityId === 'string' ? query.entityId : undefined,\n page: typeof query.page === 'string' ? query.page : undefined,\n pageSize: typeof query.pageSize === 'string' ? query.pageSize : undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query', details: parsed.error.flatten() }, { status: 400 })\n }\n const logService = container.resolve('integrationLogService') as IntegrationLogService\n\n const { items, total } = await logService.query(parsed.data, {\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n\n return finalizeIntegrationsReadResponse({\n routePath: integrationApiRoutePaths.logs,\n request: req,\n auth,\n container,\n interceptorRequest: beforeInterceptors.request,\n beforeMetadata: beforeInterceptors.metadataByInterceptor,\n enrich: {\n targetEntity: 'integrations.log',\n listKeys: ['items'],\n },\n body: {\n items: items.map((item) => ({\n id: item.id,\n integrationId: item.integrationId,\n runId: item.runId ?? null,\n scopeEntityType: item.scopeEntityType ?? null,\n scopeEntityId: item.scopeEntityId ?? null,\n level: item.level,\n message: item.message,\n code: item.code ?? null,\n payload: item.payload ?? null,\n createdAt: item.createdAt.toISOString(),\n })),\n total,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n totalPages: Math.max(1, Math.ceil(total / parsed.data.pageSize)),\n },\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,sCAAsC;AAE/C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACrE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,cAAc;AAAA,EACrB,SAAS;AACX;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,qBAAqB,MAAM,sCAAsC;AAAA,IACrE,WAAW,yBAAyB;AAAA,IACpC,SAAS;AAAA,IACT;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,CAAC,mBAAmB,IAAI;AAC1B,WAAO,aAAa,KAAK,mBAAmB,MAAM,EAAE,QAAQ,mBAAmB,WAAW,CAAC;AAAA,EAC7F;AAEA,QAAM,QAAQ,mBAAmB,QAAQ,SAAS,CAAC;AACnD,QAAM,SAAS,+BAA+B,UAAU;AAAA,IACtD,eAAe,OAAO,MAAM,kBAAkB,WAAW,MAAM,gBAAgB;AAAA,IAC/E,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,IACvD,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,IACvD,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAAA,IACtE,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;AAAA,IAChE,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;AAAA,EAClE,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvG;AACA,QAAM,aAAa,UAAU,QAAQ,uBAAuB;AAE5D,QAAM,EAAE,OAAO,MAAM,IAAI,MAAM,WAAW,MAAM,OAAO,MAAM;AAAA,IAC3D,gBAAgB,KAAK;AAAA,IACrB,UAAU,KAAK;AAAA,EACjB,CAAC;AAED,SAAO,iCAAiC;AAAA,IACtC,WAAW,yBAAyB;AAAA,IACpC,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,oBAAoB,mBAAmB;AAAA,IACvC,gBAAgB,mBAAmB;AAAA,IACnC,QAAQ;AAAA,MACN,cAAc;AAAA,MACd,UAAU,CAAC,OAAO;AAAA,IACpB;AAAA,IACA,MAAM;AAAA,MACJ,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,QAC1B,IAAI,KAAK;AAAA,QACT,eAAe,KAAK;AAAA,QACpB,OAAO,KAAK,SAAS;AAAA,QACrB,iBAAiB,KAAK,mBAAmB;AAAA,QACzC,eAAe,KAAK,iBAAiB;AAAA,QACrC,OAAO,KAAK;AAAA,QACZ,SAAS,KAAK;AAAA,QACd,MAAM,KAAK,QAAQ;AAAA,QACnB,SAAS,KAAK,WAAW;AAAA,QACzB,WAAW,KAAK,UAAU,YAAY;AAAA,MACxC,EAAE;AAAA,MACF;AAAA,MACA,MAAM,OAAO,KAAK;AAAA,MAClB,UAAU,OAAO,KAAK;AAAA,MACtB,YAAY,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,OAAO,KAAK,QAAQ,CAAC;AAAA,IACjE;AAAA,EACF,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,11 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { getAllBundles, getAllIntegrations } from "@open-mercato/shared/modules/integrations/types";
5
5
  import { buildIntegrationsCrudOpenApi, createPagedListResponseSchema, integrationInfoSchema } from "./openapi.js";
6
+ import {
7
+ finalizeIntegrationsReadResponse,
8
+ integrationApiRoutePaths,
9
+ runIntegrationsReadBeforeInterceptors
10
+ } from "./umes-read.js";
6
11
  const metadata = {
7
12
  GET: { requireAuth: true, requireFeatures: ["integrations.view"] }
8
13
  };
@@ -18,6 +23,15 @@ async function GET(req) {
18
23
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
19
24
  }
20
25
  const container = await createRequestContainer();
26
+ const beforeInterceptors = await runIntegrationsReadBeforeInterceptors({
27
+ routePath: integrationApiRoutePaths.list,
28
+ request: req,
29
+ auth,
30
+ container
31
+ });
32
+ if (!beforeInterceptors.ok) {
33
+ return NextResponse.json(beforeInterceptors.body, { status: beforeInterceptors.statusCode });
34
+ }
21
35
  const credentialsService = container.resolve("integrationCredentialsService");
22
36
  const stateService = container.resolve("integrationStateService");
23
37
  const rows = await Promise.all(
@@ -29,10 +43,14 @@ async function GET(req) {
29
43
  return {
30
44
  id: integration.id,
31
45
  title: integration.title,
46
+ description: integration.description ?? null,
32
47
  category: integration.category ?? null,
33
48
  hub: integration.hub ?? null,
34
49
  providerKey: integration.providerKey ?? null,
35
50
  bundleId: integration.bundleId ?? null,
51
+ author: integration.author ?? null,
52
+ company: integration.company ?? null,
53
+ version: integration.version ?? null,
36
54
  hasCredentials: Boolean(resolvedCredentials),
37
55
  isEnabled: state?.isEnabled ?? true,
38
56
  apiVersion: state?.apiVersion ?? null
@@ -51,13 +69,25 @@ async function GET(req) {
51
69
  enabledCount
52
70
  };
53
71
  });
54
- return NextResponse.json({
55
- items: rows,
56
- bundles,
57
- total: rows.length,
58
- page: 1,
59
- pageSize: 100,
60
- totalPages: 1
72
+ return finalizeIntegrationsReadResponse({
73
+ routePath: integrationApiRoutePaths.list,
74
+ request: req,
75
+ auth,
76
+ container,
77
+ interceptorRequest: beforeInterceptors.request,
78
+ beforeMetadata: beforeInterceptors.metadataByInterceptor,
79
+ enrich: {
80
+ targetEntity: "integrations.integration",
81
+ listKeys: ["items"]
82
+ },
83
+ body: {
84
+ items: rows,
85
+ bundles,
86
+ total: rows.length,
87
+ page: 1,
88
+ pageSize: 100,
89
+ totalPages: 1
90
+ }
61
91
  });
62
92
  }
63
93
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/integrations/api/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CredentialsService } from '../lib/credentials-service'\nimport type { IntegrationStateService } from '../lib/state-service'\nimport { getAllBundles, getAllIntegrations } from '@open-mercato/shared/modules/integrations/types'\nimport { buildIntegrationsCrudOpenApi, createPagedListResponseSchema, integrationInfoSchema } from './openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.view'] },\n}\n\nexport const openApi = buildIntegrationsCrudOpenApi({\n resourceName: 'Integration',\n pluralName: 'Integrations',\n listResponseSchema: createPagedListResponseSchema(integrationInfoSchema),\n querySchema: undefined,\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const stateService = container.resolve('integrationStateService') as IntegrationStateService\n\n const rows = await Promise.all(\n getAllIntegrations().map(async (integration) => {\n const [resolvedCredentials, state] = await Promise.all([\n credentialsService.resolve(integration.id, { organizationId: auth.orgId as string, tenantId: auth.tenantId as string }),\n stateService.get(integration.id, { organizationId: auth.orgId as string, tenantId: auth.tenantId as string }),\n ])\n\n return {\n id: integration.id,\n title: integration.title,\n category: integration.category ?? null,\n hub: integration.hub ?? null,\n providerKey: integration.providerKey ?? null,\n bundleId: integration.bundleId ?? null,\n hasCredentials: Boolean(resolvedCredentials),\n isEnabled: state?.isEnabled ?? true,\n apiVersion: state?.apiVersion ?? null,\n }\n }),\n )\n\n const bundles = getAllBundles().map((bundle) => {\n const bundleIntegrations = rows.filter((row) => row.bundleId === bundle.id)\n const enabledCount = bundleIntegrations.reduce((count, integration) => count + (integration.isEnabled ? 1 : 0), 0)\n\n return {\n id: bundle.id,\n title: bundle.title,\n description: bundle.description,\n icon: bundle.icon ?? null,\n integrationCount: bundleIntegrations.length,\n enabledCount,\n }\n })\n\n return NextResponse.json({\n items: rows,\n bundles,\n total: rows.length,\n page: 1,\n pageSize: 100,\n totalPages: 1,\n })\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAGvC,SAAS,eAAe,0BAA0B;AAClD,SAAS,8BAA8B,+BAA+B,6BAA6B;AAE5F,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACnE;AAEO,MAAM,UAAU,6BAA6B;AAAA,EAClD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,oBAAoB,8BAA8B,qBAAqB;AAAA,EACvE,aAAa;AACf,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,qBAAqB,UAAU,QAAQ,+BAA+B;AAC5E,QAAM,eAAe,UAAU,QAAQ,yBAAyB;AAEhE,QAAM,OAAO,MAAM,QAAQ;AAAA,IACzB,mBAAmB,EAAE,IAAI,OAAO,gBAAgB;AAC9C,YAAM,CAAC,qBAAqB,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,QACrD,mBAAmB,QAAQ,YAAY,IAAI,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAmB,CAAC;AAAA,QACtH,aAAa,IAAI,YAAY,IAAI,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAmB,CAAC;AAAA,MAC9G,CAAC;AAED,aAAO;AAAA,QACL,IAAI,YAAY;AAAA,QAChB,OAAO,YAAY;AAAA,QACnB,UAAU,YAAY,YAAY;AAAA,QAClC,KAAK,YAAY,OAAO;AAAA,QACxB,aAAa,YAAY,eAAe;AAAA,QACxC,UAAU,YAAY,YAAY;AAAA,QAClC,gBAAgB,QAAQ,mBAAmB;AAAA,QAC3C,WAAW,OAAO,aAAa;AAAA,QAC/B,YAAY,OAAO,cAAc;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,cAAc,EAAE,IAAI,CAAC,WAAW;AAC9C,UAAM,qBAAqB,KAAK,OAAO,CAAC,QAAQ,IAAI,aAAa,OAAO,EAAE;AAC1E,UAAM,eAAe,mBAAmB,OAAO,CAAC,OAAO,gBAAgB,SAAS,YAAY,YAAY,IAAI,IAAI,CAAC;AAEjH,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,MACX,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO,QAAQ;AAAA,MACrB,kBAAkB,mBAAmB;AAAA,MACrC;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,aAAa,KAAK;AAAA,IACvB,OAAO;AAAA,IACP;AAAA,IACA,OAAO,KAAK;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,EACd,CAAC;AACH;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CredentialsService } from '../lib/credentials-service'\nimport type { IntegrationStateService } from '../lib/state-service'\nimport { getAllBundles, getAllIntegrations } from '@open-mercato/shared/modules/integrations/types'\nimport { buildIntegrationsCrudOpenApi, createPagedListResponseSchema, integrationInfoSchema } from './openapi'\nimport {\n finalizeIntegrationsReadResponse,\n integrationApiRoutePaths,\n runIntegrationsReadBeforeInterceptors,\n} from './umes-read'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.view'] },\n}\n\nexport const openApi = buildIntegrationsCrudOpenApi({\n resourceName: 'Integration',\n pluralName: 'Integrations',\n listResponseSchema: createPagedListResponseSchema(integrationInfoSchema),\n querySchema: undefined,\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const beforeInterceptors = await runIntegrationsReadBeforeInterceptors({\n routePath: integrationApiRoutePaths.list,\n request: req,\n auth,\n container,\n })\n if (!beforeInterceptors.ok) {\n return NextResponse.json(beforeInterceptors.body, { status: beforeInterceptors.statusCode })\n }\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const stateService = container.resolve('integrationStateService') as IntegrationStateService\n\n const rows = await Promise.all(\n getAllIntegrations().map(async (integration) => {\n const [resolvedCredentials, state] = await Promise.all([\n credentialsService.resolve(integration.id, { organizationId: auth.orgId as string, tenantId: auth.tenantId as string }),\n stateService.get(integration.id, { organizationId: auth.orgId as string, tenantId: auth.tenantId as string }),\n ])\n\n return {\n id: integration.id,\n title: integration.title,\n description: integration.description ?? null,\n category: integration.category ?? null,\n hub: integration.hub ?? null,\n providerKey: integration.providerKey ?? null,\n bundleId: integration.bundleId ?? null,\n author: integration.author ?? null,\n company: integration.company ?? null,\n version: integration.version ?? null,\n hasCredentials: Boolean(resolvedCredentials),\n isEnabled: state?.isEnabled ?? true,\n apiVersion: state?.apiVersion ?? null,\n }\n }),\n )\n\n const bundles = getAllBundles().map((bundle) => {\n const bundleIntegrations = rows.filter((row) => row.bundleId === bundle.id)\n const enabledCount = bundleIntegrations.reduce((count, integration) => count + (integration.isEnabled ? 1 : 0), 0)\n\n return {\n id: bundle.id,\n title: bundle.title,\n description: bundle.description,\n icon: bundle.icon ?? null,\n integrationCount: bundleIntegrations.length,\n enabledCount,\n }\n })\n\n return finalizeIntegrationsReadResponse({\n routePath: integrationApiRoutePaths.list,\n request: req,\n auth,\n container,\n interceptorRequest: beforeInterceptors.request,\n beforeMetadata: beforeInterceptors.metadataByInterceptor,\n enrich: {\n targetEntity: 'integrations.integration',\n listKeys: ['items'],\n },\n body: {\n items: rows,\n bundles,\n total: rows.length,\n page: 1,\n pageSize: 100,\n totalPages: 1,\n },\n })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,121 @@
1
+ import { NextResponse } from "next/server";
2
+ import { runApiInterceptorsAfter, runApiInterceptorsBefore } from "@open-mercato/shared/lib/crud/interceptor-runner";
3
+ import { applyResponseEnrichers, applyResponseEnricherToRecord } from "@open-mercato/shared/lib/crud/enricher-runner";
4
+ const integrationApiRoutePaths = {
5
+ list: "integrations",
6
+ detail: "integrations/detail",
7
+ logs: "integrations/logs"
8
+ };
9
+ function headersToObject(headers) {
10
+ return Object.fromEntries(headers.entries());
11
+ }
12
+ function searchParamsToObject(url) {
13
+ const entries = {};
14
+ url.searchParams.forEach((value, key) => {
15
+ entries[key] = value;
16
+ });
17
+ return entries;
18
+ }
19
+ function toUserFeatures(features) {
20
+ if (!Array.isArray(features)) return [];
21
+ return features.filter((value) => typeof value === "string");
22
+ }
23
+ function mergeAdditiveRecord(base, candidate) {
24
+ const additions = Object.fromEntries(
25
+ Object.entries(candidate).filter(([key]) => !(key in base))
26
+ );
27
+ return {
28
+ ...base,
29
+ ...additions
30
+ };
31
+ }
32
+ function mergeAdditiveBody(baseBody, candidateBody) {
33
+ return mergeAdditiveRecord(baseBody, candidateBody);
34
+ }
35
+ function getEnricherContext(input) {
36
+ return {
37
+ organizationId: input.auth.orgId,
38
+ tenantId: input.auth.tenantId,
39
+ userId: input.auth.sub ?? "",
40
+ em: input.container.resolve("em"),
41
+ container: input.container,
42
+ userFeatures: toUserFeatures(input.auth.features)
43
+ };
44
+ }
45
+ async function runIntegrationsReadBeforeInterceptors(input) {
46
+ const url = new URL(input.request.url);
47
+ return runApiInterceptorsBefore({
48
+ routePath: input.routePath,
49
+ method: "GET",
50
+ request: {
51
+ method: "GET",
52
+ url: input.request.url,
53
+ headers: headersToObject(input.request.headers),
54
+ query: searchParamsToObject(url)
55
+ },
56
+ context: getEnricherContext(input)
57
+ });
58
+ }
59
+ async function applySafeRecordEnrichment(record, targetEntity, context) {
60
+ const enriched = await applyResponseEnricherToRecord(record, targetEntity, context);
61
+ return mergeAdditiveRecord(record, enriched.record);
62
+ }
63
+ async function applySafeListEnrichment(items, targetEntity, context) {
64
+ const enriched = await applyResponseEnrichers(items, targetEntity, context);
65
+ return items.map((item, index) => {
66
+ const enrichedItem = enriched.items[index];
67
+ if (!enrichedItem) return item;
68
+ return mergeAdditiveRecord(item, enrichedItem);
69
+ });
70
+ }
71
+ async function finalizeIntegrationsReadResponse(input) {
72
+ const method = input.method ?? "GET";
73
+ const baseBody = input.body;
74
+ const context = getEnricherContext(input);
75
+ const intercepted = await runApiInterceptorsAfter({
76
+ routePath: input.routePath,
77
+ method,
78
+ request: input.interceptorRequest,
79
+ response: {
80
+ statusCode: 200,
81
+ body: baseBody,
82
+ headers: {}
83
+ },
84
+ context,
85
+ metadataByInterceptor: input.beforeMetadata
86
+ });
87
+ if (!intercepted.ok) {
88
+ return NextResponse.json(intercepted.body, { status: intercepted.statusCode, headers: intercepted.headers });
89
+ }
90
+ let responseBody = mergeAdditiveBody(baseBody, intercepted.body);
91
+ if (input.enrich) {
92
+ const { targetEntity, recordKeys = [], listKeys = [] } = input.enrich;
93
+ for (const key of recordKeys) {
94
+ const current = responseBody[key];
95
+ if (!current || typeof current !== "object" || Array.isArray(current)) continue;
96
+ responseBody = {
97
+ ...responseBody,
98
+ [key]: await applySafeRecordEnrichment(current, targetEntity, context)
99
+ };
100
+ }
101
+ for (const key of listKeys) {
102
+ const current = responseBody[key];
103
+ if (!Array.isArray(current)) continue;
104
+ const records = current.filter(
105
+ (item) => typeof item === "object" && item !== null && !Array.isArray(item)
106
+ );
107
+ if (records.length !== current.length) continue;
108
+ responseBody = {
109
+ ...responseBody,
110
+ [key]: await applySafeListEnrichment(records, targetEntity, context)
111
+ };
112
+ }
113
+ }
114
+ return NextResponse.json(responseBody, { headers: intercepted.headers });
115
+ }
116
+ export {
117
+ finalizeIntegrationsReadResponse,
118
+ integrationApiRoutePaths,
119
+ runIntegrationsReadBeforeInterceptors
120
+ };
121
+ //# sourceMappingURL=umes-read.js.map