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>
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 anhaengendocs/— 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(keinLISTEN/NOTIFY, keinSET)
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-Revocationplatform_*— Core-Settingsfavicon:*— Favicon-Cachesso:state:*— SSO CSRFrefresh_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 impublicSchema anlegen tenantIdals Pflichtfeld in jeder Tabelle (Multi-Tenancy)createdBy/updatedByals 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:
- Neuer CI-Job
crm-service(analog zucore-service) - Docker-Image:
git.xinion.lan/gitadmin/insight-crm:TAG - Deploy-Job erweitert um CRM-Container
10. Konventionen
10.1 Git
- Branch:
feature/crm-service(vondevelopabzweigen) - Commits: Conventional Commits mit
feat(crm):,fix(crm):, etc. - Kein Push auf
main— nur ueber Merge vondevelop
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
- Arbeite NUR in
packages/crm-service/unddocker-compose.crm.yml - Aendere NICHTS am Core-Service, Frontend oder der Infrastruktur
- Eigenes Prisma-Schema mit eigenem Client-Output
- Eigener Docker-Container auf Port 3100
- JWT-Validierung mit dem shared Public Key (RS256)
- Multi-Tenancy — jede Query filtert nach
tenantIdaus dem JWT - Redis-Keys mit Prefix
app:crm: - Git-Branch:
feature/crm-servicevondevelop - Conventional Commits:
feat(crm):,fix(crm): - 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.mdunddocs/INTEGRATION.mdbevor du mit der Entwicklung startest."