@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.
- package/AGENTS.md +10 -0
- package/dist/generated/entities/integration_credentials/index.js +19 -0
- package/dist/generated/entities/integration_credentials/index.js.map +7 -0
- package/dist/generated/entities/integration_log/index.js +27 -0
- package/dist/generated/entities/integration_log/index.js.map +7 -0
- package/dist/generated/entities/integration_state/index.js +27 -0
- package/dist/generated/entities/integration_state/index.js.map +7 -0
- package/dist/generated/entities/sync_cursor/index.js +19 -0
- package/dist/generated/entities/sync_cursor/index.js.map +7 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_mapping/index.js +19 -0
- package/dist/generated/entities/sync_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_run/index.js +45 -0
- package/dist/generated/entities/sync_run/index.js.map +7 -0
- package/dist/generated/entities/sync_schedule/index.js +35 -0
- package/dist/generated/entities/sync_schedule/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +14 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +16 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/data_sync/acl.js +11 -0
- package/dist/modules/data_sync/acl.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/route.js +132 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +87 -0
- package/dist/modules/data_sync/api/run.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/runs.js +66 -0
- package/dist/modules/data_sync/api/runs.js.map +7 -0
- package/dist/modules/data_sync/api/validate.js +66 -0
- package/dist/modules/data_sync/api/validate.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
- package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
- package/dist/modules/data_sync/data/entities.js +228 -0
- package/dist/modules/data_sync/data/entities.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +7 -0
- package/dist/modules/data_sync/di.js +26 -0
- package/dist/modules/data_sync/di.js.map +7 -0
- package/dist/modules/data_sync/events.js +16 -0
- package/dist/modules/data_sync/events.js.map +7 -0
- package/dist/modules/data_sync/index.js +9 -0
- package/dist/modules/data_sync/index.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
- package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter.js +1 -0
- package/dist/modules/data_sync/lib/adapter.js.map +7 -0
- package/dist/modules/data_sync/lib/id-mapping.js +79 -0
- package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
- package/dist/modules/data_sync/lib/queue.js +17 -0
- package/dist/modules/data_sync/lib/queue.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +309 -0
- package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/data_sync/setup.js +13 -0
- package/dist/modules/data_sync/setup.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +14 -0
- package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-import.js +14 -0
- package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
- package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
- package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
- package/dist/modules/integrations/acl.js +4 -1
- package/dist/modules/integrations/acl.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/health/route.js +46 -0
- package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/route.js +65 -0
- package/dist/modules/integrations/api/[id]/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/state/route.js +109 -0
- package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/version/route.js +117 -0
- package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
- package/dist/modules/integrations/api/guards.js +31 -0
- package/dist/modules/integrations/api/guards.js.map +7 -0
- package/dist/modules/integrations/api/logs/route.js +60 -0
- package/dist/modules/integrations/api/logs/route.js.map +7 -0
- package/dist/modules/integrations/api/openapi.js +25 -0
- package/dist/modules/integrations/api/openapi.js.map +7 -0
- package/dist/modules/integrations/api/route.js +68 -0
- package/dist/modules/integrations/api/route.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.js +212 -0
- package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
- package/dist/modules/integrations/data/enrichers.js +27 -12
- package/dist/modules/integrations/data/enrichers.js.map +2 -2
- package/dist/modules/integrations/data/entities.js +136 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/data/validators.js +36 -0
- package/dist/modules/integrations/data/validators.js.map +7 -0
- package/dist/modules/integrations/di.js +24 -0
- package/dist/modules/integrations/di.js.map +7 -0
- package/dist/modules/integrations/events.js +19 -0
- package/dist/modules/integrations/events.js.map +7 -0
- package/dist/modules/integrations/lib/credentials-service.js +159 -0
- package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
- package/dist/modules/integrations/lib/health-service.js +37 -0
- package/dist/modules/integrations/lib/health-service.js.map +7 -0
- package/dist/modules/integrations/lib/log-service.js +66 -0
- package/dist/modules/integrations/lib/log-service.js.map +7 -0
- package/dist/modules/integrations/lib/registry-service.js +33 -0
- package/dist/modules/integrations/lib/registry-service.js.map +7 -0
- package/dist/modules/integrations/lib/state-service.js +55 -0
- package/dist/modules/integrations/lib/state-service.js.map +7 -0
- package/dist/modules/integrations/lib/types.js +1 -0
- package/dist/modules/integrations/lib/types.js.map +7 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/integrations/setup.js +2 -2
- package/dist/modules/integrations/setup.js.map +2 -2
- package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
- package/dist/modules/integrations/workers/log-pruner.js +18 -0
- package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
- package/generated/entities/integration_credentials/index.ts +8 -0
- package/generated/entities/integration_log/index.ts +12 -0
- package/generated/entities/integration_state/index.ts +12 -0
- package/generated/entities/sync_cursor/index.ts +8 -0
- package/generated/entities/sync_external_id_mapping/index.ts +12 -0
- package/generated/entities/sync_mapping/index.ts +8 -0
- package/generated/entities/sync_run/index.ts +21 -0
- package/generated/entities/sync_schedule/index.ts +16 -0
- package/generated/entities.ids.generated.ts +14 -0
- package/generated/entity-fields-registry.ts +16 -0
- package/package.json +2 -2
- package/src/modules/data_sync/AGENTS.md +157 -0
- package/src/modules/data_sync/acl.ts +7 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
- package/src/modules/data_sync/api/mappings/route.ts +144 -0
- package/src/modules/data_sync/api/run.ts +97 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
- package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
- package/src/modules/data_sync/api/runs.ts +69 -0
- package/src/modules/data_sync/api/validate.ts +73 -0
- package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
- package/src/modules/data_sync/data/entities.ts +180 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +38 -0
- package/src/modules/data_sync/events.ts +12 -0
- package/src/modules/data_sync/i18n/de.json +48 -0
- package/src/modules/data_sync/i18n/en.json +48 -0
- package/src/modules/data_sync/i18n/es.json +48 -0
- package/src/modules/data_sync/i18n/pl.json +48 -0
- package/src/modules/data_sync/index.ts +5 -0
- package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
- package/src/modules/data_sync/lib/adapter.ts +90 -0
- package/src/modules/data_sync/lib/id-mapping.ts +95 -0
- package/src/modules/data_sync/lib/queue.ts +19 -0
- package/src/modules/data_sync/lib/sync-engine.ts +375 -0
- package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
- package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
- package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
- package/src/modules/data_sync/setup.ts +11 -0
- package/src/modules/data_sync/workers/sync-export.ts +27 -0
- package/src/modules/data_sync/workers/sync-import.ts +27 -0
- package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
- package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
- package/src/modules/integrations/AGENTS.md +160 -0
- package/src/modules/integrations/acl.ts +3 -0
- package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
- package/src/modules/integrations/api/[id]/health/route.ts +53 -0
- package/src/modules/integrations/api/[id]/route.ts +76 -0
- package/src/modules/integrations/api/[id]/state/route.ts +121 -0
- package/src/modules/integrations/api/[id]/version/route.ts +132 -0
- package/src/modules/integrations/api/guards.ts +59 -0
- package/src/modules/integrations/api/logs/route.ts +63 -0
- package/src/modules/integrations/api/openapi.ts +22 -0
- package/src/modules/integrations/api/route.ts +73 -0
- package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
- package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
- package/src/modules/integrations/backend/integrations/page.tsx +296 -0
- package/src/modules/integrations/data/enrichers.ts +35 -18
- package/src/modules/integrations/data/entities.ts +114 -5
- package/src/modules/integrations/data/validators.ts +41 -0
- package/src/modules/integrations/di.ts +31 -0
- package/src/modules/integrations/events.ts +17 -0
- package/src/modules/integrations/i18n/de.json +70 -0
- package/src/modules/integrations/i18n/en.json +70 -0
- package/src/modules/integrations/i18n/es.json +70 -0
- package/src/modules/integrations/i18n/pl.json +70 -0
- package/src/modules/integrations/lib/credentials-service.ts +204 -0
- package/src/modules/integrations/lib/health-service.ts +59 -0
- package/src/modules/integrations/lib/log-service.ts +84 -0
- package/src/modules/integrations/lib/registry-service.ts +42 -0
- package/src/modules/integrations/lib/state-service.ts +64 -0
- package/src/modules/integrations/lib/types.ts +4 -0
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
- package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
- package/src/modules/integrations/setup.ts +2 -2
- package/src/modules/integrations/widgets/injection-table.ts +1 -1
- 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>
|