# 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:**
```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 <token>
```

**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."*
