# CRM-Service - Zusammenfassung ## Stand: 2026-03-10 ### Was wurde erstellt Der CRM-Service als eigenstaendiges NestJS-Package unter `packages/crm-service/`. ### Struktur ``` packages/crm-service/ package.json — Dependencies (NestJS 10, Prisma, Passport, ioredis) tsconfig.json — Strict TypeScript nest-cli.json — NestJS CLI Config Dockerfile — Multi-Stage (base, deps, development, build, production) .dockerignore — Excludes prisma/ crm.schema.prisma — Eigenes Schema (app_crm) mit eigenem Client-Output src/ main.ts — Bootstrap (Port 3100, Prefix: api/v1/crm, Swagger) app.module.ts — Root Module mit globalem JwtAuthGuard + ExceptionFilter config/ — Umgebungsvariablen-Validierung prisma/ — CrmPrismaService (eigener Client) redis/ — RedisService (Token-Blocklist, Cache) auth/ — JWT Strategy (RS256), JwtAuthGuard, RolesGuard, TenantGuard common/ — Decorators (@Public, @Roles, @CurrentUser), Pagination, ExceptionFilter companies/ — CRUD: Unternehmen (uebergeordnete Entity) contacts/ — CRUD: Kontakte (PERSON, ORGANIZATION) mit Company-Verknuepfung activities/ — CRUD: Aktivitaeten (NOTE, CALL, EMAIL, MEETING, TASK) pipelines/ — CRUD: Sales-Pipelines mit Stages (inkl. Stage-Update) deals/ — CRUD: Vorgaenge mit Pipeline/Stage/Contact/Company-Zuordnung ``` ### Datenbank-Modelle (app_crm Schema) - **Company** — Unternehmen mit Branche, Adresse, Tags, Audit-Trail. Eltern-Entity fuer Contacts und Deals. - **Contact** — Kontakte (Person/Organisation) mit optionaler Company-Zuordnung (companyId, position) - **Activity** — Aktivitaeten verknuepft mit Kontakten - **Pipeline** — Konfigurierbare Sales-Pipelines pro Tenant - **PipelineStage** — Stufen innerhalb einer Pipeline (Name, Farbe, Reihenfolge editierbar) - **Deal** — Vorgaenge mit Wert, Status, Pipeline/Stage/Contact/Company-Zuordnung ### Entity-Beziehungen ``` Company (1) --< (n) Contact — companyId (optional, SetNull bei Loeschung) Company (1) --< (n) Deal — companyId (optional, SetNull bei Loeschung) Contact (1) --< (n) Activity — contactId (Cascade bei Loeschung) Contact (1) --< (n) Deal — contactId (optional, SetNull bei Loeschung) Pipeline (1) --< (n) PipelineStage — pipelineId (Cascade bei Loeschung) Pipeline (1) --< (n) Deal — pipelineId (Cascade bei Loeschung) PipelineStage (1) --< (n) Deal — stageId ``` ### API-Endpunkte | Methode | Pfad | Beschreibung | |---------|------|-------------| | GET/POST | /api/v1/crm/companies | Liste / Erstellen | | GET/PATCH/DELETE | /api/v1/crm/companies/:id | Detail / Update / Delete | | GET/POST | /api/v1/crm/contacts | Liste / Erstellen | | GET/PATCH/DELETE | /api/v1/crm/contacts/:id | Detail / Update / Delete | | GET/POST | /api/v1/crm/activities | Liste / Erstellen | | GET/PATCH/DELETE | /api/v1/crm/activities/:id | Detail / Update / Delete | | GET/POST | /api/v1/crm/pipelines | Liste / Erstellen | | GET/PATCH/DELETE | /api/v1/crm/pipelines/:id | Detail / Update / Delete | | POST/DELETE | /api/v1/crm/pipelines/:id/stages | Stage hinzufuegen/entfernen | | PATCH | /api/v1/crm/pipelines/:id/stages/:stageId | Stage bearbeiten (Name, Farbe, Reihenfolge) | | GET/POST | /api/v1/crm/deals | Liste / Erstellen | | GET/PATCH/DELETE | /api/v1/crm/deals/:id | Detail / Update / Delete | | GET | /health | Health-Check (public) | ### Docker-Integration - `docker-compose.crm.yml` im Projekt-Root - Port: 3100 - Netzwerke: insight-web, insight-db, insight-cache - Traefik HTTP-Route: `Host(172.20.10.59) && PathPrefix(/api/v1/crm)` mit Priority 100 - Traefik HTTPS-Route: `crm-secure` mit `entrypoints=websecure`, `tls=true`, Priority 100 - JWT Public Key als Read-Only Volume (.keys/jwt-public.pem) - Direkte PostgreSQL-Verbindung (PgBouncer unterstuetzt kein search_path fuer Schema-Auswahl) ### Sicherheit - JWT RS256 Validierung mit shared Public Key - Token-Revocation via Redis (blocked:{jti}) - Multi-Tenancy: Alle Queries filtern nach tenantId - TenantGuard sichert mandantenbezogenen Zugriff - Globaler ValidationPipe (whitelist + forbidNonWhitelisted) - Strict TypeScript, kein `any` - 401 bei fehlendem/ungueltigem Token ### Deployment-Status **Erfolgreich deployed auf insight-dev-01 (172.20.10.59) am 2026-03-10** - Container: insight-crm (Development-Mode) - Prisma Migrationen angewendet: - `20260310163211_init` — Initiales Schema (Contact, Activity, Pipeline, PipelineStage, Deal) - `20260310183117_add_companies` — Company-Entity, Contact.companyId/position, Deal.companyId - Alle API-Endpunkte getestet und funktionsfaehig - Traefik-Routing aktiv (HTTP + HTTPS): http(s)://172.20.10.59/api/v1/crm/* - Swagger-Docs: http://172.20.10.59/api/v1/crm/docs/ ### Getestete Endpunkte | Test | Ergebnis | |------|----------| | POST /companies (Erstellen mit allen Feldern) | 200 OK, UUID + _count korrekt | | GET /companies (Liste) | 200 OK, pagination + _count korrekt | | GET /companies/:id (Detail) | 200 OK, contacts[] + deals[] + _count | | PATCH /companies/:id (Update) | 200 OK, updatedBy + tags korrekt | | GET /companies?search=Xinion | 200 OK, Suche funktioniert | | POST /contacts (mit companyId + position) | 201 Created, Company-Verknuepfung korrekt | | GET /contacts/:id (mit Company) | 200 OK, company-Objekt enthalten | | POST /activities (Notiz) | 201 Created, contactId verknuepft | | POST /pipelines (mit 4 Stages) | 201 Created, Stages korrekt | | PATCH /pipelines/:id/stages/:stageId | 200 OK, Stage-Update korrekt | | POST /deals (mit companyId + contactId) | 200 OK, Company + Contact verknuepft | | GET /deals/:id (mit Company) | 200 OK, company + pipeline.stages enthalten | | GET /deals?companyId=... | 200 OK, Filter nach Company funktioniert | | PATCH /deals/:id (WON) | 200 OK, closedAt automatisch gesetzt | | GET /contacts ohne Token | 401 Unauthorized | | Validierung (falsche Felder) | 400 Bad Request, Details korrekt | ### Bekannte Einschraenkungen - PgBouncer kann nicht genutzt werden (search_path nicht kompatibel mit transaction pooling) ### Naechste Schritte 1. Frontend: Company-Modul (Seiten, Formulare, Sidebar-Link) 2. Frontend: Contact/Deal-Formulare um Company-Selektor erweitern 3. Activity-Liste komplett laden (UI-Button "Alle anzeigen") 4. Kanban-Board fuer Vorgaenge (Drag & Drop Stage-Wechsel) 5. E2E-Tests schreiben 6. Production-Build testen (multi-stage Dockerfile)