INSIGHT-MVP/docs/CRM_HANDOFF.md
Thomas Reitz b18fe9376c docs: add CRM service handoff document for new dev session
Comprehensive briefing for a new Claude Code chat to build the CRM
container service, including: protected paths, architecture overview,
JWT validation, database schema conventions, Docker integration,
multi-tenancy rules, and quick-start guide.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:18:04 +01:00

14 KiB

CRM-Service Handoff — Briefing fuer neuen Claude Code Chat

Zweck: Dieses Dokument enthaelt alle technischen Informationen, damit ein neuer Claude Code Chat den CRM-Service im Monorepo entwickeln kann, ohne bestehende Funktionalitaet zu beschaedigen.

Stand: 2026-03-10 | Basis: INSIGHT MVP v1.0.0-alpha


1. Dein Auftrag

Entwickle den CRM-Service als eigenstaendiges Package unter packages/crm-service/ im bestehenden Monorepo. Der Service ist ein NestJS-Backend, das als eigener Docker-Container laeuft und sich in die INSIGHT-Plattform integriert.

Lese zuerst: docs/INTEGRATION.md — dort steht, wie Container-Apps sich in die Plattform integrieren.


2. Was du NICHT anfassen darfst

KRITISCH: Diese Dateien/Verzeichnisse gehoeren dem Core-Service. Aenderungen daran koennen die gesamte Plattform zerstoeren.

