19 KiB
Boilerplate-Abgleich: M34 — Public-API & Webhooks
| Feld | Wert |
|---|---|
| Modul-ID | M34 |
| Name | Public-API & Webhooks |
| Milestones | M161 (OpenAPI + Read-Endpoints), M162 (API-Key-Auth + Scopes + Rate-Limit), M163 (Webhooks ein-/ausgehend, signiert) |
| Pflichtenheft-Block | Block 7, Modul 34 (_bmad-output/pflichtenheft/blocks/block7.md Z. 264-311) |
| Reuse-Level | medium |
Kurzfazit
Die Boilerplate bringt ein echtes, dediziertes Public-API-Fundament mit: eine eigenständige Hono-App (apps/api) mit OpenAPI-Generierung, Service-Token-/Tenant-API-Key-Auth, Scope-Autorisierung, Rate-Limit-Bausteinen (429 + Retry-After), Error-Envelope, Request-ID-Korrelation, Idempotenz, API-Request-Logging (ApiKeyLog mit den richtigen Indizes), und im Worker eine generische signierte Webhook-Auslieferung mit Backoff (deliver-webhook, 5 Versuche). Das deckt das Querschnitts-Gerüst der drei Milestones zu großen Teilen ab.
ABER: Die fachliche Substanz von M34 fehlt komplett. Die existierenden API-Endpoints sind generisch (/v1/entities/:entity/rows, EntityBuilder-Rows) — es gibt keine typisierten HiveGrid-Read-Endpoints (/api/v1/contacts, deals, projects, leads …), weil die HiveGrid-Domänenentitäten selbst noch nicht existieren (app.prisma ist leerer Platzhalter). Cursor-Pagination, Sparse Fieldsets, Embed-Relations und Scope-basiertes Field-Redact sind nicht vorhanden (nur page/pageSize-Pagination). Das gesamte M34-Datenmodell (WebhookSubscription, WebhookDelivery, WebhookDeliveryAttempt, IncomingWebhookIngestion, IntegrationApiRequest) existiert nicht in der Boilerplate. Das vorhandene Webhook-Modell ist Event/EventWebhookAttempt (Legacy) bzw. form-submission-gebunden — kein Subscription-Modell mit Event-Types, Auto-Disable und HMAC-Secret-pro-Subscription. Swagger-UI mit „Try it out" fehlt (nur Roh-JSON-Viewer unter /v1/docs).
Übernehmbar aus Boilerplate
| Boilerplate-Pfad | Was | Wie nutzbar |
|---|---|---|
apps/api/src/app.ts |
Hono-App-Komposition (new Hono<ApiEnv>()), Middleware-Kette, register*Routes(app)-Muster, listener-frei für Tests |
1:1 (App-Host) — neue HiveGrid-Read-Routes via eigenes registerHiveGridReadRoutes(app) einhängen |
apps/api/src/server.ts |
HTTP-Listener-Entry (API_PORT/PORT/3001) |
1:1 |
apps/api/src/openapi.ts (+ .test.ts) |
OpenAPI-Contract-Bausteine: Error-Envelope-Schema (code/message/requestId), Rate-Limit-429-Response mit Retry-After, apiErrorResponse()-Helper, createOpenApiDocument() |
als-vorlage / anpassen — Envelope 1:1; HiveGrid-Endpoint-Schemas (contacts/deals/…) müssen ergänzt werden |
apps/api/src/routes/system-routes.ts |
GET /v1/openapi.json + GET /v1/docs (Doku-Seite) |
anpassen — /openapi.json 1:1; /v1/docs ist NUR Roh-JSON-Viewer → für „Try it out" durch echte Swagger-UI (swagger-ui-dist) ersetzen |
apps/api/src/auth.ts |
extractBearerToken, extractApiKeyFromRequest, isValidServiceToken, createServicePrincipal, Typ ServicePrincipal |
1:1 — Bearer/Key-Extraktion direkt nutzbar; Format Authorization: Bearer hg_<...> deckungsgleich mit Spec B.1 |
apps/api/src/auth/scope.ts (+ .test.ts) |
authorizeApiScope(), resolveApiTenantScope(), deny-Reasons (api_key_inactive/expired/tenant_deactivated/tenant_mismatch/…), ApiScopedPrincipal-Union |
als-vorlage — Tenant-Scope-Resolution + Key-Lifecycle 1:1; HiveGrid-Scopes (read_only/read_full/webhook_inbound/automation_trigger) müssen ergänzt werden (BP-Scopes sind read/create/update/delete pro Entity) |
apps/api/src/auth/permission.ts, login-rate-limit.ts |
Permission-Wiring + Token-/Window-Rate-Limit-Logik (IP-/Account-Scope, retryAfterSeconds) |
als-vorlage — Rate-Limit-Algorithmus + 429-Shape übernehmen, auf Per-Key/Per-Tenant/Per-Slug erweitern (Redis-Counter) |
apps/api/src/routes/shared.ts |
createLoginRateLimitResponse() (429 + Retry-After + rate_limited-Envelope), authenticateServiceCredentialRequest(), JSON-Body-Parsing, Idempotenz-Helfer |
als-vorlage — 429-Antwort-Shape und Auth-Helfer direkt als Muster |
apps/api/src/middleware/error-envelope.ts |
apiError(c, status, code, message), createErrorHandler/createNotFoundHandler |
1:1 — deckt M34 Antwort-Format {error:{code,message,details}} (34.1.7) ab |
apps/api/src/middleware/request-id.ts, audit-context.ts, body-limit.ts, metrics.ts, sentryTracing.ts |
Standard-API-Middleware-Stack (Request-Korrelation, Audit-Kontext, Body-Limit, Metrik, Tracing) | 1:1 |
apps/api/src/route-access-inventory.ts (+ .test.ts) |
routeAccessInventory + 11 Auth-Klassen inkl. tenant-api-key, service-or-tenant-api-key, webhook-signature, optional-api-key-validation, public-docs |
als-vorlage — neue HiveGrid-Routes müssen hier mit Auth-Klasse eingetragen werden (CI-Gate verify:api-inventory/verify:openapi-security) |
apps/api/src/idempotency/store.ts, shared-store-client.ts |
HTTP-Idempotenz-Cache (für Webhook-Empfang + Event-Ingestion-Idempotenz) | 1:1 |
apps/api/src/usage/usage-route.ts |
Vollständiger API-Key-Request-Flow als Referenz: Key auflösen → ApiKeyLog anlegen → Tenant-Deactivated/Expired/Inactive prüfen → Usage/402 → Log-Status setzen |
als-vorlage — exaktes Muster für „Loader prüft Key-Existenz, Scope, Tenant, Aktiv-Status" (Spec B) + Request-Logging |
apps/api/src/webhooks/mailersend-signature.ts (+ .test.ts) |
HMAC-SHA256-Signatur-Verifikation mit timingSafeEqual (Constant-Time-Compare), Timestamp-Toleranz, typisierte Failure-Reasons |
als-vorlage — direkt das Muster für M34 eingehende X-HiveGrid-Signature: sha256=<hex>-Verifikation (Spec B.2) |
packages/marketing-outbound/src/adapters/custom-webhook.ts |
Ausgehender Webhook-POST mit X-Marketing-Signature: sha256=<hmac(body)>, SSRF-Guard (adapterFetch, HTTPS-only, kein RFC1918) |
als-vorlage — exaktes Muster für M34 ausgehende HMAC-signierte Deliveries (Spec E, Signatur + SSRF-Schutz) |
apps/worker/src/jobs/form-submission-webhook-delivery.ts (+ .test.ts) |
Outbox→Webhook-Delivery-Bridge auf deliver-webhook-Job, Tenant-Mismatch-Validierung |
als-vorlage — Muster für „BullMQ-Job pro Webhook-Versand" (M163-Hinweis) |
packages/queue/src/payloads.ts → WebhookDeliveryJob |
Typisierter webhook.deliver-Job (url/method/headers/body/eventName/metadata) |
1:1 — direkt für ausgehende Webhook-Deliveries |
packages/queue/src/producers.ts (buildRetryJobOptions) |
deliver-webhook: 5 Versuche, exponentielles Backoff + Idempotenz via buildIdempotentJobId() |
anpassen — 5x deckt Spec E ab; Backoff-Stufen (1m/5m/30m/2h/12h) müssen exakt gesetzt werden (BP-Default ist delay: 2_000 exponentiell) |
apps/api/src/webhookDeliveryQueue.ts |
enqueueJob()-Wrapper für Webhook-Auslieferung mit Idempotenz-Key + TenantId |
als-vorlage |
packages/db/src/event-webhook-attempts.server.ts |
Repository-Muster für Webhook-Attempt-Persistenz (tenant-scoped find/update) | als-vorlage — Muster für neue WebhookDelivery/WebhookDeliveryAttempt-Repos |
packages/db/src/api-keys.server.ts |
API-Key-Repository: findLegacyApiKeyByKey, Key-Hashing (keyHash/keyPrefix/keyLastFour), ApiKeyLog-CRUD, Usage-Aggregation |
als-vorlage / anpassen — Key-Hashing + Logging 1:1; HiveGrid-Scope-Spalte ergänzen (BP hat per-Entity-Rechte, kein Scope-Enum) |
packages/db/src/outbox.ts (publishOutboxEvent) + apps/worker/src/outbox/event-router.ts |
Transaktionales Outbox + Event-Routing (webhook.-Prefix → outbox.webhook-Queue, longest-prefix-wins) |
1:1 — für ausgehende Webhook-Events (contact.created etc.) als Dispatch-Mechanismus |
packages/db/src/audit-events.server.ts + AuditEvent (Hash-Kette) |
Auditierung von Key-Anlage, Webhook-Setup, Inbound-Events (DoD C.13, AV #6) | 1:1 |
apps/api/Dockerfile-Target + .gitea/workflows/ci.yml api-Job |
Build-/CI-Fundament: verify:openapi-security → typecheck → vitest apps/api/src → build |
1:1 |
infra/grafana/api-latency.json + infra/prometheus/ |
API-Latenz-/Queue-Health-Dashboards (Metering-Vorbereitung Phase 2) | 1:1 |
Vorhandene Daten-Modelle (Prisma)
Alle in /Users/raffaelsheikh/Documents/GitHub/boilerplate/prisma/schema/.
| Modell | Datei | Deckt M34-Bedarf ab? |
|---|---|---|
ApiKey |
core.prisma (Z. 100-126) |
Teilweise. Gehashte Keys (keyHash/keyPrefix/keyLastFour/keyVersion), active, expires, @@unique([tenantId, alias]). Fehlt: Scope-Spalte (BP nutzt ApiKeyEntity-Join mit create/read/update/delete pro Entity, NICHT die M34-Scopes read_only/read_full/webhook_inbound/automation_trigger) und Per-Key-Rate-Limit-Override-Feld. Spec B verweist explizit auf „API-Key-Verwaltung aus M27 Block E" → Scope-Modell gehört zu M27. |
ApiKeyLog |
core.prisma (Z. 128-151) |
Gut. Deckt M34 IntegrationApiRequest weitgehend: endpoint, method, params, status, duration, error, ip, tenantId, apiKeyId + passende Indizes inkl. (apiKeyId, tenantId, createdAt DESC) und (tenantId, createdAt). Fehlt ggü. Spec A: route_pattern (statt voll-aufgelöstem endpoint), query_params als JSONB (BP: params String), request_body_size_bytes/response_size_bytes, user_agent, Retention-Logik (90/180 Tage). Entweder ApiKeyLog erweitern oder eigenes IntegrationApiRequest bauen. |
Event + EventWebhookAttempt |
core.prisma (Z. 152-178) |
Schwach / Legacy. Event (name/data/resource) + EventWebhookAttempt (endpoint/success/status/message/body). KEIN Subscription-Modell, keine Event-Types-Liste, kein HMAC-Secret-pro-Subscription, kein Auto-Disable-Counter, kein Idempotenz-event_id. Nur als grobe Vorlage für WebhookDelivery/WebhookDeliveryAttempt tauglich. |
EntityWebhook + EntityWebhookLog |
entityBuilder.prisma (Z. 206-224) |
Nicht passend. Pro-Entity-Action-Webhook im EntityBuilder (entityId/action/method/endpoint). Kein Tenant-Subscription-Modell, kein Secret, kein Status-Enum/Retry. |
Form + FormSubmission |
formWidget.prisma |
Teil-Vorlage für eingehenden Lead-Push. Form (key @unique, definition JSON, fieldMapping, allowedOrigins[], Anti-Spam-config), FormSubmission (payload JSON, ipHash, status received/processed/failed/spam). Deckt das Slug-/Mapping-/Status-Muster von M34 IncomingWebhookIngestion konzeptionell ab, ist aber Form-Widget-gebunden, nicht Webhook-Slug-gebunden mit 3 Auth-Methoden. |
App / DataConnection / AppSyncRun |
appsEngine.prisma |
Randnah. Externe Datensync-Engine (rest-api/oauth2/webhook, Rollback). Nicht für M34-Public-API, eher Phase-2-Inbound-Connectors. |
Lead / LeadEvent |
marketingLeads.prisma |
Beachten — Konflikt. BP-Lead ist operator-scoped (KEIN tenantId), M34 Lead-Push erzeugt tenant-scoped Leads (Modul 2/M042). M34-Webhook muss auf den HiveGrid-CRM-Lead schreiben, nicht auf diesen Marketing-Lead. |
OutboxEvent |
outbox.prisma |
1:1. Transaktionaler Event-Auslöser für ausgehende Webhooks. |
IdempotencyKey |
core.prisma |
1:1. HTTP-Idempotenz für Webhook-Empfang/Event-Ingestion. |
FeatureFlag |
featureFlags.prisma |
1:1. DoD-Pflicht „Feature-Flag-fähig" (M006). |
Komplett fehlende M34-Modelle (in prisma/schema/ NICHT vorhanden — verifiziert)
WebhookSubscription, WebhookDelivery, WebhookDeliveryAttempt, IncomingWebhookIngestion, IntegrationApiRequest — keiner dieser Namen existiert im Schema. Anlageort: prisma/schema/app.prisma (aktuell leerer Platzhalter // custom database models).
Lücken / komplett neu zu bauen
- HiveGrid-Domänenentitäten als Voraussetzung. Die typisierten Read-Endpoints (
/api/v1/contacts|companies|deals|projects|tasks|leads) setzen voraus, dass diese Module (M042-M074) existieren.app.prismaist leer → es gibt nichts zum Lesen. M161 hängt hart an den Daten-Modulen (PLAN: „Vorbedingung Daten-Module M042-M074"). - Typisierte Read-API-Endpoints. BP bietet nur generische EntityBuilder-Row-Endpoints (
/v1/entities/:entity/rows). Die HiveGrid-Endpoints müssen neu gebaut werden (eigenes Route-Modulregister*Routes). - Cursor-Pagination. BP hat nur
page/pageSize(offset-basiert,pagination/rows.ts). Spec verlangt opaque base64-Cursor +meta.next_cursor→ neu. - Sparse Fieldsets (
fields=), Embed-Relations (embed=, Tiefe 1), erweiterte Filter (Multi-Value-OR, Date-Range*_gte/_lte,custom.{key},q-Volltext), Sortierung (sort=-feld) — keiner dieser Mechanismen existiert generisch. Neu. - M34-Scope-Modell (
read_only/read_full/webhook_inbound/automation_trigger) + Field-Redact sensibler Felder beiread_only(nicht 403, sondern Feld-Auslassung). BP-Scope ist entity-CRUD-basiert. Neu (kommt fachlich aus M27 Block E, hier konkretisiert). - Rate-Limiting Per-Key / Per-Tenant / Per-Slug mit Token-Bucket + Redis-Counter und Headern
X-RateLimit-Limit/Remaining/Reset. BP hat nur Login-Rate-Limit (IP/Account) undRetry-Afterim 429. Die generischenX-RateLimit-*-Header existieren NICHT (nur in OpenAPI-Schema beschrieben). Neu — Algorithmus vonlogin-rate-limit.tsals Vorlage. IntegrationApiRequest-Modell (oder Erweiterung vonApiKeyLog) mitroute_pattern,query_paramsJSONB, Größen-Feldern,user_agent+ Retention-Job (90/180 Tage). Neu.- Webhook-Subscription-Modell + UI.
WebhookSubscription(Event-Types[], HMAC-Secret, Auto-Disable-Counter, Stats),WebhookDelivery(Status-Enum pending/delivered/failed/exhausted/discarded),WebhookDeliveryAttempt. Komplett neu. Inkl. Subscription-Verwaltungs-UI unter/{tenant}/admin/webhooks/, Delivery-Stats-View, Test-Funktion (test.ping), Replay (Phase-2-Out-of-Scope). Auto-Disable nach 10 konsekutiven Fehlern +notify()(M011). - Eingehende Webhook-Slugs.
IncomingWebhookIngestion+ Slug-Verwaltung mit 3 Auth-Methoden (API-Key / HMAC / URL-Token mit Klartext-Warnung),payload_mapping, Auto-Lead-Anlage + Duplikat-Check (Email-Match → Activity statt neuem Lead), Default-Tags/Assignee. HMAC-Verify-Muster ausmailersend-signature.tsübernehmbar, Rest neu. - Backoff-Stufen exakt. BP
deliver-webhook= 5 Versuche exponentielldelay 2000— Spec verlangt feste Stufen 1m/5m/30m/2h/12h. Job-Options anpassen/custom-Backoff. - Event-Type-Katalog + Payload-Envelope (
event_id/event_type/tenant_id/occurred_at/data/previous_data) mit Mapping auf die ~30 HiveGrid-Events (contact.created…call.missed). Neu — Trigger-Punkte in jedem Quell-Modul. - Allgemeine Event-Ingestion
POST /api/v1/integrations/events→ Automation-Engine-Trigger (M075/M017external_event). Verdrahtung neu. - Swagger-UI mit „Try it out". BP
/v1/docsist nur ein Roh-JSON-<pre>-Viewer (kein interaktives Swagger-UI, kein API-Key-Eingabefeld).swagger-ui-disteinbinden (M161-Hinweis nennt es als Boilerplate-Dep — als Paket vorhanden, aber NICHT als Route verdrahtet). Neu. - Versionierung/Deprecation-Header (
Sunset,Deprecation: true, Stability-Tags). Nicht vorhanden. Neu (leichtgewichtig). - 402-bei-Frozen-Tenant für Schreib (Spec 34.1.10) — da Phase 1 read-only ist, primär ein Webhook-Inbound-403-Pfad (frozen → eingehende Webhooks verworfen). BP-
usage-route.tshat das 402-Muster bereits (payment_required) als Vorlage. - UI-Layer komplett (DoD A-E): View-Models, Adapter, Sidebar-Eintrag (
packages/ui-hivegrid/src/app-shell/sidebar.config.ts), Routes, i18n-Keys, Empty/Loading-States, Visual-Regression, Hot-Swap, Smoke-Tests. Reihenfolge & Wiring perdocs/INTEGRATION-CONTRACTS.md. Nichts davon HiveGrid-spezifisch vorhanden.
Empfohlenes Vorgehen
Voraussetzung prüfen: M27 (API-Keys + Scope-Definition) und die Daten-Module M042-M074 müssen stehen — sonst gibt es keine Scopes und keine Entitäten zum Lesen. Ohne diese ist M161 nicht sinnvoll baubar.
M161 — Read-API + OpenAPI (zuerst aus BP übernehmen)
- App-Host 1:1 nutzen:
apps/api/src/app.ts+ Middleware-Stack +error-envelope.ts+ Request-ID stehen. Nur ein neues Route-ModulregisterHiveGridReadRoutes(app)registrieren. - Auth-Flow als Vorlage:
usage-route.ts+auth.ts+auth/scope.tsals Blueprint für „Key auflösen → loggen → Tenant/Active/Expired prüfen".extractBearerToken/extractApiKeyFromRequest1:1. - NEU bauen: typisierte Endpoints, Cursor-Pagination, Filter/Sort/Fields/Embed-Parser, Response-Builder
{data, meta}. - OpenAPI:
openapi.ts-Envelope +createOpenApiDocument()erweitern;swagger-ui-distals echte Route ergänzen (BP/v1/docsersetzen). - Route-Access-Inventar (
route-access-inventory.ts) für jede neue Route pflegen → sonst CI-Gateverify:openapi-security/verify:api-inventoryrot.
M162 — Scopes + Field-Redact + Rate-Limit
- Scope-Modell an
ApiKeykoppeln (kommt aus M27);authorizeApiScope-Muster fürread_only/read_fulladaptieren. - Field-Redact im Response-Builder (sensible Felder bei
read_onlyweglassen) — neu, im VM/Serializer. - Rate-Limit: Algorithmus aus
auth/login-rate-limit.ts+ Redis-Counter (@toolctrl/queueresolveRedisConnectionOptions) auf Per-Key/Per-Tenant/Per-Slug ausweiten;X-RateLimit-*-Header + 429-Shape ausroutes/shared.tsübernehmen. - Request-Logging:
ApiKeyLogerweitern oderIntegrationApiRequestneu + Retention-Cron (Muster:apps/worker/src/jobs/audit-retention.ts).
M163 — Webhooks (signiert)
- Neue Prisma-Modelle in
app.prisma:WebhookSubscription,WebhookDelivery,WebhookDeliveryAttempt,IncomingWebhookIngestion(Forward-Only-Migration mit Inverse-SQL, DoD F.28). DSGVO: neue User-FKs inpackages/db/src/dsgvo/user-fk-map.tseintragen (sonst CI rot). - Eingehende Webhooks: HMAC-Verify aus
webhooks/mailersend-signature.ts(Constant-Time) übernehmen; Slug-/Mapping-/Auth-Logik neu; Idempotenz viaIdempotencyKey; Auto-Lead-Anlage auf HiveGrid-CRM-Lead (nicht Marketing-Lead) + Activity vialogActivity. - Ausgehende Webhooks: Outbox (
publishOutboxEvent) →webhook.-Prefix-Routing →deliver-webhook-Job (5 Versuche). HMAC-Sign-/SSRF-Muster ausmarketing-outbound/custom-webhook.ts. Backoff-Stufen auf 1m/5m/30m/2h/12h anpassen. Auto-Disable nach 10 Fehlern +notify()(M011). Delivery-/Attempt-Persistenz à laevent-webhook-attempts.server.ts. - Event-Ingestion
POST /v1/integrations/events→ Automation-Trigger (M075/M017). - UI für Subscriptions/Slugs/Logs (DoD A-E) zuletzt: Subframe → Adapter → View-Model → Route → Sidebar + Permission-Gate + i18n + Tests.