# EK-DOS-WEB Büroanwendung der Elektro Krüger GmbH. Tickets, Angebote, Rechnungen, Kundenstamm, interne Aufgaben, Stundennachweise und Online-Käufe -- die Fachdaten liegen in n8n, die Benutzer in einer eigenen Datenbank. ``` Browser │ HTTPS, Sitzungscookie (undurchsichtig) ▼ nginx ────────────────┬──────────────────────────────┐ │ statisch │ /api/ über Unix-Socket │ ▼ ▼ │ React-Bündel php8.5-fpm ──► PostgreSQL (Benutzer, Rollen, Protokoll) (nur Dateien) Slim 4 ──► Redis (Sitzungen, n8n-Cache) ──► n8n (alle Fachdaten) ``` Auf dem Server läuft **kein Node**. Die Oberfläche wird im Gitea-Lauf zu statischen Dateien kompiliert; ausgeliefert wird sie von nginx. ## Verzeichnisse | Ordner | Inhalt | | ------------------- | ----------------------------------------------------------------- | | `backend/` | PHP 8.5, Slim 4. BFF-Sitzung, Benutzerverwaltung, n8n-Weiterleitung | | `frontend/` | React 19 + Vite. Baut nach `frontend/dist/` | | `deploy/nginx/` | nginx-Site | | `deploy/php-fpm/` | php-fpm-Pool | | `.gitea/workflows/` | Bau- und Ausrollstrecke | | `docs/` | Betrieb und n8n-Schnittstelle | | `legacy-nextjs/` | Die abgelöste Next.js-Fassung. Kann nach dem Umstieg weg | ## Wie es zusammenhängt **Die Anmeldung** ist ein Backend-For-Frontend: der Browser hält nur eine zufällige Kennung im Cookie, alles Weitere -- Benutzer, Rolle, Rechte -- liegt in Redis. Das Cookie wächst dadurch nicht mit den Rechten mit, und eine Rechteänderung wirkt sofort, weil das Backend die Sitzung wegwerfen kann. **Die Rechte** ersetzen die früheren Namensabfragen. Statt `user === "Sascha"` gibt es `Permission::OffersDelete`, und Rollen bündeln diese Rechte: | Rolle | darf zusätzlich | | ---------------- | -------------------------------------------------------------------- | | `admin` | alles, einschließlich Benutzerverwaltung | | `inhaber` | Angebote/Kunden/Aufgaben löschen, Online-Käufe eintragen und löschen | | `buero` | Online-Käufe abschließen, Rechnungen bearbeiten und versenden | `inhaber` entspricht dem, was Sascha vorher durfte, `buero` dem von Svenja. **Die n8n-Daten** kommen weiter live aus den Webhooks, aber durch einen kurzen Redis-Cache. Drei Dinge passieren dort: eine frische Antwort wird für 15--300 Sekunden wiederverwendet; läuft sie ab, während mehrere Ansichten gleichzeitig fragen, holt genau **eine** die Daten (Single-Flight); und ist n8n nicht erreichbar, wird die letzte bekannte Antwort ausgeliefert statt einer Fehlerseite. Jeder schreibende Aufruf verwirft seine Gruppe sofort. ## Erste Einrichtung ```bash cd backend cp .env.example .env # DB, Redis, n8n-Geheimnisse eintragen composer install --no-dev --optimize-autoloader php bin/ekdos migrate php bin/ekdos user:create # der erste Administrator php bin/ekdos check # Postgres, Redis und n8n prüfen ``` Details zu nginx, php-fpm und dem Ausrollen: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md). ## Lokal entwickeln Zwei Fenster: ```bash php -S 127.0.0.1:8080 -t backend/public ``` ```bash cd frontend && pnpm install && pnpm dev ``` Der Vite-Entwicklungsserver läuft auf `http://127.0.0.1:5173` und reicht `/api` an den PHP-Server weiter. Ein anderes Backend lässt sich über `EKDOS_BACKEND` angeben. Postgres und Redis müssen erreichbar sein; n8n nur, wenn die Fachansichten Daten zeigen sollen -- Anmeldung und Benutzerverwaltung laufen ohne. ## Wartungs-CLI ```bash php backend/bin/ekdos migrate # Schema fortschreiben php backend/bin/ekdos user:create # Benutzer anlegen php backend/bin/ekdos user:list # Benutzer auflisten php backend/bin/ekdos user:password # Passwort zurücksetzen php backend/bin/ekdos cache:flush # n8n-Cache leeren php backend/bin/ekdos check # Verbindungen prüfen ``` ## Was noch offen ist `frontend/src/views/Dashboard.tsx` ist unverändert die eine große Komponente aus der Next.js-Fassung (rund 4.700 Zeilen, 22 Ansichten in einer Zustandsmaschine). Sie wurde bewusst **kopiert statt neu geschrieben**, damit an der Darstellung nichts unbemerkt verrutscht. Herausgelöst sind bisher Anmeldung, Sitzung und die neue Benutzeransicht. Die Aufteilung in eine Ansicht pro Datei ist der nächste sinnvolle Schritt. Der Weg dorthin ohne Umschreiben der Markup-Blöcke: den Zustand in einen `AppState`-Kontext heben, und jede Ansichtsdatei oben genau die Namen destrukturieren, die ihr JSX schon verwendet -- dann bleibt das Markup wörtlich unverändert.