Pfad Grund
packages/core-service/** Bestehender Backend-Service (Auth, Users, Tenants)
packages/frontend/** Bestehende React-App (wird spaeter separat erweitert)
config/traefik/** Traefik-Konfiguration (nur lesen, nicht aendern)
.forgejo/workflows/** CI/CD Pipeline (wird spaeter fuer CRM erweitert)
docker-compose.yml Haupt-Compose (nur lesen — du erstellst ein Override)
.keys/** JWT-Schluessel und SSH-Keys
prisma/core.schema.prisma Core-Datenbank-Schema

Ausnahmen: Du darfst folgende Dateien ergaenzen (nicht ueberschreiben):

  • .env.example — Neue CRM-spezifische Variablen anhaengen
  • docs/ — Eigene Dokumentation hinzufuegen

3. Verzeichnisstruktur (Ziel)

INSIGHT-MVP/
├── packages/
│   ├── core-service/          ← NICHT ANFASSEN
│   ├── frontend/              ← NICHT ANFASSEN
│   └── crm-service/          ← DEIN BEREICH
│       ├── Dockerfile
│       ├── package.json
│       ├── tsconfig.json
│       ├── nest-cli.json
│       ├── .dockerignore
│       ├── prisma/
│       │   └── crm.schema.prisma    ← Eigenes Schema!
│       └── src/
│           ├── main.ts
│           ├── app.module.ts
│           ├── config/
│           ├── contacts/
│           ├── activities/
│           ├── pipelines/
│           └── ...
├── docker-compose.crm.yml    ← Docker Override fuer CRM
└── docs/
    └── CRM_HANDOFF.md        ← Diese Datei

4. Bestehende Architektur (Referenz)

4.1 Docker-Netzwerke

Netzwerk Typ Dein Zugriff
insight-web bridge JA — Pflicht fuer Traefik-Routing
insight-db internal JA — Fuer Datenbankzugriff
insight-cache internal JA — Fuer Redis-Zugriff

4.2 Verfuegbare Infrastruktur-Services

Service Hostname (Docker) Port Zugriff
PostgreSQL postgres 5432 Direkt (nur Migrationen)
PgBouncer pgbouncer 6432 Pooled (Runtime)
Redis redis 6379 Cache + Events
Core-API core 3000 REST-Aufrufe
Traefik traefik 80 Gateway

4.3 Datenbank-Verbindung

# Runtime (via PgBouncer — Connection Pooling)
DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@pgbouncer:6432/${DB_NAME}

# Migrationen (direkt — PgBouncer unterstuetzt kein DDL)
DATABASE_URL_DIRECT=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
  • DB_USER: insight (Standard)
  • DB_NAME: platform_core (gemeinsame DB)
  • Dein Schema: app_crm (eigenes Schema innerhalb der gleichen DB)
  • Pool-Mode: transaction (kein LISTEN/NOTIFY, kein SET)

4.4 Redis

Host: redis
Port: 6379
Passwort: optional (${REDIS_PASSWORD})

Key-Prefix fuer CRM: app:crm:*

Bereits belegte Prefixes (NICHT verwenden):

  • blocked_token:* — Token-Revocation
  • platform_* — Core-Settings
  • favicon:* — Favicon-Cache
  • sso:state:* — SSO CSRF
  • refresh_token_family:* — Token-Rotation

5. Authentifizierung & Autorisierung

5.1 JWT-Token-Architektur

Der CRM-Service empfaengt Requests mit einem Authorization: Bearer <token> Header. Der Token wurde vom Core-Service ausgestellt (RS256).

JWT-Payload:

{
  "sub": "user-uuid",
  "email": "user@example.com",
  "role": "USER | TENANT_ADMIN | PLATFORM_ADMIN",
  "tenantId": "tenant-uuid",
  "tenantSlug": "tenant_acme",
  "jti": "unique-token-id",
  "iat": 1710000000,
  "exp": 1710000900
}

5.2 Token-Validierung implementieren

Du brauchst den oeffentlichen JWT-Schluessel:

# In docker-compose.crm.yml
volumes:
  - ./keys/jwt-public.pem:/app/keys/jwt-public.pem:ro

NestJS-Implementierung:

// Passport JWT Strategy (wie im Core-Service)
// Algorithmus: RS256
// Public Key: aus /app/keys/jwt-public.pem
// Header: Authorization: Bearer <token>

Optional — Token-Revocation pruefen:

// Redis-Key: blocked_token:{jti}
// Wenn vorhanden → Token ist gesperrt → 401

5.3 Rollen

Rolle CRM-Zugriff
PLATFORM_ADMIN Voller Zugriff auf alle Mandanten
TENANT_ADMIN Voller Zugriff auf eigenen Mandanten
USER Lese-/Schreibzugriff auf eigene Daten

5.4 Multi-Tenancy

Jeder CRM-Request muss mandanten-bezogen sein!

Der tenantId kommt aus dem JWT-Payload. Alle Datenbankabfragen muessen nach tenantId gefiltert werden:

-- IMMER so:
SELECT * FROM app_crm.contacts WHERE tenant_id = :tenantId;

-- NIEMALS so:
SELECT * FROM app_crm.contacts;  -- Datenleck!

6. Datenbank-Schema (Empfehlung)

Verwende ein eigenes Prisma-Schema unter packages/crm-service/prisma/crm.schema.prisma.

Schema-Name: app_crm

generator client {
  provider = "prisma-client-js"
  output   = "../node_modules/.prisma/crm-client"
}

datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DATABASE_URL_DIRECT")
  schemas   = ["app_crm"]
}

Wichtig:

  • Eigener Prisma-Client-Output (crm-client), damit kein Konflikt mit dem Core-Client
  • Eigenes Schema app_crm — keine Tabellen im public Schema anlegen
  • tenantId als Pflichtfeld in jeder Tabelle (Multi-Tenancy)
  • createdBy / updatedBy als UUID fuer Audit-Trail (User-IDs aus Core)

6.1 Referenz: Bestehende Tenant-Schema-Entwuerfe

Im Core-Service existiert bereits ein Entwurf unter packages/core-service/prisma/tenant.schema.prisma mit Modellen fuer Contact und Activity. Diese dienen als Referenz — du kannst sie uebernehmen oder anpassen.

Contact-Modell (Referenz):

  • Typen: PERSON, ORGANIZATION
  • Felder: Name, Firma, Kontaktdaten, Adresse, Tags, Notizen
  • Tracking: createdBy, updatedBy (User-UUIDs)

Activity-Modell (Referenz):

  • Typen: NOTE, CALL, EMAIL, MEETING, TASK
  • Felder: Subject, Beschreibung, Zeitplanung, Status

7. Docker-Integration

7.1 docker-compose.crm.yml (Beispiel)

Erstelle diese Datei im Projekt-Root:

# docker-compose.crm.yml
# Start: docker compose -f docker-compose.yml -f docker-compose.crm.yml up -d

services:
  crm:
    build:
      context: ./packages/crm-service
      dockerfile: Dockerfile
      target: development
    container_name: insight-crm
    restart: unless-stopped
    environment:
      - NODE_ENV=${NODE_ENV:-development}
      - APP_PORT=3100
      - DATABASE_URL=postgresql://${DB_USER:-insight}:${DB_PASSWORD}@pgbouncer:6432/${DB_NAME:-platform_core}?schema=app_crm
      - DATABASE_URL_DIRECT=postgresql://${DB_USER:-insight}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-platform_core}?schema=app_crm
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=${REDIS_PASSWORD:-}
      - JWT_PUBLIC_KEY_PATH=/app/keys/jwt-public.pem
    volumes:
      - ./packages/crm-service:/app
      - /app/node_modules
      - ./.keys/jwt-public.pem:/app/keys/jwt-public.pem:ro
    networks:
      - insight-web
      - insight-db
      - insight-cache
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.crm.rule=PathPrefix(`/api/v1/crm`)"
      - "traefik.http.routers.crm.entrypoints=web"
      - "traefik.http.services.crm.loadbalancer.server.port=3100"
      - "traefik.http.routers.crm.middlewares=cors-headers@file,security-headers@file"
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3100/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s
    depends_on:
      pgbouncer:
        condition: service_healthy
      redis:
        condition: service_healthy

networks:
  insight-web:
    external: true
  insight-db:
    external: true
  insight-cache:
    external: true

7.2 Traefik-Route

Route-Prefix Service Port
/api/v1/crm/* crm 3100

Bereits belegte Routen:

  • /api/* → core-service:3000
  • /health → core-service:3000
  • /* → frontend:8080 (Catch-all, Priority 1)

Wichtig: Die CRM-Route /api/v1/crm hat hoehere Prioritaet als der Core-Catch-all /api, weil sie spezifischer ist. Traefik matched die laengste Prefix-Uebereinstimmung zuerst.

7.3 Port-Konvention

Service Port
Core-Service 3000
CRM-Service 3100
Frontend 8080

8. NestJS-Konfiguration (Orientierung am Core-Service)

8.1 Tech-Stack (identisch zum Core)

Komponente Version Zweck
NestJS >= 10 Framework
TypeScript strict Sprache
Prisma >= 6 ORM
ioredis >= 5 Redis-Client
passport-jwt latest JWT-Validierung
class-validator latest DTO-Validierung
class-transformer latest Typ-Transformation

8.2 main.ts Konfiguration (kopiere das Pattern)

// Wichtige Einstellungen (gleich wie Core):
// - helmet() fuer Security-Headers
// - cookieParser() fuer Cookies
// - JSON Body Limit: 12MB
// - Global Prefix: api/v1/crm (ACHTUNG: nicht api/v1 — das ist der Core!)
// - Global ValidationPipe (whitelist, forbidNonWhitelisted, transform)
// - CORS mit credentials: true
// - Port: 3100

ACHTUNG beim Global Prefix:

// RICHTIG:
app.setGlobalPrefix('api/v1/crm', { exclude: ['/health'] });

// FALSCH (wuerde Core-Routes ueberlagern):
app.setGlobalPrefix('api/v1');

8.3 Swagger

Stelle Swagger unter /api/v1/crm/docs bereit (nur Development).


9. CI/CD (spaeter)

Die CI/CD Pipeline wird spaeter separat erweitert. Fokussiere dich auf die Entwicklung. Die Pipeline-Erweiterung beinhaltet:

  1. Neuer CI-Job crm-service (analog zu core-service)
  2. Docker-Image: git.xinion.lan/gitadmin/insight-crm:TAG
  3. Deploy-Job erweitert um CRM-Container

10. Konventionen

10.1 Git

  • Branch: feature/crm-service (von develop abzweigen)
  • Commits: Conventional Commits mit feat(crm):, fix(crm):, etc.
  • Kein Push auf main — nur ueber Merge von develop

10.2 Code-Style

  • Strict TypeScript — kein any
  • ESLint + Prettier (gleiche Config wie Core)
  • DTOs mit class-validator fuer alle Inputs
  • Guards fuer Authentifizierung und Autorisierung

10.3 Datenbank

  • Schema: app_crm
  • Tabellen-Prefix: keiner (Schema ist schon der Namespace)
  • Migrations: npx prisma migrate dev --schema=prisma/crm.schema.prisma
  • Seed: npx ts-node prisma/seed.ts

10.4 Redis-Keys

app:crm:{feature}:{id}

Beispiele:

app:crm:pipeline:config:tenant_acme
app:crm:cache:contacts:tenant_acme:page1

11. API-Design (Empfehlung)

Alle CRM-Endpunkte unter /api/v1/crm/:

# Kontakte
GET    /api/v1/crm/contacts          — Liste (paginiert, filterbar)
POST   /api/v1/crm/contacts          — Erstellen
GET    /api/v1/crm/contacts/:id      — Detail
PATCH  /api/v1/crm/contacts/:id      — Aktualisieren
DELETE /api/v1/crm/contacts/:id      — Loeschen

# Aktivitaeten
GET    /api/v1/crm/activities         — Liste
POST   /api/v1/crm/activities         — Erstellen
GET    /api/v1/crm/activities/:id     — Detail
PATCH  /api/v1/crm/activities/:id     — Aktualisieren
DELETE /api/v1/crm/activities/:id     — Loeschen

# Pipelines (Sales-Pipeline)
GET    /api/v1/crm/pipelines          — Liste
POST   /api/v1/crm/pipelines          — Erstellen
...

# Deals
GET    /api/v1/crm/deals              — Liste
POST   /api/v1/crm/deals              — Erstellen
...

# Health
GET    /health                         — Health-Check (public)

12. Zusammenfassung der Regeln

  1. Arbeite NUR in packages/crm-service/ und docker-compose.crm.yml
  2. Aendere NICHTS am Core-Service, Frontend oder der Infrastruktur
  3. Eigenes Prisma-Schema mit eigenem Client-Output
  4. Eigener Docker-Container auf Port 3100
  5. JWT-Validierung mit dem shared Public Key (RS256)
  6. Multi-Tenancy — jede Query filtert nach tenantId aus dem JWT
  7. Redis-Keys mit Prefix app:crm:
  8. Git-Branch: feature/crm-service von develop
  9. Conventional Commits: feat(crm):, fix(crm):
  10. Strict TypeScript — kein any, volle Validierung

13. Schnellstart

# 1. Branch erstellen
git checkout develop
git checkout -b feature/crm-service

# 2. Package anlegen
mkdir -p packages/crm-service
cd packages/crm-service
npm init -y

# 3. NestJS installieren
npm install @nestjs/common @nestjs/core @nestjs/config @nestjs/platform-express
npm install @nestjs/passport @nestjs/jwt passport passport-jwt
npm install @prisma/client ioredis class-validator class-transformer
npm install helmet cookie-parser
npm install -D typescript @types/node @nestjs/cli prisma
npm install -D @types/passport-jwt @types/cookie-parser

# 4. Prisma initialisieren
npx prisma init --schema=prisma/crm.schema.prisma

# 5. Schema erstellen (app_crm)
# ... (siehe Abschnitt 6)

# 6. Migration ausfuehren
npx prisma migrate dev --schema=prisma/crm.schema.prisma --name init

# 7. Entwickeln
npm run start:dev

Fuer den neuen Chat: Oeffne dieses Dokument zu Beginn mit: "Lies bitte docs/CRM_HANDOFF.md und docs/INTEGRATION.md bevor du mit der Entwicklung startest."