@open-mercato/core 0.4.6-develop-6d72ec5960 → 0.4.6-develop-cd1e2a9a0e

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 (226) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/generated/entities/integration_credentials/index.js +19 -0
  3. package/dist/generated/entities/integration_credentials/index.js.map +7 -0
  4. package/dist/generated/entities/integration_log/index.js +27 -0
  5. package/dist/generated/entities/integration_log/index.js.map +7 -0
  6. package/dist/generated/entities/integration_state/index.js +27 -0
  7. package/dist/generated/entities/integration_state/index.js.map +7 -0
  8. package/dist/generated/entities/sync_cursor/index.js +19 -0
  9. package/dist/generated/entities/sync_cursor/index.js.map +7 -0
  10. package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
  11. package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
  12. package/dist/generated/entities/sync_mapping/index.js +19 -0
  13. package/dist/generated/entities/sync_mapping/index.js.map +7 -0
  14. package/dist/generated/entities/sync_run/index.js +45 -0
  15. package/dist/generated/entities/sync_run/index.js.map +7 -0
  16. package/dist/generated/entities/sync_schedule/index.js +35 -0
  17. package/dist/generated/entities/sync_schedule/index.js.map +7 -0
  18. package/dist/generated/entities.ids.generated.js +14 -0
  19. package/dist/generated/entities.ids.generated.js.map +2 -2
  20. package/dist/generated/entity-fields-registry.js +16 -0
  21. package/dist/generated/entity-fields-registry.js.map +2 -2
  22. package/dist/modules/data_sync/acl.js +11 -0
  23. package/dist/modules/data_sync/acl.js.map +7 -0
  24. package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
  25. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
  26. package/dist/modules/data_sync/api/mappings/route.js +132 -0
  27. package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
  28. package/dist/modules/data_sync/api/run.js +87 -0
  29. package/dist/modules/data_sync/api/run.js.map +7 -0
  30. package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
  31. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
  32. package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
  33. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
  34. package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
  35. package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
  36. package/dist/modules/data_sync/api/runs.js +66 -0
  37. package/dist/modules/data_sync/api/runs.js.map +7 -0
  38. package/dist/modules/data_sync/api/validate.js +66 -0
  39. package/dist/modules/data_sync/api/validate.js.map +7 -0
  40. package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
  41. package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
  42. package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
  43. package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
  44. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
  45. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
  46. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
  47. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
  48. package/dist/modules/data_sync/data/entities.js +228 -0
  49. package/dist/modules/data_sync/data/entities.js.map +7 -0
  50. package/dist/modules/data_sync/data/validators.js +32 -0
  51. package/dist/modules/data_sync/data/validators.js.map +7 -0
  52. package/dist/modules/data_sync/di.js +26 -0
  53. package/dist/modules/data_sync/di.js.map +7 -0
  54. package/dist/modules/data_sync/events.js +16 -0
  55. package/dist/modules/data_sync/events.js.map +7 -0
  56. package/dist/modules/data_sync/index.js +9 -0
  57. package/dist/modules/data_sync/index.js.map +7 -0
  58. package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
  59. package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
  60. package/dist/modules/data_sync/lib/adapter.js +1 -0
  61. package/dist/modules/data_sync/lib/adapter.js.map +7 -0
  62. package/dist/modules/data_sync/lib/id-mapping.js +79 -0
  63. package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
  64. package/dist/modules/data_sync/lib/queue.js +17 -0
  65. package/dist/modules/data_sync/lib/queue.js.map +7 -0
  66. package/dist/modules/data_sync/lib/sync-engine.js +309 -0
  67. package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
  68. package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
  69. package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
  70. package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
  71. package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
  72. package/dist/modules/data_sync/setup.js +13 -0
  73. package/dist/modules/data_sync/setup.js.map +7 -0
  74. package/dist/modules/data_sync/workers/sync-export.js +14 -0
  75. package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
  76. package/dist/modules/data_sync/workers/sync-import.js +14 -0
  77. package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
  78. package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
  79. package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
  80. package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
  81. package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
  82. package/dist/modules/integrations/acl.js +4 -1
  83. package/dist/modules/integrations/acl.js.map +2 -2
  84. package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
  85. package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
  86. package/dist/modules/integrations/api/[id]/health/route.js +46 -0
  87. package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
  88. package/dist/modules/integrations/api/[id]/route.js +65 -0
  89. package/dist/modules/integrations/api/[id]/route.js.map +7 -0
  90. package/dist/modules/integrations/api/[id]/state/route.js +109 -0
  91. package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
  92. package/dist/modules/integrations/api/[id]/version/route.js +117 -0
  93. package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
  94. package/dist/modules/integrations/api/guards.js +31 -0
  95. package/dist/modules/integrations/api/guards.js.map +7 -0
  96. package/dist/modules/integrations/api/logs/route.js +60 -0
  97. package/dist/modules/integrations/api/logs/route.js.map +7 -0
  98. package/dist/modules/integrations/api/openapi.js +25 -0
  99. package/dist/modules/integrations/api/openapi.js.map +7 -0
  100. package/dist/modules/integrations/api/route.js +68 -0
  101. package/dist/modules/integrations/api/route.js.map +7 -0
  102. package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
  103. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
  104. package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
  105. package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
  106. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
  107. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
  108. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
  109. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
  110. package/dist/modules/integrations/backend/integrations/page.js +212 -0
  111. package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
  112. package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
  113. package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
  114. package/dist/modules/integrations/data/enrichers.js +27 -12
  115. package/dist/modules/integrations/data/enrichers.js.map +2 -2
  116. package/dist/modules/integrations/data/entities.js +136 -1
  117. package/dist/modules/integrations/data/entities.js.map +2 -2
  118. package/dist/modules/integrations/data/validators.js +36 -0
  119. package/dist/modules/integrations/data/validators.js.map +7 -0
  120. package/dist/modules/integrations/di.js +24 -0
  121. package/dist/modules/integrations/di.js.map +7 -0
  122. package/dist/modules/integrations/events.js +19 -0
  123. package/dist/modules/integrations/events.js.map +7 -0
  124. package/dist/modules/integrations/lib/credentials-service.js +159 -0
  125. package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
  126. package/dist/modules/integrations/lib/health-service.js +37 -0
  127. package/dist/modules/integrations/lib/health-service.js.map +7 -0
  128. package/dist/modules/integrations/lib/log-service.js +66 -0
  129. package/dist/modules/integrations/lib/log-service.js.map +7 -0
  130. package/dist/modules/integrations/lib/registry-service.js +33 -0
  131. package/dist/modules/integrations/lib/registry-service.js.map +7 -0
  132. package/dist/modules/integrations/lib/state-service.js +55 -0
  133. package/dist/modules/integrations/lib/state-service.js.map +7 -0
  134. package/dist/modules/integrations/lib/types.js +1 -0
  135. package/dist/modules/integrations/lib/types.js.map +7 -0
  136. package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
  137. package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
  138. package/dist/modules/integrations/setup.js +2 -2
  139. package/dist/modules/integrations/setup.js.map +2 -2
  140. package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
  141. package/dist/modules/integrations/workers/log-pruner.js +18 -0
  142. package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
  143. package/generated/entities/integration_credentials/index.ts +8 -0
  144. package/generated/entities/integration_log/index.ts +12 -0
  145. package/generated/entities/integration_state/index.ts +12 -0
  146. package/generated/entities/sync_cursor/index.ts +8 -0
  147. package/generated/entities/sync_external_id_mapping/index.ts +12 -0
  148. package/generated/entities/sync_mapping/index.ts +8 -0
  149. package/generated/entities/sync_run/index.ts +21 -0
  150. package/generated/entities/sync_schedule/index.ts +16 -0
  151. package/generated/entities.ids.generated.ts +14 -0
  152. package/generated/entity-fields-registry.ts +16 -0
  153. package/package.json +2 -2
  154. package/src/modules/data_sync/AGENTS.md +157 -0
  155. package/src/modules/data_sync/acl.ts +7 -0
  156. package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
  157. package/src/modules/data_sync/api/mappings/route.ts +144 -0
  158. package/src/modules/data_sync/api/run.ts +97 -0
  159. package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
  160. package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
  161. package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
  162. package/src/modules/data_sync/api/runs.ts +69 -0
  163. package/src/modules/data_sync/api/validate.ts +73 -0
  164. package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
  165. package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
  166. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
  167. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
  168. package/src/modules/data_sync/data/entities.ts +180 -0
  169. package/src/modules/data_sync/data/validators.ts +35 -0
  170. package/src/modules/data_sync/di.ts +38 -0
  171. package/src/modules/data_sync/events.ts +12 -0
  172. package/src/modules/data_sync/i18n/de.json +48 -0
  173. package/src/modules/data_sync/i18n/en.json +48 -0
  174. package/src/modules/data_sync/i18n/es.json +48 -0
  175. package/src/modules/data_sync/i18n/pl.json +48 -0
  176. package/src/modules/data_sync/index.ts +5 -0
  177. package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
  178. package/src/modules/data_sync/lib/adapter.ts +90 -0
  179. package/src/modules/data_sync/lib/id-mapping.ts +95 -0
  180. package/src/modules/data_sync/lib/queue.ts +19 -0
  181. package/src/modules/data_sync/lib/sync-engine.ts +375 -0
  182. package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
  183. package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
  184. package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
  185. package/src/modules/data_sync/setup.ts +11 -0
  186. package/src/modules/data_sync/workers/sync-export.ts +27 -0
  187. package/src/modules/data_sync/workers/sync-import.ts +27 -0
  188. package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
  189. package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
  190. package/src/modules/integrations/AGENTS.md +160 -0
  191. package/src/modules/integrations/acl.ts +3 -0
  192. package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
  193. package/src/modules/integrations/api/[id]/health/route.ts +53 -0
  194. package/src/modules/integrations/api/[id]/route.ts +76 -0
  195. package/src/modules/integrations/api/[id]/state/route.ts +121 -0
  196. package/src/modules/integrations/api/[id]/version/route.ts +132 -0
  197. package/src/modules/integrations/api/guards.ts +59 -0
  198. package/src/modules/integrations/api/logs/route.ts +63 -0
  199. package/src/modules/integrations/api/openapi.ts +22 -0
  200. package/src/modules/integrations/api/route.ts +73 -0
  201. package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
  202. package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
  203. package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
  204. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
  205. package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
  206. package/src/modules/integrations/backend/integrations/page.tsx +296 -0
  207. package/src/modules/integrations/data/enrichers.ts +35 -18
  208. package/src/modules/integrations/data/entities.ts +114 -5
  209. package/src/modules/integrations/data/validators.ts +41 -0
  210. package/src/modules/integrations/di.ts +31 -0
  211. package/src/modules/integrations/events.ts +17 -0
  212. package/src/modules/integrations/i18n/de.json +70 -0
  213. package/src/modules/integrations/i18n/en.json +70 -0
  214. package/src/modules/integrations/i18n/es.json +70 -0
  215. package/src/modules/integrations/i18n/pl.json +70 -0
  216. package/src/modules/integrations/lib/credentials-service.ts +204 -0
  217. package/src/modules/integrations/lib/health-service.ts +59 -0
  218. package/src/modules/integrations/lib/log-service.ts +84 -0
  219. package/src/modules/integrations/lib/registry-service.ts +42 -0
  220. package/src/modules/integrations/lib/state-service.ts +64 -0
  221. package/src/modules/integrations/lib/types.ts +4 -0
  222. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
  223. package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
  224. package/src/modules/integrations/setup.ts +2 -2
  225. package/src/modules/integrations/widgets/injection-table.ts +1 -1
  226. package/src/modules/integrations/workers/log-pruner.ts +30 -0
