# 01 Architektur ## Bausteine Drei Teile, keiner davon spricht direkt mit einem anderen ausser über HTTP bzw. SQL: | Teil | Technik | Wo | | --- | --- | --- | | Frontend | React 19, TypeScript, Tailwind 4, Vite | statisches Build in `/var/www/perinco_CRM/dist` | | Backend | Express 4, mysql2 (CRM), Sequelize (Scanner) | `/var/www/perinco_CRM/server`, `127.0.0.1:5006` | | Datenbank | MariaDB 10.11 | `perinco_CRM` und `perinco_scanner` | Der Browser spricht **nie** direkt mit MariaDB - deshalb überhaupt ein Backend. Das Backend bindet ausschließlich auf `127.0.0.1` und ist von außen nur über den Apache-Reverse-Proxy erreichbar. ## Topologie ```mermaid flowchart LR U(("Browser")) -->|"HTTPS"| A["Apache :443
pcrm.perinco.de"] A -->|"/"| D["dist/
SPA, FallbackResource"] A -->|"/api"| E["Express :5006"] E --> C[("perinco_CRM")] E --> S[("perinco_scanner")] U2(("Browser")) -->|"HTTPS"| A2["Apache :443
scanner.perinco.de"] A2 -->|"/api, /uploads, /health"| E2["alte Scanner-App :5005"] E2 --> S2[("project_scanner")] classDef alt fill:#f7f7f7,stroke:#9a9a9a,stroke-dasharray: 4 3 class U2,A2,E2,S2 alt ``` Der Scanner im CRM hängt unter `/api/scanner` und braucht damit **keine eigene Apache-Regel**: alles läuft über den bestehenden `/api`-Proxy. Dasselbe gilt für die Postfachanbindung unter `/api/mail` (siehe [11 Postfachanbindung](11-postfach.md)). ## Ausgehende Verbindungen Das Backend spricht von sich aus mit drei fremden Diensten. Alle drei sind optional - fehlen die Zugangsdaten, läuft die Anwendung ohne den jeweiligen Teil weiter. | Ziel | Wofür | Ausgelöst von | | --- | --- | --- | | Projektbörsen | Scraping der Projektangebote | Scanner-Scheduler | | `api.openai.com` | Bewertung Lebenslauf gegen Projekt | Scanner-Pipeline | | `login.microsoftonline.com`, `graph.microsoft.com` | Postfach `crm@perinco.de` lesen | Mail-Scheduler | Die Postfachanbindung schickt **keine** Mailinhalte an Dritte: Kontaktdaten werden auf dem Server aus Graph-Feldern und Signaturen gelesen, ohne Sprachmodell. ## Anfrageweg mit Anmeldung ```mermaid sequenceDiagram actor B as Browser participant A as Apache participant E as Express participant M as MariaDB B->>A: POST /api/auth/login A->>E: weiterleiten E->>M: SELECT crm_users WHERE username M-->>E: Zeile mit bcrypt-Hash E->>E: bcrypt.compare E-->>B: { token (JWT, 12 h), user } Note over B: Token im localStorage B->>A: GET /api/scanner/matches
Authorization: Bearer ... A->>E: weiterleiten E->>E: requireAuth: JWT pruefen E->>M: SELECT crm_users WHERE id (ist der noch aktiv?) E->>M: SELECT matches JOIN agents, projects M-->>E: Treffer E-->>B: JSON ``` Bei `401` löscht das Frontend das Token und springt zurück auf den Anmeldeschirm (`setUnauthorizedHandler` in `src/lib/api.ts`). Das Token wird bei jeder Anfrage neu gegen die Datenbank geprüft - ein deaktivierter Benutzer ist sofort draußen, ohne auf den Ablauf des Tokens zu warten. ## Rollen ```mermaid flowchart TD L["Anmeldung"] --> R{"Rolle"} R -->|admin| A["Akquise, Opportunities, Interviews, Kontakte, Mails, Scanner
Einstellungen: Nutzerverwaltung, Mein Konto, Postfach, Scanner, Whisper"] R -->|user| U["Akquise, Opportunities, Interviews, Kontakte, Mails, Scanner
Einstellungen: Mein Konto, Whisper"] ``` Bei den Mails darf jede Rolle lesen und zuordnen - `crm@perinco.de` ist ein gemeinsames Vertriebspostfach. Einrichten, Verbindungstest und manueller Abgleich sind Administratoren vorbehalten. Die Rollenprüfung sitzt **doppelt**: das Menü blendet aus, was nicht erlaubt ist, und das Backend weist Zugriffe zusätzlich mit `403` ab (`requireAdmin`). Ausnahme ist `GET /api/users/directory` - die Benutzerliste brauchen alle Rollen für Netzwerkdiagramm, Zuständigkeiten, @-Mentions, Teilnehmerauswahl und Terminfarben. Passwort-Hashes liefert der Endpunkt nie. Ein Sicherheitsnetz im Backend verhindert, dass der letzte aktive Administrator degradiert, deaktiviert oder gelöscht wird. ## Belegte Ports auf dem Server Der Rechner hostet weitere Dienste (n8n, Docker, ~15 Apache-Vhosts). Vor jeder Portbelegung `ss -tlnp` prüfen. Relevant hier: | Port | Dienst | | --- | --- | | 5006 | perinco CRM Backend (nur `127.0.0.1`) | | 5005 | alte Scanner-App (`0.0.0.0`) | | 3000 | weitere Vite-App | | 5678 | n8n |