121 lines
17 KiB
Markdown
121 lines
17 KiB
Markdown
# Boilerplate-Abgleich: M33a — Apps-Engine / Adapter-Layer
|
|
|
|
| Feld | Wert |
|
|
|---|---|
|
|
| **Modul-ID** | M33a (Detail-Spec `_bmad-output/specs/M33a-apps-engine.md`) |
|
|
| **Name** | Apps-Engine / Adapter-Layer |
|
|
| **Milestones** | M008 (Adapter-Interface + State-Machine), M009 (Setup-Wizard + Sync-Log), M010 (Probe-Scheduler + Webhook-Sturm-Schutz) |
|
|
| **Block** | Block 4 — USP-Säulen (`_bmad-output/pflichtenheft/blocks/block4.md` Modul 33a) |
|
|
| **LPs** | LP-9 (Apps-Engine-Foundation), LP-10 (Klartext-Diagnostik), LP-11 (Adapter-Pattern für alle Integrationen) |
|
|
| **Reuse-Level** | **medium** |
|
|
|
|
## Reuse-Verdict (kurz)
|
|
|
|
Das **Daten-Modell ist fast 1:1 vorhanden** (`prisma/schema/appsEngine.prisma`: `App`, `DataConnection`, `AppSyncRun`, `AppSyncRunLog`, `AppSyncItem`) — exakt die fünf Entitäten aus Spec Block B. **Aber:** Diese Modelle sind in der Boilerplate **schema-only** — es gibt **keine** Domänen-Logik, **kein** Adapter-Interface, **keine** State-Machine, **keinen** Setup-Wizard, **keine** Settings-UI, **keinen** Probe-Scheduler und **keinen** Webhook-Receiver dafür. Verifiziert per Grep: in `apps/web/app`, `apps/worker/src`, `apps/api/src` und allen `packages/*` (außer dem generierten Prisma-Client) existiert **kein** Code, der `DataConnection`/`AppSyncRun`/`sample-data-sync` verwendet.
|
|
|
|
Das eigentliche Engineering (M008-M010) muss neu gebaut werden — aber auf einem **sehr starken Querschnitt-Fundament**: AES-256-GCM-Credential-Encryption (`CryptoApi`), BullMQ-Queue mit Idempotenz + plan-basierten Prioritäten, Webhook-Signatur-Validierung (Stripe/MailerSend als HMAC-Vorlage), ein periodischer Worker-Scheduler (`metrics-scheduler`), eine fertige Connection-Management-Admin-Route als Vorlage (`marketing-integrations.tsx` inkl. Encryption-at-rest + Secret-Masking) und das Adapter-Registry-Pattern aus `@toolctrl/marketing-outbound`. Der WASM-`plugin-runtime` deckt die **Phase-2-Marketplace**-Foundation ab (M33b/M33c), ist für Phase 1 aber nicht der Kern.
|
|
|
|
> Wichtige Begriffs-Abgrenzung (auch in der Inventur `docs/boilerplate-inventory/integrations-ki.md` §4 betont): Es gibt in der Boilerplate **zwei** „Apps-Engine"-Bedeutungen. (1) Die **Data-Sync-Engine** (`appsEngine.prisma`, `App`/`DataConnection`/`AppSyncRun`) = das, was M33a Phase 1 will. (2) Das **WASM-Plugin-System** (`plugin-runtime`/`plugin-sdk`/`plugins.prisma`) = M33a Phase 2 (Drittanbieter-Marketplace). Nicht verwechseln.
|
|
|
|
---
|
|
|
|
## Übernehmbar aus Boilerplate
|
|
|
|
| Boilerplate-Pfad | Was | Wie nutzbar |
|
|
|---|---|---|
|
|
| `prisma/schema/appsEngine.prisma` | `App`, `DataConnection`, `AppSyncRun`, `AppSyncRunLog`, `AppSyncItem` — die fünf Spec-B-Entitäten | **anpassen** — Felder/Enums an Spec angleichen (siehe „Lücken" unten) |
|
|
| `apps/web/app/utils/api/.server/CryptoApi.ts` | AES-256-GCM Verschlüsselung (`encrypt`/`decrypt`, versioniert `v1:iv:tag:ct`, `CRYPTO_SECRET`) | **1:1** für `DataConnection.credentials` „verschlüsselt at-rest" (Spec B, 33a.9.3 „Plattform-Schlüssel") |
|
|
| `apps/web/app/routes/admin/marketing-integrations.tsx` | Komplette Connection-Management-Admin-Route: Liste, Create/Toggle/Delete, Permission-Gate (`verifyUserHasPermission`), Secret-Masking, `lastDeliveredAt`/`lastError`-Anzeige, i18n | **als-vorlage** für die Settings-UI `/settings/integrationen/` (Spec F) |
|
|
| `apps/web/app/utils/leads/marketingIntegrations.db.server.ts` | Encryption-at-rest DB-Layer: `SENSITIVE_KEYS`, `encryptSensitive`/`decryptSensitive`, `maskConfig` über `CryptoApi`, Round-Trip | **als-vorlage** für `DataConnection`-Persistenz mit verschlüsselten Credentials |
|
|
| `packages/marketing-outbound/src/index.ts` + `src/types.ts` | Adapter-Interface + Registry-Pattern (`adapterRegistry`, `resolveAdapter(type)`, `MarketingOutboundAdapter<TConfig>` mit `parseConfig`/`send`), `adapterFetch()` (Timeout + SSRF-Allowlist, nur HTTPS, kein RFC1918) | **als-vorlage** für das Adapter-Interface der Apps-Engine (Spec A: `connect`/`syncPull`/`syncPush`/`handleWebhook`/`validateConnection`) + sicheres Outbound-Fetch |
|
|
| `packages/queue/*` (`names.ts`, `payloads.ts`, `producers.ts`, `priority.ts`, `runtime.ts`) | BullMQ-Wrapper: typisierte Jobs, **`buildIdempotentJobId()`** (Idempotenz 33a.4.4), `resolveJobPriorityForTenant()` (plan-basiert), `createQueue`/`createWorker` | **1:1** für Sync-Mechanik (Pull/Push/Webhook/Manual), Idempotenz-Keys, Sync-Lock |
|
|
| `apps/worker/src/metrics-scheduler.ts` | Periodischer Poller-Pattern (30s/60s-Kadenzen, best-effort, started aus Worker-Entrypoint) | **als-vorlage** für den **Probe-Scheduler** (M010: aktiv 5min / eingeschränkt 1min / defekt 15min) |
|
|
| `apps/api/src/webhooks/mailersend-signature.ts` | HMAC-Signatur-Validierung mit `timingSafeEqual`, Timestamp-Toleranz, typisierte Failure-Reasons | **als-vorlage** für Webhook-Signatur-Validierung pro App (Spec E.3, offener Punkt der Spec) |
|
|
| `apps/api/src/billing/stripe-webhook-route.ts` | Inbound-Webhook-Route mit Signatur-Konstruktion, Outbox-Publish in DB-Transaktion, Metrics, Error-Envelope | **als-vorlage** für den Webhook-Receiver `app.hivegrid.de/api/v1/webhooks/{app_key}/{tenant_id}/{connection_id}` (Spec E.3) |
|
|
| `apps/api/src/webhookDeliveryQueue.ts` + `WebhookDeliveryJob` (`@toolctrl/queue`) | Outbound-Webhook-Delivery über Queue (`buildWebhookDeliveryJob`, signierte/retried Zustellung) | **anpassen** für Push-Sync / signierte Outbound-Webhooks (Spec E.2) |
|
|
| `packages/storage/src/index.ts` (`StorageAdapter`, `createS3StorageAdapter`) + `signed/` | Scaleway-S3-Adapter + Signed-URLs | **als-vorlage** für den `kwpsync.exe`-Download im Setup-Wizard (Spec D, connection_type `windows_client`) |
|
|
| `apps/web/app/utils/integrations/githubService.ts` | Beispiel eines konkreten externen-Service-Adapters (OAuth/Token-Handling-Muster) | **als-vorlage** für die App-spezifischen Adapter (eigene Module: KWP/Gmail/Stripe) |
|
|
| `packages/plugin-runtime/*` + `packages/plugin-sdk/*` + `prisma/schema/plugins.prisma` | WASM-Sandbox, Capability-Gateway (deny-by-default), Manifest+Ed25519-Signatur, Hook-Registry, Audit-Chain, Crash-Tracker; `Plugin`/`TenantPluginCapabilityGrant`/`PluginInvocation` | **als-vorlage** ausschließlich für **Phase 2** (M33b Community-Marketplace mit Sandboxing/Review, Spec Block I) — **nicht** Phase-1-Kern |
|
|
| `packages/plugins/src/manifest-schema.ts` (`FeatureManifest`, `validateFeatureManifestShape`) | Feature-/App-Manifest-Schema (Surfaces/Workers/Provider/Permissions/Audit) | **als-vorlage** für das Manifest-Format der Adapter-Registry (Spec C `manifest.ts`) |
|
|
|
|
### Querschnitt, das M33a wie jedes Modul mitnimmt (separate Foundation-Module M001-M011)
|
|
- **Audit:** automatisch via M001-Prisma-Middleware (AGENTS.md §C.13); externe Webhook-Aufrufe manuell via `auditEvent` (INTEGRATION-CONTRACTS §6). Deckt AV #6 (Audit für Connection-Änderungen).
|
|
- **Notifications:** via `notify()` (M011, INTEGRATION-CONTRACTS §7) — für `degraded`/`broken`-Alerts (Spec G, 33a.3.5). Realtime/In-App über `@toolctrl/realtime` (SSE).
|
|
- **Mail:** `@toolctrl/mail` (MailerSend, EU) für Email-Benachrichtigungen.
|
|
- **i18n:** M003-Build-Lint; pro-App `i18n/de.json` Klartext-Übersetzungen (Spec C/G).
|
|
- **Feature-Flags:** M006 — pro App `phase`/`requires_subscription_feature` + Feature-Flag (Spec C Tenant-Sicht).
|
|
- **Permissions:** M004 `withTenantScope()` + `withPermission()`; `verifyUserHasPermission` als bestehendes Boilerplate-Muster.
|
|
|
|
---
|
|
|
|
## Vorhandene Daten-Modelle
|
|
|
|
**Quelle:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/prisma/schema/appsEngine.prisma` (generierter Client: `packages/db/generated/prisma/client/models/{App,DataConnection,AppSyncRun,AppSyncRunLog,AppSyncItem}.ts`).
|
|
|
|
| Boilerplate-Modell | Deckt Spec-Entität | Vorhandene Felder (Auszug) | Bewertung |
|
|
|---|---|---|---|
|
|
| `App` | App (Spec B) | `id`, `tenantId`, `type`, `config` (JSON), `icon`, `isActive`, `installedByUserId`, `syncRuns[]`, `@@unique([tenantId, type])` | Teilweise. **In BP tenant-scoped** (`tenantId`), Spec will App **plattform-weit** (Code-Registry-Spiegel). Es fehlen `key`/`name`/`description`/`category`/`connection_type`/`connection_schema`/`capabilities[]`/`phase`/`requires_subscription_feature`/`documentation_url`. |
|
|
| `DataConnection` | DataConnection (Spec B) | `id`, `tenantId`, `name`, `type`, `config` (Kommentar „Encrypted JSON credentials"), `description` | Solide Basis. **Keine FK zu `App`** in BP (Spec will `app_id`), kein `connection_status`-Enum, keine `last_health_check_*`/`last_sync_at`/`next_sync_at`/`last_error`/`metadata`/`created_by_user_id`. Multi-Connection-pro-App-pro-Tenant ist mit `name` schon angelegt. |
|
|
| `AppSyncRun` | AppSyncRun (Spec B) | `id`, `appId`(FK), `tenantId`, `taskKey`/`taskName`, `status` (pending/running/completed/partial/failed/cancelled/reverted), `executionMode` (manual/scheduled/webhook), `startedAt`/`completedAt`, `duration`, `itemsProcessed/Created/Updated/Failed`, `error`, `createdByUserId`, `logs[]`, `syncItems[]` | **Sehr gute Übereinstimmung.** FK zeigt auf `App`, Spec will FK auf `DataConnection` (`data_connection_id`). `kind` (pull/push/webhook/health_check/manual_resync) vs. BP `executionMode` (manual/scheduled/webhook) — angleichen. `summary_text` (Klartext) fehlt; `itemsSucceeded` als `itemsProcessed - itemsFailed` ableitbar. |
|
|
| `AppSyncRunLog` | AppSyncRunLog (Spec B) | `id`, `syncRunId`(FK), `type` (info/success/warning/error), `message`, `metadata` | Gute Basis. `severity` der Spec (`debug/info/warn/error/critical`) ⊃ BP-`type`; `timestamp` fehlt (nur `createdAt`); `tech_details` = `metadata`. |
|
|
| `AppSyncItem` | AppSyncItem (Spec B) | `id`, `syncRunId`(FK), `objectType`, `sourceId`, `destinationId`, `status` (synced/failed/skipped/pending/reverted), `action`, `sourceData`/`destinationData` (JSON), `error`, **`rolledBackData`/`rolledBackAt`** | **Sehr gute Übereinstimmung, sogar mehr** (Rollback-Felder, die die Spec nicht explizit fordert). Spec `operation` (create/update/delete/upsert/noop) vs. BP `action` (created/updated/deleted/skipped) — angleichen. `entityType`/`entityId` (HiveGrid-Entität) = BP `objectType`/`destinationId`. |
|
|
|
|
**Phase-2-Modelle (Marketplace) — vorhanden in `prisma/schema/plugins.prisma`:** `Plugin`, `TenantPluginCapabilityGrant`, `PluginUserConfig`, `PluginInvocation`, `PluginCapabilityUse` (mit per-Tenant Hash-Chain). Deckt die Spec-B-Phase-2-Entitäten (`CommunityApp`, `CommunityAppInstall`) konzeptionell ab, aber mit anderem Namensschema. **Nicht** für Phase 1 verwenden.
|
|
|
|
**Nicht vorhanden:** kein `App`-Registry-Drift-Tracking-Modell, keine Webhook-Rate-Limit-/Anomalie-State-Tabelle, kein Schema-Drift-Sentinel-State.
|
|
|
|
---
|
|
|
|
## Lücken / komplett neu zu bauen
|
|
|
|
### M008 — Adapter-Interface + State-Machine (neu)
|
|
1. **Adapter-Interface** (`app/integrations/{app-key}/index.ts`-Konvention, Spec A/C): `connect`/`disconnect`/`refreshTokens`/`validateConnection`/`syncPull`/`syncPush`/`handleWebhook` + `capabilities[]`/`triggers[]`/`actions[]` + `connection_schema`. Vorlage: `marketing-outbound`-Adapter-Registry, aber das konkrete Interface existiert nicht.
|
|
2. **5-Status-State-Machine** `pending → active → degraded → broken → revoked` (Spec G, 33a.3.1) mit Audit-Eintrag pro Übergang + Klartext-Hinweis pro Status. Existiert **nicht** (BP `DataConnection` hat keinen Status-Enum). `connection_status`-Feld + Enum neu.
|
|
3. **Adapter-Registry / Build-Time-Scan** von `app/integrations/` + DB-Sync der Manifests + Drift-Check (Spec C). Existiert **nicht** (Manifest-Schema-Vorlage in `@toolctrl/plugins`).
|
|
4. **Dummy-App** als Referenz-Implementierung (M008-Erfolgskriterium). Neu.
|
|
|
|
### M009 — Setup-Wizard + Sync-Log (neu)
|
|
5. **Generischer Setup-Wizard** pro Connection-Type (OAuth2 / API-Key / Username-Password / Windows-Client), Form-Generator aus `connection_schema` (Spec D, 33a.2). UI komplett neu (Vorlage: `marketing-integrations.tsx` für die Listen-/CRUD-Hülle).
|
|
6. **OAuth2-Redirect-Flow + Token-Refresh-Worker** (Spec D). Kein generischer OAuth-Client in BP (`authProviders.server.ts` ist Auth-Login, nicht App-Connection-OAuth; `@ai-sdk/openai`/`remix-auth-google` per EU-Constraint verboten). Neu.
|
|
7. **Sync-Log-UI** mit Filter (Status/Operation/Datum) + Detail-Schublade + Klartext-`summary_text` (Spec F, 33a.4). Neu.
|
|
8. **Disconnect-Flow** mit Multi-Step-Confirm + Cleanup (Webhook-Subscriptions kündigen, Tokens revoken) + `status=revoked` statt Hard-Delete (Spec D, 33a.7.4). Neu.
|
|
|
|
### M010 — Probe-Scheduler + Webhook-Sturm-Schutz (neu)
|
|
9. **Probe-Scheduler** mit status-adaptiver Frequenz (active 5min / degraded 1min / broken 15min, Spec G/33a.3.4). Vorlage: `metrics-scheduler.ts` (Poller-Pattern) + `@toolctrl/queue` Repeatable-Jobs — die konkrete Logik neu.
|
|
10. **Webhook-Sturm-Schutz** (Hard-Limit Events/Stunde pro Mandant, Default 1000) + Verwerfen-mit-Notification (Spec E, 33a.5.1, AV A.16.1). Rate-Limiter-Vorlage existiert in `@toolctrl/ai/guards` (`createFixedWindowAIRateLimiter`), aber nicht für Webhooks. Neu.
|
|
11. **Anomalie-Erkennung** (10x normales Volumen → Pause + Klartext, Spec E, 33a.5.2, AV A.16.2): Rolling-Window-Average + Threshold. Neu.
|
|
12. **Schema-Drift-Sentinel** (`validateSchema()`, Spec E/33a.6): Pause + Plattform-Admin-Alert (KWP-spezifisch in M092). Neu.
|
|
13. **Webhook-Receiver-Route** `api/v1/webhooks/{app_key}/{tenant_id}/{connection_id}` + per-App-Signatur-Validierung (Spec E.3). Routen-/HMAC-Vorlage in `stripe-webhook-route.ts` + `mailersend-signature.ts`, der generische Multi-App-Receiver neu.
|
|
|
|
### Querschnitt / Wiring (neu pro Modul, aber Foundation vorhanden)
|
|
14. **Sidebar-Eintrag** `Einstellungen → Integrationen (Apps)` (INTEGRATION-CONTRACTS §1, Route `app.hivegrid.de/settings/integrationen/`). Neu in `packages/ui-hivegrid/src/app-shell/sidebar.config.ts` (Datei selbst existiert in HiveGrid noch nicht — Repo ist Greenfield).
|
|
15. **6 Permissions** `integrations.read/connect/disconnect/manual_sync/view_logs/admin` (Spec H) — in `packages/entitlements/src/permissions.ts` + Default-Rollen-Mapping (GF/Vertriebsleiter/Büro). Permission-Pattern existiert (`permissions-seed.server.ts`), die Keys neu.
|
|
16. **View-Model-Layer** (`packages/view-models/**`) + UI-Adapter (`packages/ui-hivegrid/src/adapters/`) — gemäß AGENTS.md Code-Layer-Trennung. Diese Pakete existieren in der Boilerplate **noch nicht** (sind HiveGrid-Eigenbau laut AGENTS.md „Du erweiterst, was fehlt").
|
|
17. **Klartext-Fehler-Catalog-Einträge** (M002) pro Fehler-Code + Lösungs-Hinweis (Spec G/33a.10). Neu.
|
|
|
|
### Repo-Realität (wichtig)
|
|
18. **Das `HiveGrid-Cloud`-Repo enthält aktuell NUR Planungs-Artefakte** (`_bmad`, `_bmad-output`, `docs`, `prisma`, `AGENTS.md`) — **kein** `apps/`, `packages/`, kein `package.json`. Verifiziert per `ls`. Die Boilerplate muss erst als Codebase ins Repo gezogen werden (AGENTS.md: „Die Boilerplate IST deine Codebase"). Bis dahin gibt es nichts zu erweitern — der Pre-Flight-Check aus AGENTS.md (`package.json`/`apps/`/`packages/` vorhanden) schlägt fehl.
|
|
|
|
---
|
|
|
|
## Empfohlenes Vorgehen
|
|
|
|
**Reihenfolge (zuerst BP übernehmen, dann neu bauen):**
|
|
|
|
1. **Voraussetzung:** Boilerplate als Codebase ins HiveGrid-Repo bringen + EU-Sterilisations-Check (AGENTS.md Pre-Flight). Vorher kann M008 nicht starten.
|
|
|
|
2. **Daten-Layer (BP anpassen):** `prisma/schema/appsEngine.prisma` als Basis nehmen und an Spec B angleichen:
|
|
- `App`: plattform-weit machen (oder zweigeteiltes Modell App-Definition vs. installierte App klären), Felder `key/name/description/category/connection_type/connection_schema/capabilities/phase/requires_subscription_feature/documentation_url` ergänzen.
|
|
- `DataConnection`: FK `app_id`, `connection_status`-Enum (5 Status), `last_health_check_*`/`last_sync_at`/`next_sync_at`/`last_error`/`metadata`/`created_by_user_id`. Credentials über `CryptoApi` (1:1) verschlüsseln — Vorlage `marketingIntegrations.db.server.ts`.
|
|
- `AppSyncRun`/`AppSyncRunLog`/`AppSyncItem`: FK-Ziel auf `DataConnection`, `kind`/`severity`/`operation`/`summary_text` angleichen. Forward-Only-Migration mit Inverse-SQL-Kommentar (AGENTS.md §F.28).
|
|
|
|
3. **M008 (Adapter-Interface + State-Machine):** Adapter-Interface nach Vorbild `@toolctrl/marketing-outbound` (Registry + `parseConfig`/`send` → erweitern zu `connect`/`sync*`/`handleWebhook`/`validateConnection`). Outbound-Calls über `adapterFetch()` (SSRF-/Timeout-Guard). State-Machine als Enum-Transitions mit Audit pro Wechsel (M001-Middleware deckt DB-Audit, externe Calls via `auditEvent`). Dummy-App als Referenz. Manifest-Schema nach Vorbild `@toolctrl/plugins`.
|
|
|
|
4. **M009 (Setup-Wizard + Sync-Log):** Settings-Route nach Vorbild `marketing-integrations.tsx` (Liste/CRUD/Permission-Gate/Secret-Masking/i18n) → erweitern zum Wizard + Sync-Log-UI. View-Model-Layer + UI-Adapter neu (AGENTS.md Code-Layer-Disziplin). Jobs über `@toolctrl/queue` mit `buildIdempotentJobId` (Idempotenz 33a.4.4) + `resolveJobPriorityForTenant`.
|
|
|
|
5. **M010 (Probe-Scheduler + Schutz):** Probe-Scheduler nach Vorbild `metrics-scheduler.ts` + Queue-Repeatable-Jobs, status-adaptive Frequenz. Webhook-Receiver-Route nach Vorbild `stripe-webhook-route.ts`, Signatur nach `mailersend-signature.ts`. Rate-Limiter + Anomalie-Detector + Schema-Drift-Sentinel neu. Notifications strikt über `notify()` (M011).
|
|
|
|
6. **Phase 2 (separat, M33b/M33c):** Erst nach Geerkens-Launch. WASM-`plugin-runtime`/`plugin-sdk`/`plugins.prisma` als Foundation für den Drittanbieter-Marketplace (Sandboxing/Capability-Consent/Signatur/Audit-Chain). Achtung: in der BP sind in Prod nur `log`/`audit`-Capabilities freigeschaltet (`PLUGIN_AVAILABLE_HOST_CAPABILITY_NAMES`).
|
|
|
|
**Querschnitt nicht vergessen (DoD-Block):** Sidebar-Eintrag (Einstellungen-Gruppe), 6 Permissions, i18n-Keys, Feature-Flag, Klartext-Catalog, Smoke-Tests + Hot-Swap-Drill + Visual-Snapshot, Permission-Denied → 404.
|