@@ -0,0 +1,70 @@
1
+ {
2
+ "integrations.bundle.configureIntegration": "Konfigurieren",
3
+ "integrations.bundle.integrationToggles": "Integrationen",
4
+ "integrations.bundle.sharedCredentials": "Gemeinsame Zugangsdaten",
5
+ "integrations.bundle.title": "Paket",
6
+ "integrations.detail.back": "Zur\u00fcck zu Integrationen",
7
+ "integrations.detail.credentials.bundleShared": "Gemeinsame Zugangsdaten aus Paket: {bundle}",
8
+ "integrations.detail.credentials.notConfigured": "Noch keine Zugangsdaten konfiguriert",
9
+ "integrations.detail.credentials.save": "Zugangsdaten speichern",
10
+ "integrations.detail.credentials.saveError": "Zugangsdaten konnten nicht gespeichert werden",
11
+ "integrations.detail.credentials.saved": "Zugangsdaten gespeichert",
12
+ "integrations.detail.disable": "Deaktivieren",
13
+ "integrations.detail.enable": "Aktivieren",
14
+ "integrations.detail.health.check": "Pr\u00fcfung ausf\u00fchren",
15
+ "integrations.detail.health.checkError": "Statuspr\u00fcfung fehlgeschlagen",
16
+ "integrations.detail.health.checking": "Pr\u00fcfe\u2026",
17
+ "integrations.detail.health.degraded": "Beeintr\u00e4chtigt",
18
+ "integrations.detail.health.healthy": "Gesund",
19
+ "integrations.detail.health.lastChecked": "Zuletzt gepr\u00fcft: {date}",
20
+ "integrations.detail.health.neverChecked": "Nie gepr\u00fcft",
21
+ "integrations.detail.health.title": "Gesundheitsstatus",
22
+ "integrations.detail.health.unhealthy": "Nicht gesund",
23
+ "integrations.detail.health.unknown": "Unbekannt",
24
+ "integrations.detail.loadError": "Integration konnte nicht geladen werden",
25
+ "integrations.detail.logs.columns.level": "Stufe",
26
+ "integrations.detail.logs.columns.message": "Nachricht",
27
+ "integrations.detail.logs.columns.time": "Zeit",
28
+ "integrations.detail.logs.empty": "Noch keine Protokolleintr\u00e4ge",
29
+ "integrations.detail.logs.level.all": "Alle Stufen",
30
+ "integrations.detail.logs.level.error": "Fehler",
31
+ "integrations.detail.logs.level.info": "Info",
32
+ "integrations.detail.logs.level.warn": "Warnung",
33
+ "integrations.detail.logs.title": "Betriebsprotokolle",
34
+ "integrations.detail.stateError": "Status konnte nicht aktualisiert werden",
35
+ "integrations.detail.stateUpdated": "Integrationsstatus aktualisiert",
36
+ "integrations.detail.tabs.credentials": "Zugangsdaten",
37
+ "integrations.detail.tabs.health": "Status",
38
+ "integrations.detail.tabs.logs": "Protokolle",
39
+ "integrations.detail.tabs.version": "Version",
40
+ "integrations.detail.title": "Integration",
41
+ "integrations.detail.version.current": "Aktuelle Version",
42
+ "integrations.detail.version.deprecated": "Veraltet",
43
+ "integrations.detail.version.experimental": "Experimentell",
44
+ "integrations.detail.version.noVersions": "Diese Integration verwendet keine API-Versionierung",
45
+ "integrations.detail.version.saveError": "Version konnte nicht aktualisiert werden",
46
+ "integrations.detail.version.saved": "API-Version aktualisiert",
47
+ "integrations.detail.version.select": "Version ausw\u00e4hlen",
48
+ "integrations.detail.version.stable": "Stabil",
49
+ "integrations.detail.version.sunsetAt": "Ablauf: {date}",
50
+ "integrations.marketplace.categories.all": "Alle",
51
+ "integrations.marketplace.categories.communication": "Kommunikation",
52
+ "integrations.marketplace.categories.data_sync": "Datensynchronisation",
53
+ "integrations.marketplace.categories.notification": "Benachrichtigung",
54
+ "integrations.marketplace.categories.payment": "Zahlung",
55
+ "integrations.marketplace.categories.shipping": "Versand",
56
+ "integrations.marketplace.categories.storage": "Speicher",
57
+ "integrations.marketplace.categories.webhook": "Webhook",
58
+ "integrations.marketplace.configure": "Konfigurieren",
59
+ "integrations.marketplace.description": "Externe Dienste verbinden, Zugangsdaten verwalten und Integrationsstatus \u00fcberwachen.",
60
+ "integrations.marketplace.disableAll": "Alle deaktivieren",
61
+ "integrations.marketplace.disabled": "Deaktiviert",
62
+ "integrations.marketplace.enableAll": "Alle aktivieren",
63
+ "integrations.marketplace.enabled": "Aktiviert",
64
+ "integrations.marketplace.integrations": "{count} Integrationen",
65
+ "integrations.marketplace.loadError": "Integrationen konnten nicht geladen werden",
66
+ "integrations.marketplace.noResults": "Keine Integrationen gefunden",
67
+ "integrations.marketplace.search": "Integrationen suchen\u2026",
68
+ "integrations.marketplace.title": "Integrationen",
69
+ "integrations.nav.title": "Integrationen"
70
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "integrations.bundle.configureIntegration": "Configure",
3
+ "integrations.bundle.integrationToggles": "Integrations",
4
+ "integrations.bundle.sharedCredentials": "Shared Credentials",
5
+ "integrations.bundle.title": "Bundle",
6
+ "integrations.detail.back": "Back to Integrations",
7
+ "integrations.detail.credentials.bundleShared": "Shared credentials from bundle: {bundle}",
8
+ "integrations.detail.credentials.notConfigured": "No credentials configured yet",
9
+ "integrations.detail.credentials.save": "Save Credentials",
10
+ "integrations.detail.credentials.saveError": "Failed to save credentials",
11
+ "integrations.detail.credentials.saved": "Credentials saved",
12
+ "integrations.detail.disable": "Disable",
13
+ "integrations.detail.enable": "Enable",
14
+ "integrations.detail.health.check": "Run Check",
15
+ "integrations.detail.health.checkError": "Health check failed",
16
+ "integrations.detail.health.checking": "Checking\u2026",
17
+ "integrations.detail.health.degraded": "Degraded",
18
+ "integrations.detail.health.healthy": "Healthy",
19
+ "integrations.detail.health.lastChecked": "Last checked: {date}",
20
+ "integrations.detail.health.neverChecked": "Never checked",
21
+ "integrations.detail.health.title": "Health Status",
22
+ "integrations.detail.health.unhealthy": "Unhealthy",
23
+ "integrations.detail.health.unknown": "Unknown",
24
+ "integrations.detail.loadError": "Failed to load integration",
25
+ "integrations.detail.logs.columns.level": "Level",
26
+ "integrations.detail.logs.columns.message": "Message",
27
+ "integrations.detail.logs.columns.time": "Time",
28
+ "integrations.detail.logs.empty": "No log entries yet",
29
+ "integrations.detail.logs.level.all": "All Levels",
30
+ "integrations.detail.logs.level.error": "Error",
31
+ "integrations.detail.logs.level.info": "Info",
32
+ "integrations.detail.logs.level.warn": "Warning",
33
+ "integrations.detail.logs.title": "Operation Logs",
34
+ "integrations.detail.stateError": "Failed to update state",
35
+ "integrations.detail.stateUpdated": "Integration state updated",
36
+ "integrations.detail.tabs.credentials": "Credentials",
37
+ "integrations.detail.tabs.health": "Health",
38
+ "integrations.detail.tabs.logs": "Logs",
39
+ "integrations.detail.tabs.version": "Version",
40
+ "integrations.detail.title": "Integration",
41
+ "integrations.detail.version.current": "Current Version",
42
+ "integrations.detail.version.deprecated": "Deprecated",
43
+ "integrations.detail.version.experimental": "Experimental",
44
+ "integrations.detail.version.noVersions": "This integration does not use API versioning",
45
+ "integrations.detail.version.saveError": "Failed to update version",
46
+ "integrations.detail.version.saved": "API version updated",
47
+ "integrations.detail.version.select": "Select Version",
48
+ "integrations.detail.version.stable": "Stable",
49
+ "integrations.detail.version.sunsetAt": "Sunset: {date}",
50
+ "integrations.marketplace.categories.all": "All",
51
+ "integrations.marketplace.categories.communication": "Communication",
52
+ "integrations.marketplace.categories.data_sync": "Data Sync",
53
+ "integrations.marketplace.categories.notification": "Notification",
54
+ "integrations.marketplace.categories.payment": "Payment",
55
+ "integrations.marketplace.categories.shipping": "Shipping",
56
+ "integrations.marketplace.categories.storage": "Storage",
57
+ "integrations.marketplace.categories.webhook": "Webhook",
58
+ "integrations.marketplace.configure": "Configure",
59
+ "integrations.marketplace.description": "Connect external services, manage credentials, and monitor integration health.",
60
+ "integrations.marketplace.disableAll": "Disable All",
61
+ "integrations.marketplace.disabled": "Disabled",
62
+ "integrations.marketplace.enableAll": "Enable All",
63
+ "integrations.marketplace.enabled": "Enabled",
64
+ "integrations.marketplace.integrations": "{count} integrations",
65
+ "integrations.marketplace.loadError": "Failed to load integrations",
66
+ "integrations.marketplace.noResults": "No integrations found",
67
+ "integrations.marketplace.search": "Search integrations\u2026",
68
+ "integrations.marketplace.title": "Integrations",
69
+ "integrations.nav.title": "Integrations"
70
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "integrations.bundle.configureIntegration": "Configurar",
3
+ "integrations.bundle.integrationToggles": "Integraciones",
4
+ "integrations.bundle.sharedCredentials": "Credenciales compartidas",
5
+ "integrations.bundle.title": "Paquete",
6
+ "integrations.detail.back": "Volver a integraciones",
7
+ "integrations.detail.credentials.bundleShared": "Credenciales compartidas del paquete: {bundle}",
8
+ "integrations.detail.credentials.notConfigured": "No hay credenciales configuradas",
9
+ "integrations.detail.credentials.save": "Guardar credenciales",
10
+ "integrations.detail.credentials.saveError": "No se pudieron guardar las credenciales",
11
+ "integrations.detail.credentials.saved": "Credenciales guardadas",
12
+ "integrations.detail.disable": "Desactivar",
13
+ "integrations.detail.enable": "Activar",
14
+ "integrations.detail.health.check": "Ejecutar verificaci\u00f3n",
15
+ "integrations.detail.health.checkError": "La verificaci\u00f3n de estado fall\u00f3",
16
+ "integrations.detail.health.checking": "Verificando\u2026",
17
+ "integrations.detail.health.degraded": "Degradado",
18
+ "integrations.detail.health.healthy": "Saludable",
19
+ "integrations.detail.health.lastChecked": "\u00daltima verificaci\u00f3n: {date}",
20
+ "integrations.detail.health.neverChecked": "Nunca verificado",
21
+ "integrations.detail.health.title": "Estado de salud",
22
+ "integrations.detail.health.unhealthy": "No saludable",
23
+ "integrations.detail.health.unknown": "Desconocido",
24
+ "integrations.detail.loadError": "No se pudo cargar la integraci\u00f3n",
25
+ "integrations.detail.logs.columns.level": "Nivel",
26
+ "integrations.detail.logs.columns.message": "Mensaje",
27
+ "integrations.detail.logs.columns.time": "Hora",
28
+ "integrations.detail.logs.empty": "Sin entradas de registro",
29
+ "integrations.detail.logs.level.all": "Todos los niveles",
30
+ "integrations.detail.logs.level.error": "Error",
31
+ "integrations.detail.logs.level.info": "Info",
32
+ "integrations.detail.logs.level.warn": "Advertencia",
33
+ "integrations.detail.logs.title": "Registros de operaci\u00f3n",
34
+ "integrations.detail.stateError": "No se pudo actualizar el estado",
35
+ "integrations.detail.stateUpdated": "Estado de integraci\u00f3n actualizado",
36
+ "integrations.detail.tabs.credentials": "Credenciales",
37
+ "integrations.detail.tabs.health": "Estado",
38
+ "integrations.detail.tabs.logs": "Registros",
39
+ "integrations.detail.tabs.version": "Versi\u00f3n",
40
+ "integrations.detail.title": "Integraci\u00f3n",
41
+ "integrations.detail.version.current": "Versi\u00f3n actual",
42
+ "integrations.detail.version.deprecated": "Obsoleta",
43
+ "integrations.detail.version.experimental": "Experimental",
44
+ "integrations.detail.version.noVersions": "Esta integraci\u00f3n no usa versionado de API",
45
+ "integrations.detail.version.saveError": "No se pudo actualizar la versi\u00f3n",
46
+ "integrations.detail.version.saved": "Versi\u00f3n de API actualizada",
47
+ "integrations.detail.version.select": "Seleccionar versi\u00f3n",
48
+ "integrations.detail.version.stable": "Estable",
49
+ "integrations.detail.version.sunsetAt": "Fin de soporte: {date}",
50
+ "integrations.marketplace.categories.all": "Todas",
51
+ "integrations.marketplace.categories.communication": "Comunicaci\u00f3n",
52
+ "integrations.marketplace.categories.data_sync": "Sincronizaci\u00f3n",
53
+ "integrations.marketplace.categories.notification": "Notificaci\u00f3n",
54
+ "integrations.marketplace.categories.payment": "Pagos",
55
+ "integrations.marketplace.categories.shipping": "Env\u00edo",
56
+ "integrations.marketplace.categories.storage": "Almacenamiento",
57
+ "integrations.marketplace.categories.webhook": "Webhook",
58
+ "integrations.marketplace.configure": "Configurar",
59
+ "integrations.marketplace.description": "Conecta servicios externos, gestiona credenciales y monitorea el estado de las integraciones.",
60
+ "integrations.marketplace.disableAll": "Desactivar todas",
61
+ "integrations.marketplace.disabled": "Desactivada",
62
+ "integrations.marketplace.enableAll": "Activar todas",
63
+ "integrations.marketplace.enabled": "Activada",
64
+ "integrations.marketplace.integrations": "{count} integraciones",
65
+ "integrations.marketplace.loadError": "No se pudieron cargar las integraciones",
66
+ "integrations.marketplace.noResults": "No se encontraron integraciones",
67
+ "integrations.marketplace.search": "Buscar integraciones\u2026",
68
+ "integrations.marketplace.title": "Integraciones",
69
+ "integrations.nav.title": "Integraciones"
70
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "integrations.bundle.configureIntegration": "Konfiguruj",
3
+ "integrations.bundle.integrationToggles": "Integracje",
4
+ "integrations.bundle.sharedCredentials": "Wsp\u00f3\u0142dzielone dane",
5
+ "integrations.bundle.title": "Pakiet",
6
+ "integrations.detail.back": "Powr\u00f3t do integracji",
7
+ "integrations.detail.credentials.bundleShared": "Wsp\u00f3\u0142dzielone dane z pakietu: {bundle}",
8
+ "integrations.detail.credentials.notConfigured": "Brak skonfigurowanych danych",
9
+ "integrations.detail.credentials.save": "Zapisz dane",
10
+ "integrations.detail.credentials.saveError": "Nie uda\u0142o si\u0119 zapisa\u0107 danych",
11
+ "integrations.detail.credentials.saved": "Dane zapisane",
12
+ "integrations.detail.disable": "Wy\u0142\u0105cz",
13
+ "integrations.detail.enable": "W\u0142\u0105cz",
14
+ "integrations.detail.health.check": "Sprawd\u017a",
15
+ "integrations.detail.health.checkError": "Sprawdzenie stanu nie powiod\u0142o si\u0119",
16
+ "integrations.detail.health.checking": "Sprawdzanie\u2026",
17
+ "integrations.detail.health.degraded": "Pogorszony",
18
+ "integrations.detail.health.healthy": "Zdrowy",
19
+ "integrations.detail.health.lastChecked": "Ostatnie sprawdzenie: {date}",
20
+ "integrations.detail.health.neverChecked": "Nigdy nie sprawdzano",
21
+ "integrations.detail.health.title": "Stan zdrowia",
22
+ "integrations.detail.health.unhealthy": "Niezdrowy",
23
+ "integrations.detail.health.unknown": "Nieznany",
24
+ "integrations.detail.loadError": "Nie uda\u0142o si\u0119 za\u0142adowa\u0107 integracji",
25
+ "integrations.detail.logs.columns.level": "Poziom",
26
+ "integrations.detail.logs.columns.message": "Wiadomo\u015b\u0107",
27
+ "integrations.detail.logs.columns.time": "Czas",
28
+ "integrations.detail.logs.empty": "Brak wpis\u00f3w w logach",
29
+ "integrations.detail.logs.level.all": "Wszystkie poziomy",
30
+ "integrations.detail.logs.level.error": "B\u0142\u0105d",
31
+ "integrations.detail.logs.level.info": "Info",
32
+ "integrations.detail.logs.level.warn": "Ostrze\u017cenie",
33
+ "integrations.detail.logs.title": "Logi operacji",
34
+ "integrations.detail.stateError": "Nie uda\u0142o si\u0119 zaktualizowa\u0107 stanu",
35
+ "integrations.detail.stateUpdated": "Stan integracji zaktualizowany",
36
+ "integrations.detail.tabs.credentials": "Dane uwierzytelniaj\u0105ce",
37
+ "integrations.detail.tabs.health": "Stan",
38
+ "integrations.detail.tabs.logs": "Logi",
39
+ "integrations.detail.tabs.version": "Wersja",
40
+ "integrations.detail.title": "Integracja",
41
+ "integrations.detail.version.current": "Aktualna wersja",
42
+ "integrations.detail.version.deprecated": "Wycofana",
43
+ "integrations.detail.version.experimental": "Eksperymentalna",
44
+ "integrations.detail.version.noVersions": "Ta integracja nie u\u017cywa wersjonowania API",
45
+ "integrations.detail.version.saveError": "Nie uda\u0142o si\u0119 zaktualizowa\u0107 wersji",
46
+ "integrations.detail.version.saved": "Wersja API zaktualizowana",
47
+ "integrations.detail.version.select": "Wybierz wersj\u0119",
48
+ "integrations.detail.version.stable": "Stabilna",
49
+ "integrations.detail.version.sunsetAt": "Wygasa: {date}",
50
+ "integrations.marketplace.categories.all": "Wszystkie",
51
+ "integrations.marketplace.categories.communication": "Komunikacja",
52
+ "integrations.marketplace.categories.data_sync": "Synchronizacja",
53
+ "integrations.marketplace.categories.notification": "Powiadomienia",
54
+ "integrations.marketplace.categories.payment": "P\u0142atno\u015bci",
55
+ "integrations.marketplace.categories.shipping": "Wysy\u0142ka",
56
+ "integrations.marketplace.categories.storage": "Przechowywanie",
57
+ "integrations.marketplace.categories.webhook": "Webhook",
58
+ "integrations.marketplace.configure": "Konfiguruj",
59
+ "integrations.marketplace.description": "Po\u0142\u0105cz zewn\u0119trzne us\u0142ugi, zarz\u0105dzaj danymi uwierzytelniaj\u0105cymi i monitoruj stan integracji.",
60
+ "integrations.marketplace.disableAll": "Wy\u0142\u0105cz wszystkie",
61
+ "integrations.marketplace.disabled": "Wy\u0142\u0105czona",
62
+ "integrations.marketplace.enableAll": "W\u0142\u0105cz wszystkie",
63
+ "integrations.marketplace.enabled": "W\u0142\u0105czona",
64
+ "integrations.marketplace.integrations": "{count} integracji",
65
+ "integrations.marketplace.loadError": "Nie uda\u0142o si\u0119 za\u0142adowa\u0107 integracji",
66
+ "integrations.marketplace.noResults": "Nie znaleziono integracji",
67
+ "integrations.marketplace.search": "Szukaj integracji\u2026",
68
+ "integrations.marketplace.title": "Integracje",
69
+ "integrations.nav.title": "Integracje"
70
+ }
@@ -0,0 +1,204 @@
1
+ import crypto from 'node:crypto'
2
+ import type { EntityManager } from '@mikro-orm/postgresql'
3
+ import { decryptWithAesGcm, encryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'
4
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
5
+ import { createKmsService } from '@open-mercato/shared/lib/encryption/kms'
6
+ import { getBundle, getIntegration, resolveIntegrationCredentialsSchema } from '@open-mercato/shared/modules/integrations/types'
7
+ import { EncryptionMap } from '../../entities/data/entities'
8
+ import { IntegrationCredentials } from '../data/entities'
9
+ import type { IntegrationScope } from './types'
10
+
11
+ const ENCRYPTED_CREDENTIALS_BLOB_KEY = '__om_encrypted_credentials_blob_v1'
12
+ const DERIVED_KEY_CONTEXT = 'integrations.credentials'
13
+
14
+ function resolveFallbackEncryptionSecret(): string {
15
+ const candidates = [
16
+ process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY,
17
+ process.env.TENANT_DATA_ENCRYPTION_KEY,
18
+ process.env.AUTH_SECRET,
19
+ process.env.NEXTAUTH_SECRET,
20
+ ]
21
+
22
+ for (const value of candidates) {
23
+ const normalized = value?.trim()
24
+ if (normalized) return normalized
25
+ }
26
+
27
+ if (process.env.NODE_ENV !== 'production') return 'om-dev-tenant-encryption'
28
+
29
+ console.warn(
30
+ '[integrations.credentials] No encryption secret configured; using emergency fallback secret. Configure TENANT_DATA_ENCRYPTION_FALLBACK_KEY immediately.',
31
+ )
32
+ return 'om-emergency-fallback-rotate-me'
33
+ }
34
+
35
+ function deriveDekFromSecret(secret: string, tenantId: string): string {
36
+ return crypto
37
+ .createHash('sha256')
38
+ .update(`${DERIVED_KEY_CONTEXT}:${tenantId}:${secret}`)
39
+ .digest()
40
+ .toString('base64')
41
+ }
42
+
43
+ export function createCredentialsService(em: EntityManager) {
44
+ const credentialsEncryptionSpec = [{ field: 'credentials' }]
45
+
46
+ async function ensureCredentialsEncryptionMap(scope: IntegrationScope): Promise<void> {
47
+ const existing = await findOneWithDecryption(
48
+ em,
49
+ EncryptionMap,
50
+ {
51
+ entityId: 'integrations:integration_credentials',
52
+ tenantId: scope.tenantId,
53
+ organizationId: scope.organizationId,
54
+ deletedAt: null,
55
+ },
56
+ undefined,
57
+ scope,
58
+ )
59
+
60
+ if (!existing) {
61
+ const created = em.create(EncryptionMap, {
62
+ entityId: 'integrations:integration_credentials',
63
+ tenantId: scope.tenantId,
64
+ organizationId: scope.organizationId,
65
+ fieldsJson: credentialsEncryptionSpec,
66
+ isActive: true,
67
+ createdAt: new Date(),
68
+ updatedAt: new Date(),
69
+ })
70
+ em.persist(created)
71
+ return
72
+ }
73
+
74
+ existing.fieldsJson = credentialsEncryptionSpec
75
+ existing.isActive = true
76
+ }
77
+
78
+ async function resolveCredentialsDek(scope: IntegrationScope): Promise<string> {
79
+ const kms = createKmsService()
80
+ const existing = await kms.getTenantDek(scope.tenantId)
81
+ if (existing?.key) return existing.key
82
+
83
+ const created = await kms.createTenantDek(scope.tenantId)
84
+ if (created?.key) return created.key
85
+
86
+ return deriveDekFromSecret(resolveFallbackEncryptionSecret(), scope.tenantId)
87
+ }
88
+
89
+ async function encryptCredentialsBlob(
90
+ credentials: Record<string, unknown>,
91
+ scope: IntegrationScope,
92
+ ): Promise<Record<string, unknown>> {
93
+ const dek = await resolveCredentialsDek(scope)
94
+ const payload = encryptWithAesGcm(JSON.stringify(credentials), dek)
95
+ return { [ENCRYPTED_CREDENTIALS_BLOB_KEY]: payload.value }
96
+ }
97
+
98
+ async function decryptCredentialsBlob(
99
+ credentials: Record<string, unknown>,
100
+ scope: IntegrationScope,
101
+ ): Promise<Record<string, unknown>> {
102
+ const encrypted = credentials[ENCRYPTED_CREDENTIALS_BLOB_KEY]
103
+ if (typeof encrypted !== 'string' || !encrypted) return credentials
104
+
105
+ const dek = await resolveCredentialsDek(scope)
106
+ const decryptedRaw = decryptWithAesGcm(encrypted, dek)
107
+ if (!decryptedRaw) return {}
108
+
109
+ try {
110
+ const parsed = JSON.parse(decryptedRaw) as unknown
111
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
112
+ ? (parsed as Record<string, unknown>)
113
+ : {}
114
+ } catch {
115
+ return {}
116
+ }
117
+ }
118
+
119
+ return {
120
+ async getRaw(integrationId: string, scope: IntegrationScope): Promise<Record<string, unknown> | null> {
121
+ const row = await findOneWithDecryption(
122
+ em,
123
+ IntegrationCredentials,
124
+ {
125
+ integrationId,
126
+ organizationId: scope.organizationId,
127
+ tenantId: scope.tenantId,
128
+ deletedAt: null,
129
+ },
130
+ undefined,
131
+ scope,
132
+ )
133
+ if (!row) return null
134
+ return decryptCredentialsBlob(row.credentials, scope)
135
+ },
136
+
137
+ async resolve(integrationId: string, scope: IntegrationScope): Promise<Record<string, unknown> | null> {
138
+ const direct = await this.getRaw(integrationId, scope)
139
+ if (direct) return direct
140
+
141
+ const definition = getIntegration(integrationId)
142
+ if (!definition?.bundleId) return null
143
+ return this.getRaw(definition.bundleId, scope)
144
+ },
145
+
146
+ async save(integrationId: string, credentials: Record<string, unknown>, scope: IntegrationScope): Promise<void> {
147
+ await ensureCredentialsEncryptionMap(scope)
148
+ const encryptedCredentials = await encryptCredentialsBlob(credentials, scope)
149
+
150
+ const row = await findOneWithDecryption(
151
+ em,
152
+ IntegrationCredentials,
153
+ {
154
+ integrationId,
155
+ organizationId: scope.organizationId,
156
+ tenantId: scope.tenantId,
157
+ deletedAt: null,
158
+ },
159
+ undefined,
160
+ scope,
161
+ )
162
+
163
+ if (row) {
164
+ row.credentials = encryptedCredentials
165
+ await em.flush()
166
+ return
167
+ }
168
+
169
+ const created = em.create(IntegrationCredentials, {
170
+ integrationId,
171
+ credentials: encryptedCredentials,
172
+ organizationId: scope.organizationId,
173
+ tenantId: scope.tenantId,
174
+ })
175
+ await em.persistAndFlush(created)
176
+ },
177
+
178
+ async saveField(
179
+ integrationId: string,
180
+ fieldKey: string,
181
+ value: unknown,
182
+ scope: IntegrationScope,
183
+ ): Promise<Record<string, unknown>> {
184
+ const current = (await this.getRaw(integrationId, scope)) ?? {}
185
+ const updated = { ...current, [fieldKey]: value }
186
+ await this.save(integrationId, updated, scope)
187
+ return updated
188
+ },
189
+
190
+ getSchema(integrationId: string) {
191
+ const definition = getIntegration(integrationId)
192
+ if (!definition) return undefined
193
+
194
+ if (definition.bundleId) {
195
+ const bundle = getBundle(definition.bundleId)
196
+ return bundle?.credentials ?? resolveIntegrationCredentialsSchema(integrationId)
197
+ }
198
+
199
+ return definition.credentials ?? resolveIntegrationCredentialsSchema(integrationId)
200
+ },
201
+ }
202
+ }
203
+
204
+ export type CredentialsService = ReturnType<typeof createCredentialsService>
@@ -0,0 +1,59 @@
1
+ import type { AwilixContainer } from 'awilix'
2
+ import type { IntegrationStateService } from './state-service'
3
+ import type { IntegrationLogService } from './log-service'
4
+ import { getIntegration, getBundle } from '@open-mercato/shared/modules/integrations/types'
5
+ import type { IntegrationScope } from './types'
6
+
7
+ type HealthCheckResult = {
8
+ status: 'healthy' | 'degraded' | 'unhealthy'
9
+ message?: string
10
+ details?: Record<string, unknown>
11
+ }
12
+
13
+ type HealthCheckService = {
14
+ check: (credentials: Record<string, unknown> | null, scope: IntegrationScope) => Promise<HealthCheckResult>
15
+ }
16
+
17
+ export function createHealthService(
18
+ container: AwilixContainer,
19
+ stateService: IntegrationStateService,
20
+ logService: IntegrationLogService,
21
+ ) {
22
+ return {
23
+ async runHealthCheck(integrationId: string, scope: IntegrationScope): Promise<HealthCheckResult> {
24
+ const definition = getIntegration(integrationId)
25
+ const healthConfig = definition?.healthCheck ?? (definition?.bundleId ? getBundle(definition.bundleId)?.healthCheck : undefined)
26
+
27
+ if (!healthConfig?.service) {
28
+ return { status: 'unhealthy', message: 'No health check configured' }
29
+ }
30
+
31
+ let result: HealthCheckResult
32
+ try {
33
+ const checker = container.resolve<HealthCheckService>(healthConfig.service)
34
+ const credentialsService = container.resolve<{ resolve: (id: string, scope: IntegrationScope) => Promise<Record<string, unknown> | null> }>('integrationCredentialsService')
35
+ const credentials = await credentialsService.resolve(integrationId, scope)
36
+ result = await checker.check(credentials, scope)
37
+ } catch (error) {
38
+ const message = error instanceof Error ? error.message : 'Health check failed'
39
+ result = { status: 'unhealthy', message }
40
+ }
41
+
42
+ await stateService.upsert(integrationId, {
43
+ lastHealthStatus: result.status,
44
+ lastHealthCheckedAt: new Date(),
45
+ }, scope)
46
+
47
+ const logger = logService.scoped(integrationId, scope)
48
+ if (result.status === 'healthy') {
49
+ await logger.info(`Health check passed`, { status: result.status, ...result.details })
50
+ } else {
51
+ await logger.warn(`Health check: ${result.status}`, { status: result.status, message: result.message, ...result.details })
52
+ }
53
+
54
+ return result
55
+ },
56
+ }
57
+ }
58
+
59
+ export type IntegrationHealthService = ReturnType<typeof createHealthService>
@@ -0,0 +1,84 @@
1
+ import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'
2
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import type { ListIntegrationLogsQuery } from '../data/validators'
4
+ import { IntegrationLog } from '../data/entities'
5
+ import type { IntegrationScope } from './types'
6
+
7
+ type LogInput = {
8
+ integrationId: string
9
+ runId?: string | null
10
+ scopeEntityType?: string | null
11
+ scopeEntityId?: string | null
12
+ level: 'info' | 'warn' | 'error'
13
+ message: string
14
+ code?: string | null
15
+ payload?: Record<string, unknown> | null
16
+ }
17
+
18
+ export function createIntegrationLogService(em: EntityManager) {
19
+ return {
20
+ async write(input: LogInput, scope: IntegrationScope): Promise<IntegrationLog> {
21
+ const row = em.create(IntegrationLog, {
22
+ integrationId: input.integrationId,
23
+ runId: input.runId,
24
+ scopeEntityType: input.scopeEntityType,
25
+ scopeEntityId: input.scopeEntityId,
26
+ level: input.level,
27
+ message: input.message,
28
+ code: input.code,
29
+ payload: input.payload,
30
+ organizationId: scope.organizationId,
31
+ tenantId: scope.tenantId,
32
+ })
33
+ await em.persistAndFlush(row)
34
+ return row
35
+ },
36
+
37
+ scoped(integrationId: string, scope: IntegrationScope) {
38
+ return {
39
+ info: (message: string, payload?: Record<string, unknown>) => this.write({ integrationId, level: 'info', message, payload }, scope),
40
+ warn: (message: string, payload?: Record<string, unknown>) => this.write({ integrationId, level: 'warn', message, payload }, scope),
41
+ error: (message: string, payload?: Record<string, unknown>) => this.write({ integrationId, level: 'error', message, payload }, scope),
42
+ }
43
+ },
44
+
45
+ async query(query: ListIntegrationLogsQuery, scope: IntegrationScope): Promise<{ items: IntegrationLog[]; total: number }> {
46
+ const where: FilterQuery<IntegrationLog> = {
47
+ organizationId: scope.organizationId,
48
+ tenantId: scope.tenantId,
49
+ }
50
+
51
+ if (query.integrationId) where.integrationId = query.integrationId
52
+ if (query.level) where.level = query.level
53
+ if (query.runId) where.runId = query.runId
54
+ if (query.entityType) where.scopeEntityType = query.entityType
55
+ if (query.entityId) where.scopeEntityId = query.entityId
56
+
57
+ const items = await findWithDecryption(
58
+ em,
59
+ IntegrationLog,
60
+ where,
61
+ {
62
+ orderBy: { createdAt: 'DESC' },
63
+ limit: query.pageSize,
64
+ offset: (query.page - 1) * query.pageSize,
65
+ },
66
+ scope,
67
+ )
68
+ const total = await em.count(IntegrationLog, where)
69
+ return { items, total }
70
+ },
71
+
72
+ async pruneOlderThan(days: number, scope: IntegrationScope): Promise<number> {
73
+ const threshold = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
74
+ const deletedCount = await em.nativeDelete(IntegrationLog, {
75
+ organizationId: scope.organizationId,
76
+ tenantId: scope.tenantId,
77
+ createdAt: { $lt: threshold },
78
+ })
79
+ return deletedCount
80
+ },
81
+ }
82
+ }
83
+
84
+ export type IntegrationLogService = ReturnType<typeof createIntegrationLogService>