# 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 ` Header. Der Token wurde vom Core-Service ausgestellt (RS256). **JWT-Payload:** ```json { "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: ```yaml # In docker-compose.crm.yml volumes: - ./keys/jwt-public.pem:/app/keys/jwt-public.pem:ro ``` **NestJS-Implementierung:** ```typescript // Passport JWT Strategy (wie im Core-Service) // Algorithmus: RS256 // Public Key: aus /app/keys/jwt-public.pem // Header: Authorization: Bearer ``` **Optional — Token-Revocation pruefen:** ```typescript // 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: ```sql -- 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` ```prisma 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: ```yaml # 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) ```typescript // 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:** ```typescript // 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 ```bash # 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."*