diff --git a/docs/CLAUDE_BRIEFING.docx b/docs/CLAUDE_BRIEFING.docx new file mode 100644 index 0000000..bd7b3fa Binary files /dev/null and b/docs/CLAUDE_BRIEFING.docx differ diff --git a/docs/CRM_HANDOFF.md b/docs/CRM_HANDOFF.md new file mode 100644 index 0000000..9808039 --- /dev/null +++ b/docs/CRM_HANDOFF.md @@ -0,0 +1,505 @@ +# 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."* diff --git a/docs/INSIGHT_Konzept_v1.0.docx b/docs/INSIGHT_Konzept_v1.0.docx new file mode 100644 index 0000000..6d2fe60 Binary files /dev/null and b/docs/INSIGHT_Konzept_v1.0.docx differ