1246 lines
40 KiB
Markdown
1246 lines
40 KiB
Markdown
# Olympus CRM Architektur
|
|
|
|
## Projektueberblick
|
|
|
|
Olympus CRM ist als mehrschichtige CRM-Anwendung aufgebaut. Die Architektur trennt klar zwischen Browser, Frontend/BFF, Backend-API und Datenbank.
|
|
|
|
Die zentrale Regel lautet:
|
|
|
|
**Der Browser kommuniziert ausschliesslich mit Athena. Athena kommuniziert serverseitig mit Hermes. Hermes kommuniziert mit PostgreSQL.**
|
|
|
|
Diese Trennung ist verbindlich, besonders fuer Authentifizierung, API-Zugriffe und spaetere Modul-Erweiterungen wie Kunden, Projekte, Lieferanten, Kontakte und Mitarbeiter.
|
|
|
|
## Systemkomponenten
|
|
|
|
### Athena: Next.js 16 Frontend
|
|
|
|
Athena liegt unter `frontend/athena` und ist das Frontend sowie die BFF-Schicht des Systems.
|
|
|
|
Aufgaben:
|
|
|
|
- rendert die Benutzeroberflaeche mit Next.js 16 App Router
|
|
- stellt eigene API-Routes unter `/api/...` bereit
|
|
- setzt und loescht das HttpOnly-Auth-Cookie
|
|
- ruft Hermes serverseitig auf
|
|
- schuetzt Seiten ueber `proxy.ts`
|
|
|
|
Der Browser darf keine Hermes-URLs direkt aufrufen.
|
|
|
|
### Hermes: FastAPI Backend
|
|
|
|
Hermes liegt unter `backend/hermes` und stellt die interne Backend-API bereit.
|
|
|
|
Aufgaben:
|
|
|
|
- validiert Requests mit Pydantic v2
|
|
- verarbeitet Fachlogik und Persistenz
|
|
- prueft JWT Bearer Tokens
|
|
- stellt REST-Endpunkte bereit
|
|
- nutzt SQLAlchemy 2.x fuer Datenbankzugriffe
|
|
- setzt keine Browser-Cookies
|
|
|
|
Hermes bleibt Cookie-frei. Authentifizierte Hermes-Endpunkte erwarten `Authorization: Bearer <jwt>`.
|
|
|
|
### PostgreSQL Datenbank
|
|
|
|
PostgreSQL ist die relationale Datenbank fuer Olympus CRM. Hermes greift ueber SQLAlchemy darauf zu.
|
|
|
|
Schemaaenderungen erfolgen ueber Alembic-Migrationen. Manuelle Datenbankeingriffe sind nur in Notfaellen zulaessig und muessen nachvollziehbar dokumentiert werden.
|
|
|
|
### Docker Compose
|
|
|
|
Die gemeinsame Docker-Compose-Konfiguration liegt im Projektroot in `docker-compose.yml`.
|
|
|
|
Sie verbindet:
|
|
|
|
- `hermes`
|
|
- `athena`
|
|
- das konfigurierte Docker-Netzwerk
|
|
- die notwendigen Runtime-Umgebungsvariablen
|
|
|
|
Im gemeinsamen Compose-Stack wird Athena am Host veroeffentlicht. Hermes wird nur intern im Docker-Netzwerk exponiert und von Athena ueber `HERMES_INTERNAL_URL` erreicht.
|
|
|
|
## BFF-Architektur
|
|
|
|
Olympus nutzt eine Backend-for-Frontend-Architektur.
|
|
|
|
Regeln:
|
|
|
|
- Browser spricht ausschliesslich mit Athena.
|
|
- Athena spricht serverseitig mit Hermes.
|
|
- Hermes setzt keine Browser-Cookies.
|
|
- Cross-Origin-Cookies werden nicht verwendet.
|
|
- Frontend-API-Zugriffe laufen ueber relative Athena-Routen wie `/api/users`.
|
|
|
|
Der Datenfluss bleibt dadurch kontrolliert, sicher und leichter hinter Reverse Proxies betreibbar.
|
|
|
|
## Authentifizierungsfluss
|
|
|
|
### Login
|
|
|
|
1. Der Browser sendet Benutzername und Passwort an Athena: `POST /api/login`.
|
|
2. Athena ruft serverseitig Hermes auf: `POST /auth/login`.
|
|
3. Hermes validiert die Zugangsdaten.
|
|
4. Hermes gibt JWT, Ablaufzeit und Benutzerdaten als JSON zurueck.
|
|
5. Athena setzt das JWT in einem HttpOnly-Cookie.
|
|
6. Der Browser wird auf eine geschuetzte Seite weitergeleitet.
|
|
|
|
Hermes setzt kein Cookie.
|
|
|
|
### Logout
|
|
|
|
1. Der Browser sendet `POST /api/logout` an Athena.
|
|
2. Athena loescht das HttpOnly-Cookie.
|
|
3. Der Benutzer wird zur Login-Seite gefuehrt.
|
|
|
|
### Aktueller Benutzer
|
|
|
|
Athena stellt `GET /api/me` bereit und ruft dafuer serverseitig Hermes `GET /auth/me` auf. Der Header laedt den aktuellen Benutzer ausschliesslich ueber diese BFF-Route und zeigt bevorzugt Vorname plus Nachname, danach den Benutzernamen und vor dem Laden `Benutzer`.
|
|
|
|
Der Browser erhaelt dabei kein Token und ruft Hermes nicht direkt auf.
|
|
|
|
### HttpOnly Cookie
|
|
|
|
Das Auth-Cookie wird von Athena gesetzt.
|
|
|
|
Eigenschaften:
|
|
|
|
- `HttpOnly`
|
|
- `SameSite=Lax`
|
|
- `Path=/`
|
|
- `Secure` in Produktion
|
|
- Ablaufzeit orientiert sich an der JWT-Ablaufzeit
|
|
|
|
Tokens duerfen nicht in `localStorage` oder `sessionStorage` gespeichert werden.
|
|
|
|
### JWT
|
|
|
|
Hermes erzeugt JWT Access Tokens.
|
|
|
|
Wichtige Claims:
|
|
|
|
- `sub`: Benutzername
|
|
- `exp`: Ablaufzeit
|
|
- `iat`: Ausstellungszeitpunkt
|
|
- `iss`: konfigurierter Issuer
|
|
- `type`: Token-Typ, aktuell `access`
|
|
|
|
### Issuer
|
|
|
|
Der Issuer wird ueber `JWT_ISSUER` konfiguriert. Hermes prueft den Issuer bei der Token-Validierung.
|
|
|
|
### Ablaufzeit
|
|
|
|
Die Ablaufzeit wird ueber `ACCESS_TOKEN_EXPIRE_MINUTES` konfiguriert. Athena nutzt die von Hermes gelieferte Ablaufzeit fuer das Cookie.
|
|
|
|
### `proxy.ts`
|
|
|
|
Athena schuetzt Routen ueber `frontend/athena/proxy.ts`.
|
|
|
|
Aufgaben:
|
|
|
|
- geschuetzte Bereiche ohne Cookie auf `/login` umleiten
|
|
- eingeloggte Benutzer von `/login` auf `/dashboard` umleiten
|
|
|
|
## API-Datenfluss
|
|
|
|
Standardfluss:
|
|
|
|
```text
|
|
Browser -> Athena /api/... -> Hermes -> PostgreSQL
|
|
```
|
|
|
|
Beispiel Benutzerliste:
|
|
|
|
```text
|
|
Browser
|
|
GET /api/users
|
|
|
|
Athena
|
|
GET {HERMES_INTERNAL_URL}/users
|
|
Authorization: Bearer <jwt aus HttpOnly Cookie>
|
|
|
|
Hermes
|
|
SQLAlchemy Query gegen PostgreSQL
|
|
```
|
|
|
|
Athena ist die einzige API-Oberflaeche fuer den Browser.
|
|
|
|
## Lexware Office Integration
|
|
|
|
Ab v0.8.9 besitzt Olympus eine Lexware-Office-Foundation.
|
|
|
|
Verantwortlichkeiten:
|
|
|
|
- Olympus bleibt Werkstatt-ERP fuer Kunden, Reparaturen, Lager und Kostenvoranschlaege.
|
|
- Lexware Office bleibt fuehrend fuer Buchhaltung, Rechnungen, Steuer, DATEV und EÜR.
|
|
- Eine automatische Rechnungserstellung findet in v0.8.9 noch nicht statt.
|
|
|
|
Komponenten:
|
|
|
|
- Hermes speichert Lexware-Konfiguration in `system_settings`.
|
|
- Der API-Key ist ein Secret und wird nie an Athena zurueckgegeben; Athena sieht nur `api_key_is_set`.
|
|
- Env-Fallbacks sind `LEXWARE_ENABLED`, `LEXWARE_API_BASE_URL` und `LEXWARE_API_KEY`.
|
|
- Hermes testet die Verbindung serverseitig ueber `GET /v1/profile` an der Lexware Public API unter `https://api.lexware.io`.
|
|
- Freigegebene KVs koennen manuell fuer eine spaetere Lexware-Rechnung vorbereitet werden.
|
|
- Die eigentliche Rechnung wird weiterhin in externer Buchhaltungssoftware wie Lexware Office oder sevdesk erstellt.
|
|
|
|
Datenfluss:
|
|
|
|
```text
|
|
Browser -> Athena /api/lexware/... -> Hermes -> Lexware Office
|
|
Browser -> Athena /api/repairs/.../lexware/prepare-invoice -> Hermes -> PostgreSQL
|
|
```
|
|
|
|
Der Browser ruft Lexware nie direkt auf. Vorbereitete Exporte werden in `lexware_sync_records` dokumentiert.
|
|
|
|
Buchhaltungsworkflow:
|
|
|
|
- `prepared`: Rechnungsvorbereitung wurde in Olympus erstellt.
|
|
- `transferred`: Daten wurden manuell in die externe Buchhaltung uebernommen.
|
|
- `booked`: Rechnung ist in der Buchhaltung gebucht, fuer spaetere Ausbaustufen vorbereitet.
|
|
- `cancelled`: Vorbereitung wurde verworfen, fuer spaetere Ausbaustufen vorbereitet.
|
|
|
|
Athena zeigt bei freigegebenen Kostenvoranschlaegen die Aktion `In Buchhaltung übernehmen`. Diese oeffnet eine Kopierhilfe fuer Kundendaten und Positionen. Nach dem Speichern der Rechnung in der externen Buchhaltungssoftware kann der Benutzer die Vorbereitung mit Buchhaltungsnotiz als `transferred` markieren.
|
|
|
|
Neue zentrale Endpunkte:
|
|
|
|
- `GET /lexware/settings`
|
|
- `PUT /lexware/settings`
|
|
- `POST /lexware/test-connection`
|
|
- `POST /repairs/{repair_id}/estimates/{estimate_id}/lexware/prepare-invoice`
|
|
|
|
RBAC:
|
|
|
|
- `lexware.read`
|
|
- `lexware.manage`
|
|
- `lexware.export`
|
|
|
|
Techniker erhalten keine Lexware-Rechte. Management und Administratoren koennen die Integration nutzen.
|
|
|
|
## Dashboard
|
|
|
|
Das Dashboard liegt in Athena unter `/dashboard` und bezieht seine Daten ueber die BFF-Route `GET /api/dashboard/summary`.
|
|
|
|
Hermes stellt dafuer `GET /dashboard/summary` bereit. Der Endpunkt ist mit `dashboard.read` geschuetzt und liefert nur Datenbloecke, fuer die der aktuelle Benutzer weitere Berechtigungen besitzt.
|
|
|
|
Beispiele:
|
|
|
|
- Kundenstatistiken nur mit `customers.read`
|
|
- Benutzerstatistiken nur mit `users.read`
|
|
- Rollenstatus nur mit `roles.read`
|
|
- Reparaturkennzahlen nur mit `repairs.read`
|
|
|
|
Nicht vorhandene Module wie Aufgaben, Tickets oder Projekte werden nicht mit Fake-Daten gefuellt. Stattdessen liefert das Dashboard leere Widgets mit klarer Meldung.
|
|
|
|
### Activity Feed
|
|
|
|
Der Activity Feed ist Teil der v0.5.0-Qualitaetsplattform.
|
|
|
|
Athena ruft `GET /api/activity-feed` auf. Die BFF-Route ruft serverseitig Hermes `GET /activity-feed` auf.
|
|
|
|
Der Feed basiert auf persistenten Audit Logs und zeigt die letzten relevanten Aktivitaeten. Hermes filtert die Eintraege anhand der Berechtigungen des aktuellen Benutzers. Ein Benutzer sieht dadurch nur Aktivitaeten zu Bereichen, fuer die er Leserechte besitzt.
|
|
|
|
## Reparaturmanagement
|
|
|
|
Das Modul `Reparaturen` verwaltet interne Werkstattvorgaenge. Es liegt fachlich in Hermes und wird im Browser ausschliesslich ueber Athena erreicht.
|
|
|
|
Datenfluss:
|
|
|
|
```text
|
|
Browser -> Athena /api/repairs... -> Hermes /repairs... -> PostgreSQL
|
|
```
|
|
|
|
Kernfunktionen:
|
|
|
|
- Reparaturen manuell anlegen und bearbeiten
|
|
- eindeutige Reparaturnummer im Format `R<jahr>-<laufende nummer sechsstellig>`, z. B. `R2026-000001`
|
|
- Statusmodell von `new` bis `completed` oder `cancelled`
|
|
- deutsche UI-Statuslabels in Athena, die internen API-Statuscodes bleiben englisch und stabil
|
|
- Statushistorie in `repair_status_history`
|
|
- werkstattgerechte Status-Timeline mit Datum, Benutzer und Notiz
|
|
- Audit Logs fuer Erstellung, Bearbeitung, Statuswechsel, Storno, Intake und Statuslink-Verwaltung
|
|
- Dashboard-Kennzahlen fuer neue Reparaturen, Diagnose, wartende Kundenfreigaben, laufende Reparaturen, Endpruefung und abgeschlossene Reparaturen
|
|
|
|
Tabellen:
|
|
|
|
- `repairs`
|
|
- `repair_status_history`
|
|
- `repair_intake_events`
|
|
- `repair_documents`
|
|
- `repair_public_access_tokens`
|
|
- `repair_notification_events`
|
|
|
|
## Lager / Ersatzteile
|
|
|
|
Das Modul `Lager / Ersatzteile` verwaltet Bauteile und Verbrauchsmaterial fuer Werkstattprozesse. Seit v0.8.8 ist es mit Kostenvoranschlaegen verbunden: Mitarbeiter koennen aktive Lagerartikel als KV-Position uebernehmen.
|
|
|
|
Datenfluss:
|
|
|
|
```text
|
|
Browser -> Athena /api/inventory... -> Hermes /inventory... -> PostgreSQL
|
|
```
|
|
|
|
Kernregeln:
|
|
|
|
- Browser greifen nie direkt auf Hermes zu.
|
|
- Artikelnummern/SKUs sind eindeutig.
|
|
- Leere SKU bei Erstellung wird automatisch im Format `ET-<jahr>-000001` vergeben.
|
|
- Preise werden intern in cents gespeichert und in Athena als Euro eingegeben.
|
|
- `quantity_available` wird konsistent als `quantity_on_hand - quantity_reserved` gespeichert.
|
|
- Bestandsaenderungen erfolgen ueber Bewegungen, nicht ueber freie Client-Manipulation.
|
|
- Artikel mit Bewegungen werden bei Delete deaktiviert statt hart geloescht.
|
|
- Lagerartikel in KV-Positionen werden ueber `inventory_item_id` verknuepft.
|
|
- KV-Positionen speichern Snapshot-Felder fuer Name, SKU, Hersteller und Teilenummer, damit ein KV unveraendert bleibt, wenn der Lagerartikel spaeter geaendert wird.
|
|
- Verkaufspreis, Einheit, Artikelname und SKU werden serverseitig aus dem Lager uebernommen. Manuelle Preisueberschreibung ist moeglich und wird auditiert.
|
|
|
|
Tabellen:
|
|
|
|
- `inventory_items`
|
|
- `inventory_categories`
|
|
- `inventory_locations`
|
|
- `inventory_suppliers`
|
|
- `inventory_stock_movements`
|
|
|
|
Bestandsbewegungen:
|
|
|
|
- `initial`
|
|
- `purchase`
|
|
- `adjustment`
|
|
- `reservation`
|
|
- `release`
|
|
- `consumption`
|
|
- `return`
|
|
- `correction`
|
|
|
|
RBAC-Permissions:
|
|
|
|
- `inventory.read`
|
|
- `inventory.create`
|
|
- `inventory.update`
|
|
- `inventory.delete`
|
|
- `inventory.stock.adjust`
|
|
- `inventory.stock.reserve`
|
|
- `inventory.stock.consume`
|
|
- `inventory.manage.masterdata`
|
|
|
|
Dashboard-Kennzahlen fuer Lagerdaten werden nur mit `inventory.read` ausgeliefert. Audit Logs und Activity Feed enthalten Artikel- und Bestandsaktionen, aber keine unnoetigen sensiblen Lieferantendaten.
|
|
|
|
Reservierungslogik:
|
|
|
|
- Entwurf: keine Reservierung.
|
|
- Senden: verknuepfte Lagerartikel werden reserviert.
|
|
- Freigabe: Reservierung bleibt bestehen.
|
|
- Ablehnung: Reservierung wird freigegeben.
|
|
- Storno: Reservierung wird freigegeben.
|
|
- Loeschung eines gesendeten KV: Reservierung wird freigegeben.
|
|
|
|
Automatischer Verbrauch beim Reparaturabschluss ist vorbereitet, wird aber noch nicht automatisch ausgefuehrt. CSV-Import/Export und Barcode-/QR-Funktionen bleiben vorbereitete Folgefeatures.
|
|
|
|
Hermes-Endpunkte:
|
|
|
|
- `GET /repairs`
|
|
- `GET /repairs/{id}`
|
|
- `POST /repairs`
|
|
- `PUT /repairs/{id}`
|
|
- `DELETE /repairs/{id}` als fachliches Storno
|
|
- `PUT /repairs/{id}/status`
|
|
- `GET /repairs/{id}/history`
|
|
- `GET /repairs/{id}/public-link`
|
|
- `POST /repairs/{id}/public-link`
|
|
- `DELETE /repairs/{id}/public-link`
|
|
- `GET /repairs/{id}/notifications`
|
|
- `GET /public/repairs/status/{token}`
|
|
|
|
Athena-BFF-Routen spiegeln diese Endpunkte unter `/api/repairs...`. Mutierende Athena-Routen behalten die Same-Origin-Pruefung bei.
|
|
|
|
Statuslabels:
|
|
|
|
```text
|
|
new = Neu
|
|
accepted = Angenommen
|
|
diagnosis = Diagnose
|
|
estimate = Kostenvoranschlag
|
|
waiting_for_customer = Wartet auf Kunde
|
|
approved = Freigegeben
|
|
repair = Reparatur
|
|
final_test = Endprüfung
|
|
ready_for_pickup = Abholbereit
|
|
shipped = Versand
|
|
completed = Abgeschlossen
|
|
cancelled = Storniert
|
|
```
|
|
|
|
### Sicherer Reparaturstatus-Link
|
|
|
|
v0.8.1 bereitet einen oeffentlichen Statuslink vor. Es wird noch kein Kundenportal und kein Kundenlogin gebaut.
|
|
|
|
Sicherheitsmodell:
|
|
|
|
- Klartexttoken wird lang und zufaellig erzeugt.
|
|
- In PostgreSQL wird nur ein HMAC/SHA-256-Hash des Tokens gespeichert.
|
|
- Der Klartexttoken wird nur einmal bei Erstellung an Athena zurueckgegeben.
|
|
- Tokens werden niemals geloggt.
|
|
- `repair_public_access_tokens` speichert Aktivstatus, Ablaufdatum, letzte Nutzung und Widerruf.
|
|
- `GET /public/repairs/status/{token}` gibt nur kundenfreundliche Statusdaten aus.
|
|
- Keine Kundendaten, internen Notizen oder Diagnosedetails werden oeffentlich ausgegeben.
|
|
|
|
Die spaetere Website kann daraus einen Pfad wie `/status/<token>` anbieten. Die eigentliche Website wird in Olympus nicht veraendert.
|
|
|
|
### Reparatur-Benachrichtigungen
|
|
|
|
`repair_notification_events` speichert jeden Versandversuch fuer Reparaturstatusmails. Der Statuswechsel selbst wird nie durch SMTP-Probleme blockiert.
|
|
|
|
Vorbereitete Ereignisse:
|
|
|
|
- Reparatur erfasst
|
|
- Geraet angenommen
|
|
- Diagnose laeuft
|
|
- Kostenvoranschlag erstellt
|
|
- Wartet auf Kundenfreigabe
|
|
- Reparatur laeuft
|
|
- Endpruefung
|
|
- Abholbereit
|
|
- Versand
|
|
- Abgeschlossen
|
|
|
|
Vorlagen enthalten deutsche Betreffzeilen und Texte mit Platzhaltern fuer `repair_number`, `customer_name`, `device`, `status_label`, `public_status_url` und `company_name`.
|
|
|
|
Ab v0.8.3 sendet Hermes beim Statuswechsel automatisch eine Statusmail, wenn eine Kunden-E-Mail vorhanden ist. Fuer jede Mail wird ein frischer sicherer Statuslink erzeugt, weil Klartexttokens nicht gespeichert werden. Ist SMTP nicht konfiguriert oder schlaegt der Versand fehl, wird ein `repair_notification_events`-Eintrag mit `skipped` oder `failed` geschrieben und ein Audit-Eintrag erzeugt.
|
|
|
|
Ab v0.8.4 wird SMTP bevorzugt ueber `system_settings` aus PostgreSQL geladen. Nur wenn die Admin-Konfiguration deaktiviert, unvollstaendig oder nicht vorhanden ist, nutzt Hermes die Env Vars als Fallback. Das SMTP-Passwort wird nie an Athena zurueckgegeben; die API liefert nur `password_is_set`. Verschluesselung at rest ist in der Struktur vorbereitet und fuer v0.8.x als Sicherheitsausbau geplant.
|
|
|
|
Hermes-Endpunkt fuer manuellen Neuversand:
|
|
|
|
- `POST /repairs/{id}/send-status-mail`
|
|
|
|
Athena ruft diesen Endpunkt ausschliesslich ueber die BFF-Route `/api/repairs/[id]/send-status-mail` auf.
|
|
|
|
### System Settings
|
|
|
|
`system_settings` speichert zentrale Betriebswerte als Key-Value-Daten:
|
|
|
|
- `smtp.host`
|
|
- `smtp.port`
|
|
- `smtp.username`
|
|
- `smtp.password`
|
|
- `smtp.from_email`
|
|
- `smtp.from_name`
|
|
- `smtp.use_tls`
|
|
- `smtp.enabled`
|
|
- `public.repair_status_base_url`
|
|
|
|
Hermes-Endpunkte:
|
|
|
|
- `GET /system-settings/smtp`
|
|
- `PUT /system-settings/smtp`
|
|
- `POST /system-settings/smtp/test`
|
|
- `GET /system-settings/public-links`
|
|
- `PUT /system-settings/public-links`
|
|
|
|
Athena ruft diese Endpunkte ausschliesslich ueber BFF-Routen unter `/api/system-settings/...` auf. Mutierende Requests verwenden den bestehenden Same-Origin-Schutz. Zugriff erfordert `system_settings.manage`, das der Administratorrolle zugewiesen ist.
|
|
|
|
### Reparaturdokumente
|
|
|
|
Ab v0.8.5 verwaltet Olympus Bilder und Dokumente direkt an Reparaturen. Die Fachlogik liegt im Repair-Modul, Dateizugriffe laufen ueber das zentrale Storage Framework.
|
|
|
|
Datenmodell `repair_documents`:
|
|
|
|
- `repair_id`
|
|
- `title`
|
|
- `document_type`
|
|
- `original_filename`
|
|
- `stored_filename`
|
|
- `storage_path`
|
|
- `mime_type`
|
|
- `size_bytes`
|
|
- `checksum_sha256`
|
|
- `visibility`
|
|
- `note`
|
|
- `uploaded_by_user_id`
|
|
- Zeitstempel
|
|
|
|
Dokumenttypen:
|
|
|
|
- `device_photo`
|
|
- `fault_photo`
|
|
- `measurement`
|
|
- `estimate`
|
|
- `repair_report`
|
|
- `shipping`
|
|
- `other`
|
|
|
|
`visibility` kann `internal` oder `customer` sein. Die Kundensicht ist nur vorbereitet; in v0.8.5 werden Dokumente nicht oeffentlich fuer Kunden ausgeliefert.
|
|
|
|
Storage:
|
|
|
|
- Namespace: `repairs/<repair_id>/documents`
|
|
- Erlaubte Uploads: JPG, PNG, WEBP und PDF
|
|
- Dateinamen werden normalisiert
|
|
- SHA-256 wird gespeichert
|
|
- Dateiinhalte werden nicht geloggt
|
|
- Storage-Pfade werden nicht in Audit-Metadaten geschrieben
|
|
- Browser erhalten Dateien nur ueber Athena-BFF und Hermes-RBAC
|
|
|
|
Hermes-Endpunkte:
|
|
|
|
- `GET /repairs/{id}/documents`
|
|
- `POST /repairs/{id}/documents/upload`
|
|
- `GET /repairs/{id}/documents/{document_id}`
|
|
- `PUT /repairs/{id}/documents/{document_id}`
|
|
- `GET /repairs/{id}/documents/{document_id}/download`
|
|
- `DELETE /repairs/{id}/documents/{document_id}`
|
|
|
|
RBAC:
|
|
|
|
- Liste, Detail und Download erfordern `repairs.read`.
|
|
- Upload, Aenderung und Loeschung erfordern `repairs.update`.
|
|
|
|
Athena ruft diese Endpunkte ausschliesslich ueber `/api/repairs/[id]/documents...` auf. Mutierende Requests verwenden den Same-Origin-Schutz.
|
|
|
|
### Kostenvoranschläge
|
|
|
|
Ab v0.8.6 verwaltet Olympus Kostenvoranschlaege direkt im Repair-Modul. Es gibt keinen Kundenlogin und keine neue Website-App. Kundenentscheidungen laufen ueber den bestehenden sicheren Public-Statuslink.
|
|
|
|
Datenmodell:
|
|
|
|
- `repair_estimates`
|
|
- `repair_estimate_items`
|
|
- `repair_estimate_events`
|
|
|
|
Kostenvoranschlagsnummern folgen `KV<jahr>-000001`. Positionstypen sind `labor`, `part`, `flat_rate`, `shipping` und `other`. Statuswerte sind `draft`, `sent`, `approved`, `declined`, `expired` und `cancelled`.
|
|
|
|
Hermes berechnet alle Summen serverseitig:
|
|
|
|
- `subtotal_cents` aus Positionen
|
|
- `tax_cents` aus `tax_rate_percent`
|
|
- `total_cents` als Summe aus netto und Steuer
|
|
|
|
Interne Hermes-Endpunkte:
|
|
|
|
- `GET /repairs/{repair_id}/estimates`
|
|
- `POST /repairs/{repair_id}/estimates`
|
|
- `GET /repairs/{repair_id}/estimates/{estimate_id}`
|
|
- `PUT /repairs/{repair_id}/estimates/{estimate_id}`
|
|
- `DELETE /repairs/{repair_id}/estimates/{estimate_id}`
|
|
- `POST /repairs/{repair_id}/estimates/{estimate_id}/send`
|
|
- `POST /repairs/{repair_id}/estimates/{estimate_id}/cancel`
|
|
- `GET /repairs/{repair_id}/estimates/{estimate_id}/events`
|
|
|
|
RBAC:
|
|
|
|
- `repair_estimates.read`
|
|
- `repair_estimates.create`
|
|
- `repair_estimates.update`
|
|
- `repair_estimates.delete`
|
|
- `repair_estimates.send`
|
|
|
|
Administrator bekommt alle Rechte. Management und Technik bekommen Lesen/Erstellen/Bearbeiten/Senden. Support bekommt Lesen/Senden.
|
|
|
|
Public API:
|
|
|
|
- `GET /public/repairs/status/{token}` liefert bei aktivem gesendetem KV ein Feld `estimate`.
|
|
- `POST /public/repairs/status/{token}/estimate/approve`
|
|
- `POST /public/repairs/status/{token}/estimate/decline`
|
|
- `POST /public/repairs/status/{token}/estimate/question`
|
|
|
|
Die Public-Antwort enthaelt keine internen Notizen, keine internen Events und keine Tokens. Bei Freigabe setzt Olympus den KV auf `approved` und den Reparaturstatus auf `approved`. Bei Ablehnung oder Rueckfrage bleibt der Prozess bei `waiting_for_customer`.
|
|
|
|
Der KV-Versand nutzt die bestehende SMTP-Konfiguration aus `system_settings` mit Env-Fallback. Fehler beim Mailversand zerstoeren den KV nicht; der Versandversuch wird als Notification Event dokumentiert.
|
|
|
|
Nicht enthalten in v0.8.6:
|
|
|
|
- PDF-Erzeugung
|
|
- Lexoffice
|
|
- Rechnungserstellung
|
|
- Kundenlogin
|
|
- Anzeige/Freigabe in der oeffentlichen Website
|
|
|
|
Die Website kann spaeter, z. B. in `funktechnik-schubert-website` v0.4.3, die bereits vorhandenen Public-API-Daten anzeigen und die Entscheidungsendpunkte aufrufen.
|
|
|
|
### Website Repair Intake
|
|
|
|
Hermes stellt `POST /public/repair-intake` fuer eine spaetere serverseitige Website-Anbindung bereit.
|
|
|
|
Sicherheitsmodell:
|
|
|
|
- keine Browser-Cookie- oder JWT-Authentifizierung
|
|
- Token per Header `X-Olympus-Intake-Token`
|
|
- Token aus `OLYMPUS_REPAIR_INTAKE_TOKEN`
|
|
- keine CORS-Freigabe fuer Browser erforderlich
|
|
- Token wird nicht geloggt
|
|
|
|
Der Intake speichert immer ein `repair_intake_events`-Ereignis und erzeugt bei erfolgreicher Verarbeitung einen Reparaturdatensatz mit `source = website` und `status = new`.
|
|
|
|
Eine spaetere Statusseite ist architektonisch vorbereitet, aber noch nicht in der oeffentlichen Website implementiert.
|
|
|
|
## RBAC: Rollen und Berechtigungen
|
|
|
|
Olympus verwendet ein serverseitiges RBAC-System als Grundlage fuer alle CRM-Module.
|
|
|
|
Konzepte:
|
|
|
|
- Benutzer besitzen genau eine primaere Rolle.
|
|
- Rollen besitzen mehrere Berechtigungen.
|
|
- Berechtigungen sind stabile String-Keys wie `users.read`.
|
|
- Hermes prueft Berechtigungen serverseitig ueber Dependencies.
|
|
- Athena darf Permissions fuer UI-Sichtbarkeit nutzen, aber niemals als Auth-Quelle.
|
|
|
|
Tabellen:
|
|
|
|
- `roles`
|
|
- `permissions`
|
|
- `role_permissions`
|
|
- `users.role_id`
|
|
|
|
Das alte `users.role`-Feld bleibt vorerst als Legacy-Kompatibilitaet erhalten. Neue Logik verwendet `users.role_id` und die `roles`-Beziehung.
|
|
|
|
### Standardrollen
|
|
|
|
- `administrator`
|
|
- `management`
|
|
- `sales`
|
|
- `technician`
|
|
- `support`
|
|
- `warehouse`
|
|
- `guest`
|
|
|
|
`administrator` erhaelt alle Berechtigungen. `management` erhaelt mindestens Lesezugriff auf Dashboard, Benutzer, Kunden, Projekte und Tickets. `guest` erhaelt minimal `dashboard.read`.
|
|
|
|
### Standardberechtigungen
|
|
|
|
Benutzer:
|
|
|
|
- `users.read`
|
|
- `users.create`
|
|
- `users.update`
|
|
- `users.delete`
|
|
- `users.password.update`
|
|
|
|
Rollen:
|
|
|
|
- `roles.read`
|
|
- `roles.create`
|
|
- `roles.update`
|
|
- `roles.delete`
|
|
- `roles.assign`
|
|
|
|
Kunden:
|
|
|
|
- `customers.read`
|
|
- `customers.create`
|
|
- `customers.update`
|
|
- `customers.delete`
|
|
|
|
Knowledge:
|
|
|
|
- `knowledge.read`
|
|
- `knowledge.create`
|
|
- `knowledge.update`
|
|
- `knowledge.delete`
|
|
- `knowledge.upload`
|
|
- `knowledge.download`
|
|
|
|
### Initial Admin Bootstrap
|
|
|
|
Hermes prueft beim Startup nach dem RBAC-Seed, ob mindestens ein aktiver Benutzer existiert. Existiert ein aktiver Benutzer, wird kein Benutzer automatisch erstellt.
|
|
|
|
Existiert kein aktiver Benutzer, kann Hermes ueber optionale Umgebungsvariablen einen initialen Administrator anlegen:
|
|
|
|
- `INITIAL_ADMIN_USERNAME`
|
|
- `INITIAL_ADMIN_EMAIL`
|
|
- `INITIAL_ADMIN_PASSWORD`
|
|
- `INITIAL_ADMIN_FIRST_NAME`
|
|
- `INITIAL_ADMIN_LAST_NAME`
|
|
|
|
Benutzername, E-Mail und Passwort sind fuer die automatische Anlage erforderlich. Fehlen Werte oder ist die E-Mail ungueltig, startet Hermes weiter und schreibt nur eine Warnung ohne Secrets. Das Passwort wird mit der bestehenden `hash_password`-Funktion gehasht. Der Benutzer erhaelt `role_id` der Systemrolle `administrator` und das Legacy-Feld `role=administrator`.
|
|
|
|
### Kundenimport
|
|
|
|
Hermes stellt fuer CSV-Importe zwei Endpunkte bereit:
|
|
|
|
- `POST /customers/import/preview`
|
|
- `POST /customers/import/commit`
|
|
|
|
Athena stellt die Browser-BFF-Routen bereit:
|
|
|
|
- `POST /api/customers/import/preview`
|
|
- `POST /api/customers/import/commit`
|
|
- `GET /api/customers/import/template`
|
|
|
|
Der Browser sendet Multipart-FormData nur an Athena. Athena leitet die Datei serverseitig mit Bearer Token aus dem HttpOnly-Cookie an Hermes weiter. Hermes begrenzt CSV-Dateien auf 5 MB, bevorzugt UTF-8, erkennt Semikolon und Komma und ignoriert leere Zeilen.
|
|
|
|
Importmodi:
|
|
|
|
- `create_only`: neue Kunden erstellen, bestehende Kundennummern ueberspringen
|
|
- `update_existing`: bestehende Kunden anhand der Kundennummer aktualisieren
|
|
- `upsert`: bestehende Kunden aktualisieren und neue Kunden erstellen
|
|
|
|
RBAC:
|
|
|
|
- Preview benoetigt `customers.read`
|
|
- Commit mit `create_only` benoetigt `customers.create`
|
|
- Commit mit `update_existing` benoetigt `customers.update`
|
|
- Commit mit `upsert` benoetigt `customers.create` und `customers.update`
|
|
|
|
CSV-Spalten:
|
|
|
|
```text
|
|
customer_number;company_name;legal_name;customer_type;status;industry;website;email;phone;tax_number;vat_id;notes;address_type;street;postal_code;city;state;country;address_is_primary;contact_first_name;contact_last_name;contact_position;contact_email;contact_phone;contact_mobile;contact_is_primary;contact_notes
|
|
```
|
|
|
|
Pflichtfeld ist `company_name`. Wenn `customer_number`, `customer_type` oder `status` fehlen, erzeugt der Import fuer neue Kunden eine Kundennummer bzw. nutzt produktive Defaults und weist in der Preview darauf hin. Vollstaendige CSV-Inhalte werden nicht im Audit Log gespeichert.
|
|
|
|
Projekte:
|
|
|
|
- `projects.read`
|
|
- `projects.create`
|
|
- `projects.update`
|
|
- `projects.delete`
|
|
|
|
Tickets:
|
|
|
|
- `tickets.read`
|
|
- `tickets.create`
|
|
- `tickets.update`
|
|
- `tickets.delete`
|
|
|
|
Dashboard und System:
|
|
|
|
- `dashboard.read`
|
|
- `system.settings.read`
|
|
- `system.settings.update`
|
|
|
|
Audit:
|
|
|
|
- `audit_logs.read`
|
|
|
|
### Berechtigungspruefung
|
|
|
|
Zentrale Hermes-Dependencies:
|
|
|
|
- `get_current_user`
|
|
- `get_current_active_user`
|
|
- `require_permission("permission.name")`
|
|
- `require_any_permission([...])`
|
|
- `require_all_permissions([...])`
|
|
|
|
Backend-Endpunkte muessen immer selbst pruefen. Frontend-Helfer wie `hasPermission` dienen nur dazu, Buttons oder Navigation auszublenden.
|
|
|
|
### Rollen-API
|
|
|
|
Hermes:
|
|
|
|
- `GET /roles`
|
|
- `GET /roles/{id}`
|
|
- `POST /roles`
|
|
- `PUT /roles/{id}`
|
|
- `DELETE /roles/{id}`
|
|
- `PUT /roles/{id}/permissions`
|
|
- `GET /permissions`
|
|
|
|
Athena stellt entsprechende BFF-Routen unter `/api/roles`, `/api/permissions` und `/api/me` bereit.
|
|
|
|
## Benutzerverwaltung
|
|
|
|
Die Benutzerverwaltung ist das erste Enterprise-Modul und dient als Vorlage fuer weitere Module.
|
|
|
|
Funktionen:
|
|
|
|
- Benutzerliste
|
|
- Detailseite
|
|
- Erstellen
|
|
- Bearbeiten
|
|
- Loeschen
|
|
- Passwort separat aendern
|
|
- Suche, Filter, Sortierung und Pagination im Frontend
|
|
|
|
Backend-Endpunkte in Hermes:
|
|
|
|
- `GET /users`
|
|
- `GET /users/{id}`
|
|
- `POST /users`
|
|
- `PUT /users/{id}`
|
|
- `PUT /users/{id}/password`
|
|
- `DELETE /users/{id}`
|
|
|
|
Athena stellt die passenden BFF-Routen unter `/api/users` und `/api/users/[id]` bereit.
|
|
|
|
Diese Endpunkte sind per RBAC geschuetzt:
|
|
|
|
- `GET /users` und `GET /users/{id}`: `users.read`
|
|
- `POST /users`: `users.create` und fuer Rollenzuweisung `roles.assign`
|
|
- `PUT /users/{id}`: `users.update`; Rollenwechsel benoetigt `roles.assign`
|
|
- `PUT /users/{id}/password`: `users.password.update`
|
|
- `DELETE /users/{id}`: `users.delete`
|
|
|
|
### Passwortaenderung
|
|
|
|
Wenn beim Bearbeiten kein Passwort uebergeben wird, bleibt das bestehende Passwort unveraendert.
|
|
|
|
Wenn ein Passwort gesetzt wird, wird es in Hermes mit `pwdlib` neu gehasht.
|
|
|
|
### Self-Delete-Schutz
|
|
|
|
Der aktuell angemeldete Benutzer darf sich nicht selbst loeschen. Hermes verhindert dies serverseitig.
|
|
|
|
### Rollenmodell
|
|
|
|
Benutzer besitzen eine primaere RBAC-Rolle ueber `role_id`. Die Rolle bestimmt die serverseitigen Berechtigungen.
|
|
|
|
## Kundenmodul
|
|
|
|
Das Kundenmodul ist das zweite Enterprise-Modul und folgt den gleichen Grundsaetzen wie die Benutzerverwaltung:
|
|
|
|
- Hermes stellt REST-Endpunkte bereit.
|
|
- Athena proxyt diese Endpunkte ueber `/api/customers`.
|
|
- Der Browser spricht nicht direkt mit Hermes.
|
|
- Berechtigungen werden serverseitig in Hermes geprueft.
|
|
- Die UI nutzt Permissions nur fuer Sichtbarkeit und Bedienkomfort.
|
|
|
|
Hermes-Endpunkte:
|
|
|
|
- `GET /customers`
|
|
- `GET /customers/{id}`
|
|
- `POST /customers`
|
|
- `PUT /customers/{id}`
|
|
- `DELETE /customers/{id}`
|
|
- `GET /customers/{id}/contacts`
|
|
- `POST /customers/{id}/contacts`
|
|
- `PUT /customers/{id}/contacts/{contact_id}`
|
|
- `DELETE /customers/{id}/contacts/{contact_id}`
|
|
|
|
Athena-BFF-Routen:
|
|
|
|
- `GET /api/customers`
|
|
- `POST /api/customers`
|
|
- `GET /api/customers/[id]`
|
|
- `PUT /api/customers/[id]`
|
|
- `DELETE /api/customers/[id]`
|
|
- `GET /api/customers/[id]/contacts`
|
|
- `POST /api/customers/[id]/contacts`
|
|
- `PUT /api/customers/[id]/contacts/[contactId]`
|
|
- `DELETE /api/customers/[id]/contacts/[contactId]`
|
|
|
|
Tabellen:
|
|
|
|
- `customers`
|
|
- `customer_addresses`
|
|
- `customer_contacts`
|
|
|
|
Kunden-Permissions:
|
|
|
|
- `customers.read`
|
|
- `customers.create`
|
|
- `customers.update`
|
|
- `customers.delete`
|
|
|
|
Kontakte und Adressen gehoeren fachlich zum Kunden und werden aktuell ueber `customers.read` beziehungsweise `customers.update` gesteuert.
|
|
|
|
Ein Kunden-Delete entfernt aktuell den Kunden inklusive Adressen und Ansprechpartnern. Fuer spaetere Projekte oder Tickets ist ein fachlicher Loeschschutz vorzubereiten, sobald diese Module existieren.
|
|
|
|
## Audit Logs
|
|
|
|
Audit Logs sind die zentrale Nachvollziehbarkeitsschicht fuer v0.5.0.
|
|
|
|
Hermes persistiert relevante Ereignisse in der Tabelle `audit_logs`.
|
|
|
|
Erfasste Informationen:
|
|
|
|
- handelnder Benutzer
|
|
- Aktion, z. B. `users.update` oder `customers.delete`
|
|
- Objekttyp und Objekt-ID
|
|
- lesbares Objektlabel
|
|
- IP-Adresse und User-Agent
|
|
- Vorher-/Nachher-Daten fuer Aenderungen
|
|
- Metadaten fuer Kontextinformationen
|
|
- Erstellungszeitpunkt
|
|
|
|
Sensible Felder wie Passwoerter, Tokens und Secrets werden vor dem Schreiben maskiert.
|
|
|
|
Audit Logs werden aktuell fuer diese Bereiche geschrieben:
|
|
|
|
- Login erfolgreich/fehlgeschlagen
|
|
- Benutzer erstellen, bearbeiten, loeschen, Passwort aendern
|
|
- Rollen erstellen, bearbeiten, loeschen, Berechtigungen aendern
|
|
- Kunden erstellen, bearbeiten, loeschen
|
|
- Ansprechpartner erstellen, bearbeiten, loeschen
|
|
|
|
Hermes-Endpunkte:
|
|
|
|
- `GET /audit-logs`
|
|
- `GET /activity-feed`
|
|
|
|
Athena-BFF-Routen:
|
|
|
|
- `GET /api/audit-logs`
|
|
- `GET /api/activity-feed`
|
|
|
|
Die Audit-Log-UI liegt unter `frontend/athena/app/audit-logs` und ist ueber `audit_logs.read` sichtbar.
|
|
|
|
## API-Response-Standard
|
|
|
|
Neue Plattform-Endpunkte verwenden eine einheitliche Response-Huelle:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {},
|
|
"message": "Daten geladen"
|
|
}
|
|
```
|
|
|
|
Fehlerantworten aus Hermes verwenden ein konsistentes Format mit `success`, `message`, `error_code` und `details`. Fuer bestehende Athena-Fehlerbehandlung bleibt `detail` zusaetzlich erhalten.
|
|
|
|
## Logging
|
|
|
|
Hermes konfiguriert Logging zentral ueber `backend/hermes/app/core/logging.py`.
|
|
|
|
HTTP-Requests werden strukturiert mit Methode, Pfad, Statuscode und Dauer geloggt. Fachliche Ereignisse bleiben in den API-Modulen als strukturierte Logeintraege erhalten.
|
|
|
|
Das Runtime-Loglevel wird ueber `LOG_LEVEL` gesteuert.
|
|
|
|
## Frontend-Feedback
|
|
|
|
Athena nutzt einen globalen Toast-Provider fuer konsistente Erfolgs- und Fehlermeldungen.
|
|
|
|
Aktuell werden Toasts in den CRUD-Flows fuer Benutzer, Rollen, Kunden und Ansprechpartner genutzt. Inline-Fehler bleiben dort erhalten, wo sie fuer Formulare und Dialoge hilfreich sind.
|
|
|
|
## Wissensdatenbank / Service Library
|
|
|
|
v0.6.0 fuehrt das Modul Wissensdatenbank ein. Es verwaltet Hersteller, Geraete und Modelle, Dokumente sowie Reparaturhinweise fuer Funktechnik, Elektronikreparatur und Werkstattbetrieb.
|
|
|
|
Hermes stellt dafuer Tabellen mit dem Prefix `knowledge_` bereit:
|
|
|
|
- `knowledge_manufacturers`
|
|
- `knowledge_devices`
|
|
- `knowledge_documents`
|
|
- `knowledge_notes`
|
|
- `knowledge_categories`
|
|
|
|
Athena stellt die BFF-Routen unter `/api/knowledge/...` bereit. Der Browser spricht weiterhin ausschliesslich mit Athena.
|
|
|
|
### Knowledge-Workflow
|
|
|
|
Seit v0.7.1 fuehrt die UI Benutzer explizit durch den fachlichen Ablauf:
|
|
|
|
```text
|
|
Hersteller -> Gerät -> Dokument -> Notiz/Reparaturhinweis
|
|
```
|
|
|
|
Dokumente wie Schaltplaene, Service Manuals oder Abgleichanleitungen werden immer einem konkreten Geraet zugeordnet. Der Upload wird in Athena deaktiviert, solange Hersteller oder Geraet fehlen. Empty States erklaeren, was fehlt, und bieten die naechste sinnvolle Aktion an.
|
|
|
|
Upload-Voraussetzungen:
|
|
|
|
- Hersteller ist Pflicht.
|
|
- Geraet ist Pflicht.
|
|
- Dokumenttyp ist Pflicht.
|
|
- Datei ist Pflicht bei neuen Uploads.
|
|
- Das Geraet muss zum ausgewaehlten Hersteller gehoeren.
|
|
|
|
Hermes validiert diese Regeln serverseitig und liefert Benutzerfehler mit klaren Meldungen statt 500er-Antworten.
|
|
|
|
### Standard-Hersteller-Seeding
|
|
|
|
Hermes legt beim Startup idempotent Standard-Hersteller an, sofern sie noch nicht existieren:
|
|
|
|
- Stabo
|
|
- President
|
|
- Albrecht
|
|
- Marconi
|
|
- Rohde & Schwarz
|
|
- HP
|
|
- CRT
|
|
- Alinco
|
|
- Motorola
|
|
- Team
|
|
|
|
Das Seeding ueberschreibt bestehende Hersteller nicht. Fehlende Slugs werden ergaenzt. Websites und Notizen bleiben leer, solange keine sicheren Stammdaten gepflegt sind.
|
|
|
|
### Empty States
|
|
|
|
Athena verwendet fuer Knowledge-Listen die zentrale Komponente `EmptyState`.
|
|
|
|
Sie wird eingesetzt fuer:
|
|
|
|
- leere Herstellerliste
|
|
- leere Geraeteliste
|
|
- leere Dokumentliste
|
|
- leere Notizliste
|
|
- Suche oder Filter ohne Treffer
|
|
|
|
Jeder Empty State erklaert kurz den fehlenden Zustand und bietet eine passende Aktion an.
|
|
|
|
### Knowledge-Dateispeicherung
|
|
|
|
Seit v0.7.0 laufen Knowledge-Dateien ueber das zentrale Storage Framework. KnowledgeService speichert, liest und loescht Dateien nicht mehr direkt ueber verstreute Dateioperationen, sondern ueber `StorageService`.
|
|
|
|
Neue Uploads werden im Namespace `knowledge/documents/<manufacturer_id>` gespeichert. Die Datenbank-Metadaten bleiben kompatibel: `knowledge_documents.file_path` enthaelt den Storage-Key fuer neue Dateien oder einen bestehenden Legacy-Pfad fuer alte Dateien.
|
|
|
|
Reparaturdokumente werden ab v0.8.5 im Namespace `repairs/<repair_id>/documents` gespeichert. Zulaessig sind JPG, PNG, WEBP und PDF. PDF-Anzeige bleibt auf Inline-Open/Download begrenzt; ein eigener PDF-Viewer ist ein spaeteres Feature.
|
|
|
|
Konfiguration:
|
|
|
|
- `STORAGE_PROVIDER`, Default `local`
|
|
- `STORAGE_BASE_PATH`, Default `/data/storage`
|
|
- `STORAGE_MAX_UPLOAD_MB`, Default `50`
|
|
- `KNOWLEDGE_STORAGE_PATH`, Legacy-Fallback fuer bestehende Installationen
|
|
- `KNOWLEDGE_MAX_UPLOAD_MB`, Legacy-Fallback fuer bestehende Installationen
|
|
|
|
Docker Compose bindet den Host-Pfad aus `STORAGE_HOST_PATH` nach `/data/storage` ein. Fuer den VPS ist `/opt/olympus-storage` empfohlen. Bestehende Dateien unter `/data/knowledge` werden nicht automatisch verschoben; wenn Migration noetig ist, muss sie kontrolliert geplant und vorher gebackupt werden.
|
|
|
|
Erlaubte Uploadtypen:
|
|
|
|
- PDF
|
|
- JPG/JPEG
|
|
- PNG
|
|
- WEBP
|
|
- TXT
|
|
- ZIP
|
|
|
|
Hermes normalisiert Dateinamen, erzeugt eindeutige gespeicherte Dateinamen, validiert Extension und MIME-Type, begrenzt die Uploadgroesse, berechnet SHA256 und verhindert Path Traversal.
|
|
|
|
## Storage Framework
|
|
|
|
Das Storage Framework liegt unter `backend/hermes/app/storage`.
|
|
|
|
Bestandteile:
|
|
|
|
- `StorageProvider` als Interface
|
|
- `LocalDiskStorageProvider` als erste Implementierung
|
|
- `StorageService` als zentrale API fuer Fachmodule
|
|
- Storage-Schemas und eigene Storage-Exceptions
|
|
|
|
Zentrale Operationen:
|
|
|
|
- `save_file()`
|
|
- `open_file()`
|
|
- `delete_file()`
|
|
- `file_exists()`
|
|
- `get_file_metadata()`
|
|
- `calculate_checksum()`
|
|
- `safe_filename()`
|
|
- `validate_file_type()`
|
|
- `validate_file_size()`
|
|
|
|
Der aktuelle Provider ist `local`. Die Architektur ist bewusst fuer spaetere Provider vorbereitet:
|
|
|
|
- NAS
|
|
- S3
|
|
- MinIO
|
|
- Paperless
|
|
- Azure Blob
|
|
- Backblaze / Wasabi
|
|
|
|
Pfadstruktur im lokalen Provider:
|
|
|
|
```text
|
|
/data/storage/
|
|
knowledge/
|
|
documents/
|
|
thumbnails/
|
|
customers/
|
|
projects/
|
|
tickets/
|
|
imports/
|
|
temp/
|
|
```
|
|
|
|
Datei-Inhalte werden nicht geloggt. Browser greifen nie direkt auf Storage oder Hermes-Dateipfade zu; Downloads laufen ueber Athena-BFF und Hermes-Berechtigungspruefung.
|
|
|
|
## Backup und Deployment
|
|
|
|
Im Projektroot liegen robuste Bash-Skripte fuer Betrieb und Deployment:
|
|
|
|
- `scripts/migrate.sh`
|
|
- `scripts/healthcheck.sh`
|
|
- `scripts/deploy.sh`
|
|
- `scripts/backup.sh`
|
|
- `scripts/restore.sh`
|
|
|
|
`deploy.sh` baut Images, startet Docker Compose, fuehrt Migrationen aus und startet den Healthcheck. Es erzwingt kein `git pull`.
|
|
|
|
`backup.sh` erzeugt ein ZIP-Backup mit `manifest.json`, `database.dump` und `storage/`. Die Datei landet standardmaessig unter `${STORAGE_HOST_PATH}/backups`.
|
|
|
|
`restore.sh` validiert ein Backup-ZIP, fordert den bestaetigten Risikotext an, erstellt vor dem Storage-Restore einen Snapshot und spielt Datenbank sowie Storage ueber CLI zurueck.
|
|
|
|
### Backup-Modul ab v0.9.1
|
|
|
|
Olympus nutzt fuer operative Backups jetzt ein dediziertes Modul:
|
|
|
|
- Hermes-Service: `backend/hermes/app/services/backup_service.py`
|
|
- Hermes-API: `backend/hermes/app/api/backups.py`
|
|
- Hermes-Schemas: `backend/hermes/app/schemas/backup.py`
|
|
- Athena-Seite: `frontend/athena/app/backups/page.tsx`
|
|
- Athena-BFF: `/api/backups/...`
|
|
|
|
Sicherheitsregeln:
|
|
|
|
- Backup-Dateien liegen ausschliesslich unter `${STORAGE_BASE_PATH}/backups`.
|
|
- Der Backup-Ordner ist Teil des persistenten Storage-Volumes.
|
|
- Hermes anonymisiert den Datenbank-Host im Manifest.
|
|
- Passwoerter, komplette `DATABASE_URL`-Werte und Dateiinhalte werden nicht geloggt.
|
|
- Restore bleibt in der Weboberflaeche bewusst deaktiviert und liefert vorbereitetes, aber sicheres `501`.
|
|
|
|
Hermes erzeugt Datenbank-Dumps ueber `pg_dump` im Custom-Format. Deshalb enthaelt das Hermes-Image ab v0.9.1 den `postgresql-client`.
|
|
|
|
### Knowledge-RBAC
|
|
|
|
Neue Berechtigungen:
|
|
|
|
- `knowledge.read`
|
|
- `knowledge.create`
|
|
- `knowledge.update`
|
|
- `knowledge.delete`
|
|
- `knowledge.upload`
|
|
- `knowledge.download`
|
|
|
|
Standardrollen:
|
|
|
|
- `administrator`: alle Knowledge-Rechte
|
|
- `management`: `knowledge.read`, `knowledge.download`
|
|
- `technician`: `knowledge.read`, `knowledge.create`, `knowledge.update`, `knowledge.upload`, `knowledge.download`
|
|
- `support`: `knowledge.read`, `knowledge.download`
|
|
- `guest`: keine Knowledge-Rechte
|
|
|
|
Hermes erzwingt diese Rechte serverseitig. Athena nutzt sie nur fuer Navigation und UI.
|
|
|
|
### Paperless-Vorbereitung
|
|
|
|
Eine echte Paperless-ngx-Integration ist in v0.6.0 nicht aktiv. Dokumente enthalten bereits `paperless_document_id` und `external_url`, damit spaeter ein PaperlessConnector angebunden werden kann. API-Keys oder verpflichtende Paperless-Umgebungsvariablen werden in v0.6.0 nicht eingefuehrt.
|
|
|
|
Spaetere Verknuepfungen sind fuer Kunden, Projekte, Tickets und Reparaturen vorgesehen.
|
|
|
|
## Verzeichnisstruktur
|
|
|
|
```text
|
|
backend/hermes
|
|
app/
|
|
api/
|
|
audit/
|
|
core/
|
|
db/
|
|
models/
|
|
repositories/
|
|
schemas/
|
|
alembic/
|
|
alembic.ini
|
|
dockerfile
|
|
pyproject.toml
|
|
|
|
frontend/athena
|
|
app/
|
|
api/
|
|
audit-logs/
|
|
customers/
|
|
roles/
|
|
users/
|
|
components/
|
|
lib/
|
|
types/
|
|
proxy.ts
|
|
Dockerfile
|
|
package.json
|
|
|
|
docker-compose.yml
|
|
```
|
|
|
|
## Umgebungsvariablen
|
|
|
|
### Hermes
|
|
|
|
`DATABASE_URL`
|
|
: PostgreSQL-Verbindungsstring.
|
|
|
|
`SECRET_KEY`
|
|
: Signierschluessel fuer JWTs. Muss geheim bleiben.
|
|
|
|
`JWT_ISSUER`
|
|
: Erwarteter JWT-Issuer, z. B. `hermes`.
|
|
|
|
`ACCESS_TOKEN_EXPIRE_MINUTES`
|
|
: Ablaufzeit fuer Access Tokens in Minuten.
|
|
|
|
`LOG_LEVEL`
|
|
: Runtime-Loglevel fuer Hermes, z. B. `INFO`, `WARNING` oder `ERROR`.
|
|
|
|
`STORAGE_PROVIDER`
|
|
: Storage Provider. Aktuell produktiv implementiert: `local`.
|
|
|
|
`STORAGE_BASE_PATH`
|
|
: Interner Storage-Basispfad in Hermes. Default `/data/storage`.
|
|
|
|
`STORAGE_MAX_UPLOAD_MB`
|
|
: Maximale Uploadgroesse fuer Storage-Dateien in MB. Default `50`.
|
|
|
|
`STORAGE_HOST_PATH`
|
|
: Docker-Host-Pfad, der nach `/data/storage` gemountet wird. Lokal z. B. `./storage`, auf dem VPS empfohlen `/opt/olympus-storage`.
|
|
|
|
`KNOWLEDGE_STORAGE_PATH`
|
|
: Legacy-Fallback fuer bestehende Knowledge-Dateien. Neue Installationen sollen `STORAGE_*` verwenden.
|
|
|
|
`KNOWLEDGE_MAX_UPLOAD_MB`
|
|
: Legacy-Fallback fuer die maximale Knowledge-Uploadgroesse.
|
|
|
|
`OLYMPUS_REPAIR_INTAKE_TOKEN`
|
|
: Server-zu-Server Token fuer `POST /public/repair-intake`. Dieser Wert darf nicht im Frontend oder in Logs erscheinen.
|
|
|
|
`PUBLIC_REPAIR_STATUS_BASE_URL`
|
|
: Env-Fallback fuer von Olympus erzeugte oeffentliche Reparaturstatuslinks, z. B. `https://test.funktechnik-schubert.de/status`. Bevorzugt wird `public.repair_status_base_url` aus der Admin-Konfiguration. Wenn beides leer ist, gibt Hermes weiterhin relative Links im Format `/status/<token>` zurueck.
|
|
|
|
`SMTP_HOST`, `SMTP_PORT`, `SMTP_USERNAME`, `SMTP_PASSWORD`, `SMTP_FROM_EMAIL`, `SMTP_FROM_NAME`, `SMTP_USE_TLS`
|
|
: Env-Fallback fuer automatische Reparaturstatusmails. Bevorzugt wird die Admin-Konfiguration unter `/settings`. `SMTP_PASSWORD` darf nicht geloggt, committed oder an Athena zurueckgegeben werden. Fuer Apple Mail/iCloud: `smtp.mail.me.com`, Port `587`, TLS/STARTTLS aktiv, Benutzername vollstaendige Mailadresse, Passwort app-spezifisches Passwort.
|
|
|
|
### Athena
|
|
|
|
`HERMES_INTERNAL_URL`
|
|
: Serverseitige URL, ueber die Athena Hermes erreicht.
|
|
|
|
`AUTH_COOKIE_SECURE`
|
|
: Steuert das `Secure`-Flag des Cookies. In Produktion `true`.
|
|
|
|
## Datenbankmigrationen
|
|
|
|
Olympus verwendet Alembic fuer Datenbankmigrationen.
|
|
|
|
Regeln:
|
|
|
|
- Schemaaenderungen immer ueber Alembic.
|
|
- Migrationen muessen ins Repository aufgenommen werden.
|
|
- Vor Deployment `alembic upgrade head` testen.
|
|
- Keine manuellen DB-Aenderungen ausser in Notfaellen.
|
|
|
|
Typischer Ablauf:
|
|
|
|
```bash
|
|
cd backend/hermes
|
|
alembic upgrade head
|
|
```
|
|
|
|
## Sicherheitsprinzipien
|
|
|
|
Verbindliche Regeln:
|
|
|
|
- Keine Tokens im `localStorage`.
|
|
- Keine Tokens im `sessionStorage`.
|
|
- Keine Browserzugriffe auf Hermes.
|
|
- Keine Cross-Origin-Cookies.
|
|
- Hermes bleibt Cookie-frei.
|
|
- Athena setzt HttpOnly Cookies.
|
|
- Mutierende Athena-API-Routes pruefen Same-Origin.
|
|
- Hermes prueft JWTs serverseitig.
|
|
- Hermes prueft RBAC-Berechtigungen serverseitig.
|
|
- Secrets gehoeren in Umgebungsvariablen, nicht in den Code.
|
|
|
|
## Produktionshinweise
|
|
|
|
Fuer Produktion gilt:
|
|
|
|
- HTTPS ist erforderlich.
|
|
- `AUTH_COOKIE_SECURE=true` setzen.
|
|
- Reverse Proxy ist moeglich und soll Forwarded Header korrekt setzen.
|
|
- `HERMES_INTERNAL_URL` muss serverseitig erreichbar sein.
|
|
- `SECRET_KEY` muss stark, geheim und stabil sein.
|
|
- Datenbankmigrationen vor App-Rollout ausfuehren.
|
|
|
|
Lokaler HTTP-Betrieb ist nur fuer Entwicklung gedacht. In Produktion duerfen keine unsicheren Cookie-Einstellungen verwendet werden.
|