@open-mercato/core 0.6.6-develop.6551.1.1511bfa890 → 0.6.6

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 (304) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities.ids.generated.js +0 -1
  3. package/dist/generated/entities.ids.generated.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +0 -12
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/modules/api_keys/api/keys/route.js +13 -30
  7. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  8. package/dist/modules/api_keys/cli.js +1 -2
  9. package/dist/modules/api_keys/cli.js.map +2 -2
  10. package/dist/modules/attachments/api/file/[id]/route.js +7 -9
  11. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  12. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +7 -9
  13. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  14. package/dist/modules/attachments/api/route.js +17 -21
  15. package/dist/modules/attachments/api/route.js.map +2 -2
  16. package/dist/modules/attachments/components/AttachmentContentPreview.js +35 -6
  17. package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
  18. package/dist/modules/audit_logs/backend/audit-logs/page.js +24 -14
  19. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  20. package/dist/modules/auth/api/users/route.js +2 -6
  21. package/dist/modules/auth/api/users/route.js.map +2 -2
  22. package/dist/modules/auth/cli.js +2 -3
  23. package/dist/modules/auth/cli.js.map +2 -2
  24. package/dist/modules/configs/lib/upgrade-actions.js +0 -22
  25. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  26. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +0 -1
  27. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
  28. package/dist/modules/customer_accounts/api/admin/roles/[id].js +3 -17
  29. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  30. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +1 -6
  31. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +2 -2
  32. package/dist/modules/customer_accounts/api/portal/users-invite.js +1 -6
  33. package/dist/modules/customer_accounts/api/portal/users-invite.js.map +2 -2
  34. package/dist/modules/customer_accounts/services/customerInvitationService.js +0 -1
  35. package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
  36. package/dist/modules/customer_accounts/services/customerRbacService.js +1 -6
  37. package/dist/modules/customer_accounts/services/customerRbacService.js.map +2 -2
  38. package/dist/modules/customer_accounts/widgets/injection-table.js +2 -0
  39. package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
  40. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +1 -7
  41. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  42. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +2 -2
  43. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +2 -2
  44. package/dist/modules/customers/components/detail/CompanyDetailTabs.js +43 -37
  45. package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
  46. package/dist/modules/customers/components/detail/DealDetailTabs.js +35 -23
  47. package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
  48. package/dist/modules/customers/components/detail/DetailTabsLayout.js +21 -6
  49. package/dist/modules/customers/components/detail/DetailTabsLayout.js.map +2 -2
  50. package/dist/modules/customers/components/detail/MobilePersonDetail.js +32 -26
  51. package/dist/modules/customers/components/detail/MobilePersonDetail.js.map +2 -2
  52. package/dist/modules/customers/components/detail/PersonDetailTabs.js +39 -29
  53. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  54. package/dist/modules/dashboards/api/roles/widgets/route.js +3 -22
  55. package/dist/modules/dashboards/api/roles/widgets/route.js.map +2 -2
  56. package/dist/modules/dashboards/api/users/widgets/route.js +3 -22
  57. package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
  58. package/dist/modules/dashboards/cli.js +3 -4
  59. package/dist/modules/dashboards/cli.js.map +2 -2
  60. package/dist/modules/dashboards/lib/widgetAssignmentScope.js +1 -9
  61. package/dist/modules/dashboards/lib/widgetAssignmentScope.js.map +2 -2
  62. package/dist/modules/dictionaries/commands/entries.js +14 -7
  63. package/dist/modules/dictionaries/commands/entries.js.map +2 -2
  64. package/dist/modules/dictionaries/commands/entry-operations.js +16 -7
  65. package/dist/modules/dictionaries/commands/entry-operations.js.map +2 -2
  66. package/dist/modules/dictionaries/commands/factory.js +3 -9
  67. package/dist/modules/dictionaries/commands/factory.js.map +2 -2
  68. package/dist/modules/dictionaries/fields/dictionary.js +41 -56
  69. package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
  70. package/dist/modules/directory/utils/organizationScope.js +3 -10
  71. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  72. package/dist/modules/entities/api/records.js +4 -5
  73. package/dist/modules/entities/api/records.js.map +2 -2
  74. package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js +0 -1
  75. package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js.map +2 -2
  76. package/dist/modules/entities/backend/entities/user/[entityId]/page.js +49 -49
  77. package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
  78. package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js +0 -1
  79. package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js.map +2 -2
  80. package/dist/modules/entities/cli.js +1 -2
  81. package/dist/modules/entities/cli.js.map +2 -2
  82. package/dist/modules/feature_toggles/api/check/boolean/route.js +14 -0
  83. package/dist/modules/feature_toggles/api/check/boolean/route.js.map +2 -2
  84. package/dist/modules/feature_toggles/defaults.json +0 -8
  85. package/dist/modules/integrations/backend/integrations/[id]/page.js +80 -9
  86. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  87. package/dist/modules/payment_gateways/data/entities.js +0 -45
  88. package/dist/modules/payment_gateways/data/entities.js.map +2 -2
  89. package/dist/modules/payment_gateways/lib/gateway-service.js +38 -173
  90. package/dist/modules/payment_gateways/lib/gateway-service.js.map +3 -3
  91. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +14 -16
  92. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +2 -2
  93. package/dist/modules/planner/components/AvailabilityRulesEditor.js +30 -18
  94. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  95. package/dist/modules/query_index/api/status.js +8 -2
  96. package/dist/modules/query_index/api/status.js.map +2 -2
  97. package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -24
  98. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  99. package/dist/modules/sales/backend/sales/channels/create/page.js +0 -6
  100. package/dist/modules/sales/backend/sales/channels/create/page.js.map +2 -2
  101. package/dist/modules/sales/backend/sales/channels/create/page.meta.js +0 -2
  102. package/dist/modules/sales/backend/sales/channels/create/page.meta.js.map +2 -2
  103. package/dist/modules/sales/backend/sales/channels/offers/page.js +0 -6
  104. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  105. package/dist/modules/sales/backend/sales/channels/offers/page.meta.js +0 -2
  106. package/dist/modules/sales/backend/sales/channels/offers/page.meta.js.map +2 -2
  107. package/dist/modules/sales/backend/sales/channels/page.js +0 -6
  108. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  109. package/dist/modules/sales/backend/sales/channels/page.meta.js +0 -2
  110. package/dist/modules/sales/backend/sales/channels/page.meta.js.map +2 -2
  111. package/dist/modules/sales/backend/sales/documents/[id]/page.js +4 -6
  112. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  113. package/dist/modules/sales/commands/documents.js +0 -44
  114. package/dist/modules/sales/commands/documents.js.map +2 -2
  115. package/dist/modules/sales/commands/returns.js +27 -2
  116. package/dist/modules/sales/commands/returns.js.map +2 -2
  117. package/dist/modules/sales/components/documents/AddressesSection.js +2 -6
  118. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  119. package/dist/modules/sales/components/documents/ItemsSection.js +0 -1
  120. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  121. package/dist/modules/sales/components/documents/LineItemDialog.js +0 -9
  122. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  123. package/dist/modules/sales/components/documents/SalesDocumentForm.js +3 -51
  124. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  125. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +8 -10
  126. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  127. package/dist/modules/sales/components/documents/ShipmentDialog.js +38 -59
  128. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  129. package/dist/modules/sales/lib/shipments/snapshots.js +1 -27
  130. package/dist/modules/sales/lib/shipments/snapshots.js.map +2 -2
  131. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +25 -22
  132. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  133. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +14 -12
  134. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  135. package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js +29 -10
  136. package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js.map +2 -2
  137. package/dist/modules/sync_excel/lib/adapters/customers.js +20 -19
  138. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  139. package/dist/modules/sync_excel/lib/parser.js +0 -185
  140. package/dist/modules/sync_excel/lib/parser.js.map +2 -2
  141. package/dist/modules/sync_excel/lib/upload-storage.js +1 -20
  142. package/dist/modules/sync_excel/lib/upload-storage.js.map +2 -2
  143. package/dist/modules/workflows/frontend/checkout-demo/page.js +8 -1
  144. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  145. package/dist/modules/workflows/lib/activity-executor.js +1 -1
  146. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  147. package/dist/modules/workflows/lib/find-definition.js +18 -2
  148. package/dist/modules/workflows/lib/find-definition.js.map +2 -2
  149. package/dist/modules/workflows/lib/signal-handler.js +3 -2
  150. package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
  151. package/dist/modules/workflows/lib/step-handler.js +4 -8
  152. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  153. package/dist/modules/workflows/lib/task-handler.js +3 -5
  154. package/dist/modules/workflows/lib/task-handler.js.map +2 -2
  155. package/dist/modules/workflows/lib/timer-handler.js +2 -1
  156. package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
  157. package/dist/modules/workflows/lib/transition-handler.js +5 -13
  158. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  159. package/dist/modules/workflows/lib/workflow-executor.js +11 -9
  160. package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
  161. package/dist/modules/workflows/migrations/Migration20260715120000.js +68 -0
  162. package/dist/modules/workflows/migrations/Migration20260715120000.js.map +7 -0
  163. package/dist/modules/workflows/migrations/Migration20260716120000.js +24 -0
  164. package/dist/modules/workflows/migrations/Migration20260716120000.js.map +7 -0
  165. package/dist/modules/workflows/workflows.js +2 -0
  166. package/dist/modules/workflows/workflows.js.map +2 -2
  167. package/generated/entities.ids.generated.ts +0 -1
  168. package/generated/entity-fields-registry.ts +0 -12
  169. package/package.json +8 -9
  170. package/src/modules/api_keys/api/keys/route.ts +13 -36
  171. package/src/modules/api_keys/cli.ts +3 -2
  172. package/src/modules/attachments/api/file/[id]/route.ts +7 -11
  173. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +7 -11
  174. package/src/modules/attachments/api/route.ts +21 -26
  175. package/src/modules/attachments/components/AttachmentContentPreview.tsx +44 -9
  176. package/src/modules/audit_logs/backend/audit-logs/page.tsx +22 -12
  177. package/src/modules/auth/api/users/route.ts +2 -11
  178. package/src/modules/auth/cli.ts +2 -3
  179. package/src/modules/configs/i18n/de.json +1 -5
  180. package/src/modules/configs/i18n/en.json +1 -5
  181. package/src/modules/configs/i18n/es.json +1 -5
  182. package/src/modules/configs/i18n/pl.json +1 -5
  183. package/src/modules/configs/lib/upgrade-actions.ts +0 -24
  184. package/src/modules/customer_accounts/AGENTS.md +6 -8
  185. package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +0 -1
  186. package/src/modules/customer_accounts/api/admin/roles/[id].ts +3 -17
  187. package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +1 -6
  188. package/src/modules/customer_accounts/api/portal/users-invite.ts +1 -6
  189. package/src/modules/customer_accounts/services/customerInvitationService.ts +0 -1
  190. package/src/modules/customer_accounts/services/customerRbacService.ts +2 -9
  191. package/src/modules/customer_accounts/widgets/injection-table.ts +2 -0
  192. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +1 -11
  193. package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +2 -6
  194. package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +55 -39
  195. package/src/modules/customers/components/detail/DealDetailTabs.tsx +49 -24
  196. package/src/modules/customers/components/detail/DetailTabsLayout.tsx +24 -13
  197. package/src/modules/customers/components/detail/MobilePersonDetail.tsx +31 -25
  198. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +50 -29
  199. package/src/modules/dashboards/api/roles/widgets/route.ts +3 -22
  200. package/src/modules/dashboards/api/users/widgets/route.ts +3 -22
  201. package/src/modules/dashboards/cli.ts +9 -4
  202. package/src/modules/dashboards/lib/widgetAssignmentScope.ts +0 -18
  203. package/src/modules/dictionaries/commands/entries.ts +16 -7
  204. package/src/modules/dictionaries/commands/entry-operations.ts +18 -8
  205. package/src/modules/dictionaries/commands/factory.ts +2 -8
  206. package/src/modules/dictionaries/fields/dictionary.tsx +32 -55
  207. package/src/modules/dictionaries/i18n/de.json +0 -2
  208. package/src/modules/dictionaries/i18n/en.json +0 -2
  209. package/src/modules/dictionaries/i18n/es.json +0 -2
  210. package/src/modules/dictionaries/i18n/pl.json +0 -2
  211. package/src/modules/directory/utils/organizationScope.ts +2 -36
  212. package/src/modules/entities/api/records.ts +7 -5
  213. package/src/modules/entities/backend/entities/system/[entityId]/page.meta.ts +0 -1
  214. package/src/modules/entities/backend/entities/user/[entityId]/page.meta.ts +0 -1
  215. package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +49 -51
  216. package/src/modules/entities/cli.ts +1 -2
  217. package/src/modules/entities/i18n/de.json +0 -34
  218. package/src/modules/entities/i18n/en.json +0 -34
  219. package/src/modules/entities/i18n/es.json +0 -34
  220. package/src/modules/entities/i18n/pl.json +0 -34
  221. package/src/modules/feature_toggles/api/check/boolean/route.ts +15 -0
  222. package/src/modules/feature_toggles/defaults.json +0 -8
  223. package/src/modules/integrations/backend/integrations/[id]/page.tsx +59 -16
  224. package/src/modules/payment_gateways/data/entities.ts +0 -39
  225. package/src/modules/payment_gateways/lib/gateway-service.ts +40 -211
  226. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +0 -236
  227. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +20 -14
  228. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +34 -16
  229. package/src/modules/query_index/api/status.ts +8 -2
  230. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +37 -27
  231. package/src/modules/sales/backend/sales/channels/create/page.meta.ts +0 -3
  232. package/src/modules/sales/backend/sales/channels/create/page.tsx +0 -7
  233. package/src/modules/sales/backend/sales/channels/offers/page.meta.ts +0 -3
  234. package/src/modules/sales/backend/sales/channels/offers/page.tsx +0 -7
  235. package/src/modules/sales/backend/sales/channels/page.meta.ts +0 -4
  236. package/src/modules/sales/backend/sales/channels/page.tsx +0 -7
  237. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +5 -7
  238. package/src/modules/sales/commands/documents.ts +0 -65
  239. package/src/modules/sales/commands/returns.ts +32 -2
  240. package/src/modules/sales/components/documents/AddressesSection.tsx +2 -6
  241. package/src/modules/sales/components/documents/ItemsSection.tsx +0 -5
  242. package/src/modules/sales/components/documents/LineItemDialog.tsx +0 -10
  243. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +3 -53
  244. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +8 -10
  245. package/src/modules/sales/components/documents/ShipmentDialog.tsx +40 -65
  246. package/src/modules/sales/i18n/de.json +0 -5
  247. package/src/modules/sales/i18n/en.json +0 -5
  248. package/src/modules/sales/i18n/es.json +0 -5
  249. package/src/modules/sales/i18n/pl.json +0 -5
  250. package/src/modules/sales/lib/shipments/snapshots.ts +0 -30
  251. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +34 -25
  252. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +17 -11
  253. package/src/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.tsx +28 -11
  254. package/src/modules/sync_excel/lib/adapters/customers.ts +22 -20
  255. package/src/modules/sync_excel/lib/parser.ts +0 -245
  256. package/src/modules/sync_excel/lib/upload-storage.ts +1 -23
  257. package/src/modules/workflows/frontend/checkout-demo/page.tsx +12 -1
  258. package/src/modules/workflows/lib/activity-executor.ts +22 -2
  259. package/src/modules/workflows/lib/find-definition.ts +53 -1
  260. package/src/modules/workflows/lib/signal-handler.ts +5 -4
  261. package/src/modules/workflows/lib/step-handler.ts +4 -8
  262. package/src/modules/workflows/lib/task-handler.ts +2 -4
  263. package/src/modules/workflows/lib/timer-handler.ts +3 -2
  264. package/src/modules/workflows/lib/transition-handler.ts +5 -13
  265. package/src/modules/workflows/lib/workflow-executor.ts +11 -8
  266. package/src/modules/workflows/migrations/Migration20260715120000.ts +114 -0
  267. package/src/modules/workflows/migrations/Migration20260716120000.ts +57 -0
  268. package/src/modules/workflows/workflows.ts +2 -0
  269. package/dist/generated/entities/gateway_session_initialization/index.js +0 -23
  270. package/dist/generated/entities/gateway_session_initialization/index.js.map +0 -7
  271. package/dist/modules/attachments/lib/reconcileOrganization.js +0 -110
  272. package/dist/modules/attachments/lib/reconcileOrganization.js.map +0 -7
  273. package/dist/modules/attachments/lib/requestScope.js +0 -10
  274. package/dist/modules/attachments/lib/requestScope.js.map +0 -7
  275. package/dist/modules/auth/api/autologin.js +0 -85
  276. package/dist/modules/auth/api/autologin.js.map +0 -7
  277. package/dist/modules/auth/lib/autologin.js +0 -19
  278. package/dist/modules/auth/lib/autologin.js.map +0 -7
  279. package/dist/modules/customers/components/detail/useDealsAccess.js +0 -16
  280. package/dist/modules/customers/components/detail/useDealsAccess.js.map +0 -7
  281. package/dist/modules/payment_gateways/lib/session-idempotency.js +0 -104
  282. package/dist/modules/payment_gateways/lib/session-idempotency.js.map +0 -7
  283. package/dist/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.js +0 -11
  284. package/dist/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.js.map +0 -7
  285. package/dist/modules/sales/components/SalesChannelsDisabledNotice.js +0 -15
  286. package/dist/modules/sales/components/SalesChannelsDisabledNotice.js.map +0 -7
  287. package/dist/modules/sales/components/useSalesChannelsEnabled.js +0 -44
  288. package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +0 -7
  289. package/dist/modules/sales/lib/salesChannelsToggle.js +0 -18
  290. package/dist/modules/sales/lib/salesChannelsToggle.js.map +0 -7
  291. package/dist/modules/sales/lib/salesChannelsToggleId.js +0 -5
  292. package/dist/modules/sales/lib/salesChannelsToggleId.js.map +0 -7
  293. package/generated/entities/gateway_session_initialization/index.ts +0 -10
  294. package/src/modules/attachments/lib/reconcileOrganization.ts +0 -188
  295. package/src/modules/attachments/lib/requestScope.ts +0 -27
  296. package/src/modules/auth/api/autologin.ts +0 -100
  297. package/src/modules/auth/lib/autologin.ts +0 -36
  298. package/src/modules/customers/components/detail/useDealsAccess.ts +0 -19
  299. package/src/modules/payment_gateways/lib/session-idempotency.ts +0 -142
  300. package/src/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.ts +0 -10
  301. package/src/modules/sales/components/SalesChannelsDisabledNotice.tsx +0 -21
  302. package/src/modules/sales/components/useSalesChannelsEnabled.ts +0 -59
  303. package/src/modules/sales/lib/salesChannelsToggle.ts +0 -26
  304. package/src/modules/sales/lib/salesChannelsToggleId.ts +0 -1
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/payment_gateways/lib/session-idempotency.ts"],
4
- "sourcesContent": ["import { createHash, randomUUID } from 'node:crypto'\nimport { UniqueConstraintViolationException } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { GatewaySessionInitialization } from '../data/entities'\n\ntype Scope = { organizationId: string; tenantId: string }\n\nexport type OwnedPaymentSessionInitialization = {\n id: string\n claimToken: string\n}\n\nexport function buildPaymentSessionOperationKey(input: {\n idempotencyKey: string\n paymentId: string\n providerKey: string\n scope: Scope\n}): string {\n const digest = createHash('sha256')\n .update(JSON.stringify([\n input.scope.tenantId,\n input.scope.organizationId,\n input.providerKey,\n input.paymentId,\n input.idempotencyKey,\n ]))\n .digest('hex')\n return `om-payment-session:${digest}`\n}\n\nexport async function findPaymentSessionInitialization(\n em: EntityManager,\n operationKey: string,\n providerKey: string,\n scope: Scope,\n): Promise<GatewaySessionInitialization | null> {\n return findOneWithDecryption(\n em.fork(),\n GatewaySessionInitialization,\n {\n operationKey,\n providerKey,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n}\n\nexport async function claimPaymentSessionInitialization(\n em: EntityManager,\n operationKey: string,\n providerKey: string,\n scope: Scope,\n claimedAt: Date,\n): Promise<OwnedPaymentSessionInitialization | null> {\n const claimEm = em.fork()\n const claimToken = randomUUID()\n const record = claimEm.create(GatewaySessionInitialization, {\n operationKey,\n providerKey,\n claimToken,\n claimedAt,\n gatewayTransactionId: null,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n try {\n await claimEm.persist(record).flush()\n return { id: record.id, claimToken }\n } catch (error: unknown) {\n if (error instanceof UniqueConstraintViolationException) return null\n throw error\n }\n}\n\nexport async function reclaimPaymentSessionInitialization(\n em: EntityManager,\n claim: GatewaySessionInitialization,\n scope: Scope,\n claimedAt: Date,\n staleBefore: Date,\n): Promise<OwnedPaymentSessionInitialization | null> {\n const claimToken = randomUUID()\n const claimedRows = await em.fork().nativeUpdate(\n GatewaySessionInitialization,\n {\n id: claim.id,\n operationKey: claim.operationKey,\n providerKey: claim.providerKey,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n gatewayTransactionId: null,\n $or: [\n { claimToken: null },\n { claimedAt: { $lt: staleBefore } },\n ],\n },\n { claimToken, claimedAt, updatedAt: claimedAt },\n )\n return claimedRows > 0 ? { id: claim.id, claimToken } : null\n}\n\nexport async function releasePaymentSessionInitialization(\n em: EntityManager,\n ownership: OwnedPaymentSessionInitialization,\n scope: Scope,\n): Promise<void> {\n await em.fork().nativeUpdate(\n GatewaySessionInitialization,\n {\n id: ownership.id,\n claimToken: ownership.claimToken,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n gatewayTransactionId: null,\n },\n { claimToken: null, claimedAt: null, updatedAt: new Date() },\n )\n}\n\nexport async function refreshPaymentSessionInitialization(\n em: EntityManager,\n ownership: OwnedPaymentSessionInitialization,\n scope: Scope,\n claimedAt: Date,\n): Promise<boolean> {\n const refreshedRows = await em.fork().nativeUpdate(\n GatewaySessionInitialization,\n {\n id: ownership.id,\n claimToken: ownership.claimToken,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n gatewayTransactionId: null,\n },\n { claimedAt, updatedAt: claimedAt },\n )\n return refreshedRows > 0\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY,kBAAkB;AACvC,SAAS,0CAA0C;AAEnD,SAAS,6BAA6B;AACtC,SAAS,oCAAoC;AAStC,SAAS,gCAAgC,OAKrC;AACT,QAAM,SAAS,WAAW,QAAQ,EAC/B,OAAO,KAAK,UAAU;AAAA,IACrB,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,IACZ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACR,CAAC,CAAC,EACD,OAAO,KAAK;AACf,SAAO,sBAAsB,MAAM;AACrC;AAEA,eAAsB,iCACpB,IACA,cACA,aACA,OAC8C;AAC9C,SAAO;AAAA,IACL,GAAG,KAAK;AAAA,IACR;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,kCACpB,IACA,cACA,aACA,OACA,WACmD;AACnD,QAAM,UAAU,GAAG,KAAK;AACxB,QAAM,aAAa,WAAW;AAC9B,QAAM,SAAS,QAAQ,OAAO,8BAA8B;AAAA,IAC1D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB,gBAAgB,MAAM;AAAA,IACtB,UAAU,MAAM;AAAA,EAClB,CAAC;AACD,MAAI;AACF,UAAM,QAAQ,QAAQ,MAAM,EAAE,MAAM;AACpC,WAAO,EAAE,IAAI,OAAO,IAAI,WAAW;AAAA,EACrC,SAAS,OAAgB;AACvB,QAAI,iBAAiB,mCAAoC,QAAO;AAChE,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,oCACpB,IACA,OACA,OACA,WACA,aACmD;AACnD,QAAM,aAAa,WAAW;AAC9B,QAAM,cAAc,MAAM,GAAG,KAAK,EAAE;AAAA,IAClC;AAAA,IACA;AAAA,MACE,IAAI,MAAM;AAAA,MACV,cAAc,MAAM;AAAA,MACpB,aAAa,MAAM;AAAA,MACnB,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,sBAAsB;AAAA,MACtB,KAAK;AAAA,QACH,EAAE,YAAY,KAAK;AAAA,QACnB,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE;AAAA,MACpC;AAAA,IACF;AAAA,IACA,EAAE,YAAY,WAAW,WAAW,UAAU;AAAA,EAChD;AACA,SAAO,cAAc,IAAI,EAAE,IAAI,MAAM,IAAI,WAAW,IAAI;AAC1D;AAEA,eAAsB,oCACpB,IACA,WACA,OACe;AACf,QAAM,GAAG,KAAK,EAAE;AAAA,IACd;AAAA,IACA;AAAA,MACE,IAAI,UAAU;AAAA,MACd,YAAY,UAAU;AAAA,MACtB,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,sBAAsB;AAAA,IACxB;AAAA,IACA,EAAE,YAAY,MAAM,WAAW,MAAM,WAAW,oBAAI,KAAK,EAAE;AAAA,EAC7D;AACF;AAEA,eAAsB,oCACpB,IACA,WACA,OACA,WACkB;AAClB,QAAM,gBAAgB,MAAM,GAAG,KAAK,EAAE;AAAA,IACpC;AAAA,IACA;AAAA,MACE,IAAI,UAAU;AAAA,MACd,YAAY,UAAU;AAAA,MACtB,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,sBAAsB;AAAA,IACxB;AAAA,IACA,EAAE,WAAW,WAAW,UAAU;AAAA,EACpC;AACA,SAAO,gBAAgB;AACzB;",
6
- "names": []
7
- }
@@ -1,11 +0,0 @@
1
- import { Migration } from "@mikro-orm/migrations";
2
- class Migration20260709220938_payment_gateways extends Migration {
3
- up() {
4
- this.addSql(`create table "gateway_session_initializations" ("id" uuid not null default gen_random_uuid(), "operation_key" text not null, "provider_key" text not null, "claim_token" uuid null, "claimed_at" timestamptz null, "gateway_transaction_id" uuid null, "organization_id" uuid not null, "tenant_id" uuid not null, "created_at" timestamptz not null, "updated_at" timestamptz not null, primary key ("id"));`);
5
- this.addSql(`alter table "gateway_session_initializations" add constraint "gateway_session_initializations_scope_operation_unique" unique ("operation_key", "provider_key", "organization_id", "tenant_id");`);
6
- }
7
- }
8
- export {
9
- Migration20260709220938_payment_gateways
10
- };
11
- //# sourceMappingURL=Migration20260709220938_payment_gateways.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.ts"],
4
- "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260709220938_payment_gateways extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`create table \"gateway_session_initializations\" (\"id\" uuid not null default gen_random_uuid(), \"operation_key\" text not null, \"provider_key\" text not null, \"claim_token\" uuid null, \"claimed_at\" timestamptz null, \"gateway_transaction_id\" uuid null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, primary key (\"id\"));`);\n this.addSql(`alter table \"gateway_session_initializations\" add constraint \"gateway_session_initializations_scope_operation_unique\" unique (\"operation_key\", \"provider_key\", \"organization_id\", \"tenant_id\");`);\n }\n\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,iDAAiD,UAAU;AAAA,EAE7D,KAA2B;AAClC,SAAK,OAAO,+YAA+Y;AAC3Z,SAAK,OAAO,iMAAiM;AAAA,EAC/M;AAEF;",
6
- "names": []
7
- }
@@ -1,15 +0,0 @@
1
- "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- import { Page, PageBody } from "@open-mercato/ui/backend/Page";
4
- import { useT } from "@open-mercato/shared/lib/i18n/context";
5
- function SalesChannelsDisabledNotice() {
6
- const t = useT();
7
- return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-muted/50 p-6 text-sm text-muted-foreground", children: t(
8
- "sales.channels.disabledNotice",
9
- 'Sales channels are disabled for this tenant. Enable the "Sales Channels" feature toggle to manage them.'
10
- ) }) }) });
11
- }
12
- export {
13
- SalesChannelsDisabledNotice
14
- };
15
- //# sourceMappingURL=SalesChannelsDisabledNotice.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/sales/components/SalesChannelsDisabledNotice.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport function SalesChannelsDisabledNotice() {\n const t = useT()\n return (\n <Page>\n <PageBody>\n <div className=\"rounded-lg border bg-muted/50 p-6 text-sm text-muted-foreground\">\n {t(\n 'sales.channels.disabledNotice',\n 'Sales channels are disabled for this tenant. Enable the \"Sales Channels\" feature toggle to manage them.',\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": ";AAWQ;AARR,SAAS,MAAM,gBAAgB;AAC/B,SAAS,YAAY;AAEd,SAAS,8BAA8B;AAC5C,QAAM,IAAI,KAAK;AACf,SACE,oBAAC,QACC,8BAAC,YACC,8BAAC,SAAI,WAAU,mEACZ;AAAA,IACC;AAAA,IACA;AAAA,EACF,GACF,GACF,GACF;AAEJ;",
6
- "names": []
7
- }
@@ -1,44 +0,0 @@
1
- "use client";
2
- import * as React from "react";
3
- import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
4
- import { SALES_CHANNELS_TOGGLE_ID } from "../lib/salesChannelsToggleId.js";
5
- const CACHE_TTL_MS = 6e4;
6
- async function fetchSalesChannelsEnabled() {
7
- try {
8
- const call = await apiCall(
9
- `/api/feature_toggles/check/boolean?identifier=${SALES_CHANNELS_TOGGLE_ID}`
10
- );
11
- if (!call.ok) return true;
12
- return !(call.result?.ok === true && call.result.value === false);
13
- } catch {
14
- return true;
15
- }
16
- }
17
- let cache = null;
18
- function getSalesChannelsEnabled() {
19
- const now = Date.now();
20
- if (!cache || now - cache.at > CACHE_TTL_MS) {
21
- cache = { value: fetchSalesChannelsEnabled(), at: now };
22
- }
23
- return cache.value;
24
- }
25
- function useSalesChannelsEnabled() {
26
- const [resolved, setResolved] = React.useState(null);
27
- React.useEffect(() => {
28
- let active = true;
29
- getSalesChannelsEnabled().then((value) => {
30
- if (active) setResolved(value);
31
- }).catch(() => {
32
- if (active) setResolved(true);
33
- });
34
- return () => {
35
- active = false;
36
- };
37
- }, []);
38
- return { enabled: resolved !== false, isLoading: resolved === null };
39
- }
40
- export {
41
- SALES_CHANNELS_TOGGLE_ID,
42
- useSalesChannelsEnabled
43
- };
44
- //# sourceMappingURL=useSalesChannelsEnabled.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/sales/components/useSalesChannelsEnabled.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { SALES_CHANNELS_TOGGLE_ID } from '../lib/salesChannelsToggleId'\n\nexport { SALES_CHANNELS_TOGGLE_ID }\n\nconst CACHE_TTL_MS = 60_000\n\ntype FeatureToggleCheckResponse = {\n ok?: boolean\n value?: unknown\n}\n\n// Fails open: channels stay visible unless the toggle explicitly resolves to\n// false. A missing toggle definition (404) or an absent feature_toggles module\n// must not hide sales channel UI.\nasync function fetchSalesChannelsEnabled(): Promise<boolean> {\n try {\n const call = await apiCall<FeatureToggleCheckResponse>(\n `/api/feature_toggles/check/boolean?identifier=${SALES_CHANNELS_TOGGLE_ID}`,\n )\n if (!call.ok) return true\n return !(call.result?.ok === true && call.result.value === false)\n } catch {\n return true\n }\n}\n\nlet cache: { value: Promise<boolean>; at: number } | null = null\n\nfunction getSalesChannelsEnabled(): Promise<boolean> {\n const now = Date.now()\n if (!cache || now - cache.at > CACHE_TTL_MS) {\n cache = { value: fetchSalesChannelsEnabled(), at: now }\n }\n return cache.value\n}\n\nexport function useSalesChannelsEnabled(): { enabled: boolean; isLoading: boolean } {\n const [resolved, setResolved] = React.useState<boolean | null>(null)\n\n React.useEffect(() => {\n let active = true\n getSalesChannelsEnabled()\n .then((value) => {\n if (active) setResolved(value)\n })\n .catch(() => {\n if (active) setResolved(true)\n })\n return () => {\n active = false\n }\n }, [])\n\n return { enabled: resolved !== false, isLoading: resolved === null }\n}\n"],
5
- "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,eAAe;AACxB,SAAS,gCAAgC;AAIzC,MAAM,eAAe;AAUrB,eAAe,4BAA8C;AAC3D,MAAI;AACF,UAAM,OAAO,MAAM;AAAA,MACjB,iDAAiD,wBAAwB;AAAA,IAC3E;AACA,QAAI,CAAC,KAAK,GAAI,QAAO;AACrB,WAAO,EAAE,KAAK,QAAQ,OAAO,QAAQ,KAAK,OAAO,UAAU;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAI,QAAwD;AAE5D,SAAS,0BAA4C;AACnD,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,CAAC,SAAS,MAAM,MAAM,KAAK,cAAc;AAC3C,YAAQ,EAAE,OAAO,0BAA0B,GAAG,IAAI,IAAI;AAAA,EACxD;AACA,SAAO,MAAM;AACf;AAEO,SAAS,0BAAoE;AAClF,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAyB,IAAI;AAEnE,QAAM,UAAU,MAAM;AACpB,QAAI,SAAS;AACb,4BAAwB,EACrB,KAAK,CAAC,UAAU;AACf,UAAI,OAAQ,aAAY,KAAK;AAAA,IAC/B,CAAC,EACA,MAAM,MAAM;AACX,UAAI,OAAQ,aAAY,IAAI;AAAA,IAC9B,CAAC;AACH,WAAO,MAAM;AACX,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,SAAS,aAAa,OAAO,WAAW,aAAa,KAAK;AACrE;",
6
- "names": []
7
- }
@@ -1,18 +0,0 @@
1
- import { SALES_CHANNELS_TOGGLE_ID } from "./salesChannelsToggleId.js";
2
- async function isSalesChannelsEnabledForTenant(tenantId) {
3
- if (!tenantId) return true;
4
- try {
5
- const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
6
- const container = await createRequestContainer();
7
- const service = container.resolve("featureTogglesService");
8
- const result = await service.getBoolConfig(SALES_CHANNELS_TOGGLE_ID, tenantId);
9
- return !(result.ok === true && result.value === false);
10
- } catch {
11
- return true;
12
- }
13
- }
14
- export {
15
- SALES_CHANNELS_TOGGLE_ID,
16
- isSalesChannelsEnabledForTenant
17
- };
18
- //# sourceMappingURL=salesChannelsToggle.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/sales/lib/salesChannelsToggle.ts"],
4
- "sourcesContent": ["import { SALES_CHANNELS_TOGGLE_ID } from './salesChannelsToggleId'\n\nexport { SALES_CHANNELS_TOGGLE_ID }\n\ntype BoolConfigResult = { ok: boolean; value?: unknown }\n\ntype FeatureTogglesServiceLike = {\n getBoolConfig: (identifier: string, tenantId: string) => Promise<BoolConfigResult>\n}\n\n// Server-side counterpart of useSalesChannelsEnabled with the same fail-open\n// contract: channels stay enabled unless the toggle explicitly resolves to\n// false. The DI container and feature_toggles service are optional peers, so\n// they are loaded lazily and any failure keeps channels visible.\nexport async function isSalesChannelsEnabledForTenant(tenantId: string | null | undefined): Promise<boolean> {\n if (!tenantId) return true\n try {\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const service = container.resolve('featureTogglesService') as FeatureTogglesServiceLike\n const result = await service.getBoolConfig(SALES_CHANNELS_TOGGLE_ID, tenantId)\n return !(result.ok === true && result.value === false)\n } catch {\n return true\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,gCAAgC;AAczC,eAAsB,gCAAgC,UAAuD;AAC3G,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI;AACF,UAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,uCAAuC;AACvF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,UAAU,UAAU,QAAQ,uBAAuB;AACzD,UAAM,SAAS,MAAM,QAAQ,cAAc,0BAA0B,QAAQ;AAC7E,WAAO,EAAE,OAAO,OAAO,QAAQ,OAAO,UAAU;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
- "names": []
7
- }
@@ -1,5 +0,0 @@
1
- const SALES_CHANNELS_TOGGLE_ID = "sales_channels_enabled";
2
- export {
3
- SALES_CHANNELS_TOGGLE_ID
4
- };
5
- //# sourceMappingURL=salesChannelsToggleId.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/sales/lib/salesChannelsToggleId.ts"],
4
- "sourcesContent": ["export const SALES_CHANNELS_TOGGLE_ID = 'sales_channels_enabled'\n"],
5
- "mappings": "AAAO,MAAM,2BAA2B;",
6
- "names": []
7
- }
@@ -1,10 +0,0 @@
1
- export const id = "id";
2
- export const operation_key = "operation_key";
3
- export const provider_key = "provider_key";
4
- export const claim_token = "claim_token";
5
- export const claimed_at = "claimed_at";
6
- export const gateway_transaction_id = "gateway_transaction_id";
7
- export const organization_id = "organization_id";
8
- export const tenant_id = "tenant_id";
9
- export const created_at = "created_at";
10
- export const updated_at = "updated_at";
@@ -1,188 +0,0 @@
1
- import type { EntityManager } from '@mikro-orm/postgresql'
2
- import { createLogger } from '@open-mercato/shared/lib/logger'
3
- import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
4
- import { Attachment } from '../data/entities'
5
-
6
- const logger = createLogger('attachments').child({ component: 'reconcileOrganization' })
7
-
8
- /**
9
- * Virtual entity id for library attachments — these are not attached to a
10
- * parent record that owns an organization, so there is nothing to reconcile
11
- * against. They are counted as skipped rather than unresolved.
12
- */
13
- const LIBRARY_ENTITY_ID = 'attachments:library'
14
-
15
- const DEFAULT_BATCH_SIZE = 500
16
-
17
- export type AttachmentOrgReconcileEntityStat = {
18
- scanned: number
19
- updated: number
20
- unresolved: number
21
- }
22
-
23
- export type AttachmentOrgReconcileReport = {
24
- scanned: number
25
- updated: number
26
- unresolved: number
27
- skippedVirtual: number
28
- byEntity: Record<string, AttachmentOrgReconcileEntityStat>
29
- }
30
-
31
- type AttachmentScanRow = {
32
- id: string
33
- entity_id: string | null
34
- record_id: string | null
35
- organization_id: string | null
36
- }
37
-
38
- function readParentOrganizationId(record: Record<string, unknown>): string | null {
39
- const raw = record['organization_id'] ?? record['organizationId']
40
- if (typeof raw === 'string' && raw.trim().length) return raw.trim()
41
- return null
42
- }
43
-
44
- function ensureBucket(
45
- report: AttachmentOrgReconcileReport,
46
- entityId: string,
47
- ): AttachmentOrgReconcileEntityStat {
48
- const existing = report.byEntity[entityId]
49
- if (existing) return existing
50
- const bucket: AttachmentOrgReconcileEntityStat = { scanned: 0, updated: 0, unresolved: 0 }
51
- report.byEntity[entityId] = bucket
52
- return bucket
53
- }
54
-
55
- /**
56
- * Reconcile the `organization_id` of existing attachments to the organization
57
- * of the record they are attached to.
58
- *
59
- * Background (#3765): before the upload route became selected-organization
60
- * aware, a multi-org admin who switched the header organization uploaded files
61
- * that were silently stored under their *home* organization instead of the
62
- * selected one (the organization of the parent record). Those rows are now
63
- * invisible to org-scoped reads and stay orphaned. This heals them forward.
64
- *
65
- * The parent record's organization is the ground truth: an attachment is not
66
- * distinguishable from a legitimately home-org one by looking at the attachment
67
- * alone. Parent organizations are resolved generically through the Query Engine
68
- * by `entityId` (works for both base and custom entities, tenant-scoped and
69
- * organization-agnostic so the parent's real org is returned regardless of
70
- * which org the caller sits in) — the same mechanism the attachments module
71
- * already uses to enrich assignment details.
72
- *
73
- * The reconciliation is:
74
- * - idempotent — rows already matching their parent are left untouched;
75
- * - conservative — when the parent org cannot be resolved (unregistered
76
- * entity id, hard-deleted parent, parent without an org column) the row is
77
- * counted as `unresolved` and left as-is rather than guessed at;
78
- * - tenant-scoped — it reconciles every attachment in the tenant across all
79
- * organizations (the whole point is moving rows between orgs).
80
- *
81
- * Writes go through the passed `EntityManager` (which the caller runs inside a
82
- * transaction) so the whole reconciliation commits atomically.
83
- */
84
- export async function reconcileAttachmentOrganizations(opts: {
85
- em: EntityManager
86
- queryEngine: QueryEngine
87
- tenantId: string
88
- batchSize?: number
89
- }): Promise<AttachmentOrgReconcileReport> {
90
- const { em, queryEngine, tenantId } = opts
91
- const batchSize = opts.batchSize && opts.batchSize > 0 ? opts.batchSize : DEFAULT_BATCH_SIZE
92
- const report: AttachmentOrgReconcileReport = {
93
- scanned: 0,
94
- updated: 0,
95
- unresolved: 0,
96
- skippedVirtual: 0,
97
- byEntity: {},
98
- }
99
-
100
- const rows = (await em.getConnection().execute(
101
- 'select id, entity_id, record_id, organization_id from attachments where tenant_id = ?',
102
- [tenantId],
103
- )) as AttachmentScanRow[]
104
- report.scanned = rows.length
105
- if (!rows.length) return report
106
-
107
- const groups = new Map<string, AttachmentScanRow[]>()
108
- for (const row of rows) {
109
- if (!row.id || !row.entity_id || !row.record_id) continue
110
- const list = groups.get(row.entity_id) ?? []
111
- list.push(row)
112
- groups.set(row.entity_id, list)
113
- }
114
-
115
- const pendingUpdates: Array<{ id: string; organizationId: string }> = []
116
-
117
- for (const [entityId, group] of groups) {
118
- const bucket = ensureBucket(report, entityId)
119
- bucket.scanned += group.length
120
-
121
- if (entityId === LIBRARY_ENTITY_ID) {
122
- report.skippedVirtual += group.length
123
- continue
124
- }
125
-
126
- const recordIds = Array.from(
127
- new Set(group.map((row) => row.record_id).filter((value): value is string => !!value)),
128
- )
129
- const parentOrgByRecordId = new Map<string, string | null>()
130
- let resolvable = true
131
-
132
- for (let index = 0; index < recordIds.length; index += batchSize) {
133
- const chunk = recordIds.slice(index, index + batchSize)
134
- try {
135
- const result = await queryEngine.query(entityId as any, {
136
- fields: ['id', 'organization_id'],
137
- filters: { id: chunk.length === 1 ? { $eq: chunk[0] } : { $in: chunk } },
138
- tenantId,
139
- withDeleted: true,
140
- page: { pageSize: Math.max(chunk.length, 1) },
141
- })
142
- for (const item of result.items ?? []) {
143
- const record = item as Record<string, unknown>
144
- const recordId = record.id != null ? String(record.id) : null
145
- if (!recordId) continue
146
- parentOrgByRecordId.set(recordId, readParentOrganizationId(record))
147
- }
148
- } catch (error) {
149
- resolvable = false
150
- logger.warn('org reconcile: cannot resolve parent organization for entity', {
151
- entityId,
152
- error,
153
- })
154
- break
155
- }
156
- }
157
-
158
- if (!resolvable) {
159
- bucket.unresolved += group.length
160
- report.unresolved += group.length
161
- continue
162
- }
163
-
164
- for (const row of group) {
165
- const target = row.record_id ? parentOrgByRecordId.get(row.record_id) : null
166
- if (!target) {
167
- bucket.unresolved += 1
168
- report.unresolved += 1
169
- continue
170
- }
171
- if ((row.organization_id ?? null) === target) continue
172
- pendingUpdates.push({ id: row.id, organizationId: target })
173
- bucket.updated += 1
174
- report.updated += 1
175
- }
176
- }
177
-
178
- for (let index = 0; index < pendingUpdates.length; index += batchSize) {
179
- const chunk = pendingUpdates.slice(index, index + batchSize)
180
- for (const update of chunk) {
181
- const reference = em.getReference(Attachment, update.id)
182
- reference.organizationId = update.organizationId
183
- }
184
- await em.flush()
185
- }
186
-
187
- return report
188
- }
@@ -1,27 +0,0 @@
1
- import type { AwilixContainer } from 'awilix'
2
- import type { AuthContext } from '@open-mercato/shared/lib/auth/server'
3
- import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
4
-
5
- /**
6
- * Resolve the organization an attachment request should act within.
7
- *
8
- * `auth.orgId` alone is NOT selected-organization aware for non-superadmin
9
- * principals: `applySuperAdminScope` only rewrites `orgId` from the
10
- * `om_selected_org` cookie for superadmins, so a regular multi-org admin who
11
- * switches the header organization keeps `auth.orgId` pinned to their own home
12
- * organization. The CRUD factory and other org-scoped routes derive the active
13
- * organization via `resolveOrganizationScopeForRequest` (cookie-driven and
14
- * RBAC-validated for ALL users, falling back to the home org when the selection
15
- * is absent or inaccessible). Attachments must do the same so uploaded files
16
- * land under — and are read back from — the currently selected organization
17
- * rather than the uploader's home organization (#3765).
18
- */
19
- export async function resolveAttachmentOrganizationId(
20
- container: AwilixContainer,
21
- auth: AuthContext,
22
- request: Request,
23
- ): Promise<string | null> {
24
- if (!auth) return null
25
- const scope = await resolveOrganizationScopeForRequest({ container, auth, request })
26
- return scope?.selectedId ?? auth.orgId ?? null
27
- }
@@ -1,100 +0,0 @@
1
- import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
2
- import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
- import { createLogger } from '@open-mercato/shared/lib/logger'
4
- import { AuthService } from '@open-mercato/core/modules/auth/services/authService'
5
- import { signJwt } from '@open-mercato/shared/lib/auth/jwt'
6
- import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
7
- import { buildSafeRedirectResponse, resolveTrustedRedirectBase } from '@open-mercato/core/modules/auth/lib/requestRedirect'
8
- import { sanitizeRedirectPath } from '@open-mercato/core/modules/auth/lib/safeRedirect'
9
- import { resolveAutoLoginCredentials } from '@open-mercato/core/modules/auth/lib/autologin'
10
- import { emitAuthEvent } from '@open-mercato/core/modules/auth/events'
11
-
12
- export const metadata = { requireAuth: false }
13
-
14
- const accessTokenMaxAgeSeconds = 60 * 60 * 8
15
- const logger = createLogger('auth').child({ component: 'autologin' })
16
-
17
- // Demo-only convenience: when OM_AUTOLOGIN_* credentials are configured, sign the
18
- // visitor in with those credentials and redirect into the app. Gated entirely
19
- // behind env vars — the homepage only routes here when they are set. On any
20
- // failure (feature off, bad credentials) it falls back to the manual login form
21
- // so a misconfigured demo can never loop.
22
- export async function GET(req: Request) {
23
- const url = new URL(req.url)
24
- const baseUrl = resolveTrustedRedirectBase(req) ?? url.origin
25
- const redirectTo = sanitizeRedirectPath(url.searchParams.get('redirect'), baseUrl, '/backend')
26
-
27
- const credentials = resolveAutoLoginCredentials()
28
- if (!credentials) {
29
- return buildSafeRedirectResponse(req, '/login')
30
- }
31
-
32
- // Already signed in — go straight to the app instead of re-issuing a session.
33
- const existing = await getAuthFromRequest(req)
34
- if (existing) {
35
- return buildSafeRedirectResponse(req, redirectTo)
36
- }
37
-
38
- const container = await createRequestContainer()
39
- const auth = container.resolve<AuthService>('authService')
40
-
41
- let user = null
42
- if (credentials.tenantId) {
43
- user = await auth.findUserByEmailAndTenant(credentials.email, credentials.tenantId)
44
- } else {
45
- const users = await auth.findUsersByEmail(credentials.email)
46
- user = users.length === 1 ? users[0] : null
47
- }
48
-
49
- const ok = await auth.verifyPassword(user, credentials.password)
50
- if (!user || !ok) {
51
- // Misconfigured demo credentials — never loop; drop to the manual form.
52
- logger.warn('OM_AUTOLOGIN credentials did not resolve to a single valid user; falling back to login')
53
- return buildSafeRedirectResponse(req, '/login')
54
- }
55
-
56
- const resolvedTenantId = credentials.tenantId ?? (user.tenantId ? String(user.tenantId) : null)
57
- const roles = await auth.getUserRoles(user, resolvedTenantId)
58
- await auth.updateLastLoginAt(user)
59
- const expiresAt = new Date(Date.now() + accessTokenMaxAgeSeconds * 1000)
60
- const { session } = await auth.createSession(user, expiresAt)
61
- const token = signJwt({
62
- sub: String(user.id),
63
- sid: String(session.id),
64
- tenantId: resolvedTenantId,
65
- orgId: user.organizationId ? String(user.organizationId) : null,
66
- email: user.email,
67
- roles,
68
- })
69
- void emitAuthEvent('auth.login.success', {
70
- id: String(user.id),
71
- email: user.email,
72
- tenantId: resolvedTenantId,
73
- organizationId: user.organizationId ? String(user.organizationId) : null,
74
- }).catch(() => undefined)
75
-
76
- const res = buildSafeRedirectResponse(req, redirectTo)
77
- res.cookies.set('auth_token', token, {
78
- httpOnly: true,
79
- path: '/',
80
- sameSite: 'lax',
81
- secure: process.env.NODE_ENV === 'production',
82
- maxAge: accessTokenMaxAgeSeconds,
83
- })
84
- return res
85
- }
86
-
87
- export const openApi: OpenApiRouteDoc = {
88
- tag: 'Authentication & Accounts',
89
- summary: 'Demo autologin',
90
- methods: {
91
- GET: {
92
- summary: 'Auto sign-in using env-configured demo credentials',
93
- description:
94
- 'When OM_AUTOLOGIN_EMAIL / OM_AUTOLOGIN_PASSWORD are configured, signs the visitor in with those credentials and redirects into the app. Intended for single-tenant demo instances only. Falls back to the login page when disabled or misconfigured.',
95
- responses: [
96
- { status: 307, description: 'Redirect into the app (or to /login on failure)', mediaType: 'text/html' },
97
- ],
98
- },
99
- },
100
- }
@@ -1,36 +0,0 @@
1
- export type AutoLoginCredentials = {
2
- email: string
3
- password: string
4
- tenantId: string | null
5
- }
6
-
7
- function readEnvValue(env: Record<string, string | undefined>, key: string): string {
8
- const value = env[key]
9
- return typeof value === 'string' ? value.trim() : ''
10
- }
11
-
12
- /**
13
- * Reads the optional demo-autologin credentials from env. When both
14
- * `OM_AUTOLOGIN_EMAIL` and `OM_AUTOLOGIN_PASSWORD` are set, the homepage hands
15
- * unauthenticated visitors to the autologin route which signs them in and drops
16
- * them into the app. Unset (the default) → `null` → normal login flow.
17
- *
18
- * `OM_AUTOLOGIN_TENANT` is optional and only needed when the same email exists
19
- * across multiple tenants. Pure: pass the env bag so it stays testable.
20
- */
21
- export function resolveAutoLoginCredentials(
22
- env: Record<string, string | undefined> = process.env,
23
- ): AutoLoginCredentials | null {
24
- const email = readEnvValue(env, 'OM_AUTOLOGIN_EMAIL')
25
- const password = env.OM_AUTOLOGIN_PASSWORD ?? ''
26
- if (!email || !password) return null
27
- const tenantId = readEnvValue(env, 'OM_AUTOLOGIN_TENANT')
28
- return { email, password, tenantId: tenantId || null }
29
- }
30
-
31
- /** True when demo autologin credentials are configured via env. */
32
- export function isAutoLoginEnabled(
33
- env: Record<string, string | undefined> = process.env,
34
- ): boolean {
35
- return resolveAutoLoginCredentials(env) !== null
36
- }
@@ -1,19 +0,0 @@
1
- "use client"
2
-
3
- import * as React from 'react'
4
- import { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'
5
- import { hasFeature } from '@open-mercato/shared/security/features'
6
-
7
- export type DealsAccess = {
8
- canViewDeals: boolean
9
- isReady: boolean
10
- }
11
-
12
- export function useDealsAccess(): DealsAccess {
13
- const { payload, isReady } = useBackendChrome()
14
- const canViewDeals = React.useMemo(
15
- () => hasFeature(payload?.grantedFeatures ?? [], 'customers.deals.view'),
16
- [payload?.grantedFeatures],
17
- )
18
- return { canViewDeals, isReady }
19
- }