Handbuch für Admins
Betreiber-Sicht auf die E-Rechnungs-Werkstatt: Rollen, Nutzer, Logs, Templates, Keys, Deploy. Sprache: du-Form, technisch. Als Admin siehst du auch das User-Handbuch, damit du weißt, was normale Nutzer lesen.
1Rollen
Die App kennt zwei Rollen: standard (Regelfall) und admin. Die Rolle liest der Client per GET /api/auth/me und rendert entsprechende Zusatz-Elemente.
Neue Admins ernennen
Unter 👥 Nutzer hast du pro Nutzer den Umschalter Rolle. Auf admin setzen, speichern – der Nutzer sieht beim nächsten Reload seinen Admin-Header (Logs, Nutzer, Admin-Handbuch).
Schutz des letzten Admins
Die API blockiert das Herabstufen des letzten aktiven Admin-Kontos. Wenn du die Rolle nicht ändern kannst, ist entweder kein zweiter Admin vorhanden oder das Konto ist deaktiviert.
2Nutzerverwaltung (/admin-users.html)
Die Nutzerverwaltung listet alle registrierten Konten mit E-Mail, Rolle, Rate-Limit und Erstellungsdatum.
Rate-Limit pro Nutzer
| Wert | Bedeutung |
|---|---|
| 1/min (Default) | Standardwert – reicht für alle Nutzer, die die App im Tagesgeschäft normal bedienen. |
| 5/min, 10/min, … | Anhebung z. B. für Power-User oder für den Batch-Betrieb aus /debug.html. |
| „kein Limit" | Nur für Admin-Konten wählbar. Sinnvoll für Batch-Läufe oder Debug-Sessions. |
Auswirkung auf KI-Kosten
Die Extraktionen laufen mit den Keys der jeweiligen Nutzer – der Betreiber trägt die KI-Kosten nicht. Trotzdem: ein Nutzer, den du auf „kein Limit" hebst, kann bei versehentlichem Batch-Loop schnell mehrere Euro an eigenen Anthropic-Credits verbrennen. Bitte den Nutzer vorher informieren.
3Log-Ansicht (/admin-logs.html)
Zwei Log-Familien liegen auf dem VPS im Container und sind über die Admin-Ansicht paginiert einsehbar. Rotation: täglich, Aufbewahrung 30 Tage.
Access-Log
HTTP-Zugriffe im Combined-Format. Nützlich, um Traffic-Bursts, 4xx/5xx-Spikes oder verdächtige User-Agents zu erkennen.
App-Log
Anwendungslog des Node-Servers: KI-Aufrufe, Extract-Ergebnisse, Validierungs-Ausgänge, Warnings, Errors. Suchmuster für typische Fälle:
| Suche nach | Bedeutung |
|---|---|
| MulterError | Upload-Fehler: Datei zu groß oder unerlaubter MIME-Typ. Nutzer meldet meist „Upload klappt nicht". |
| extract:reject | Extraktion abgelehnt (z. B. leerer Text-Layer bei Text-PDF-Route). Hinweis: Nutzer auf KI- oder Mistral-Route lenken. |
| validation:fail | KoSIT-Prüfung schlug fehl. Betroffene BR-Codes stehen daneben. |
| key:decrypt-fail | Ein serverseitig gespeicherter Schlüssel ließ sich nicht entschlüsseln (siehe Kap. 5). |
Die Ansicht kann auto-refresh (5s) und Download der ganzen Datei.
4Templates verwalten
Templates sind gelernte Regelwerke pro Rechnungslayout. Der Nutzer legt sie unter /settings-templates.html an; Admins können bei Problemen mit reinschauen.
Klassik-Engines
Aktuell orion-classic. Weitere Engines sind vorbereitet (dünner Ableger für hybrid-tabellarische Layouts). Die Engine bestimmt, wie Anker und Regex-Regeln aus dem Beispiel-PDF gelernt werden.
Grenzen
10 Templates pro Nutzer. Anschlag ist eine Serverkonfiguration – ändern erfordert Server-Deploy.
Lernkosten
Ein einmaliger Claude-Aufruf pro Template, ca. 2 ct EK (4 ct VK), gezahlt aus dem Anthropic-Guthaben des Nutzers.
Auto-Match via layoutHash
Beim Upload berechnet der Server einen deterministischen Hash über das Textlayout (Font-Boxen, dominante Textbereiche). Passt der Hash zu einem gespeicherten Template, wird es vorgeschlagen.
Nutzer-Debugging
Wenn ein Nutzer meldet „meine Vorlage passt nicht mehr":
- Log auf
template:hash-missprüfen – das zeigt an, dass der Hash sich geändert hat. - Nutzer bitten, eine aktuelle Beispiel-Rechnung als neues Template anzulegen (Lieferant hat vermutlich das Layout geändert).
- Alte Vorlage kann bleiben oder gelöscht werden.
5API-Keys serverseitig (AES-256-GCM)
Nutzer können ihre Anthropic- und Mistral-Keys wahlweise nur im Browser (localStorage) oder serverseitig verschlüsselt ablegen. Für den serverseitigen Weg gilt:
| Aspekt | Wie umgesetzt |
|---|---|
| Algorithmus | AES-256-GCM – authenticated encryption, verhindert nachträgliche Manipulation. |
| Master-Key | 32 Byte, liegt in der .env auf dem VPS als KEY_MASTER. Wird beim Container-Start eingelesen. |
| Speicherform | Ciphertext + IV + Auth-Tag, Base64-serialisiert in der User-Zeile der DB. |
| Nutzung | Nur zur Laufzeit einer Extract-Anfrage entschlüsselt, wird nicht geloggt und nicht gecacht. |
⚠️ Bei Master-Key-Verlust
Wenn KEY_MASTER verloren geht oder rotiert wird ohne Migration, sind alle serverseitig gespeicherten Keys irreversibel unlesbar. Die Nutzer müssen ihre Keys neu eintragen. Die Extract-Routen fangen den Fehler ab (Log-Marker key:decrypt-fail) und weisen den Nutzer im UI freundlich darauf hin.
.env schreiben.6Deploy & Betrieb
GitHub-Workflow
Deploy erfolgt manuell über den Workflow erechnung-deploy.yml im Repo:
gh workflow run erechnung-deploy.yml --ref claude/analyze-linkedin-app-OncKV
Der Workflow baut das Container-Image, pushed es und startet auf dem VPS docker compose up -d mit dem neuen Tag.
Wo die App läuft
| Komponente | Details |
|---|---|
| Host | Hostinger-VPS 76.13.153.48 |
| Container | erechnung-app, Node/Express + statisches public/ |
| Reverse-Proxy | Traefik mit Let's-Encrypt, öffentliche URL https://erechnung-erstellen.hiltmann.cloud |
| Health-Check | /api/health → { ok: true, version, uptime } |
Logs auf VPS-Ebene
Wenn die App-Logs in der Admin-Ansicht nicht reichen (z. B. Traefik-Fehler, TLS-Probleme), SSH auf den VPS:
ssh root@76.13.153.48 docker logs -f erechnung-app --tail 200 docker logs -f traefik --tail 200 | grep erechnung
7Firmenprofil-JSON-Import
Der Profil-JSON-Import (PR #32) überschreibt alle passenden Felder im Profil. Struktur (verkürzt):
{
"company": {
"name": "…",
"vatId": "DE123456789",
"taxNumber": "…",
"email": "…",
"contactName": "…",
"phone": "…",
"address": {
"line1": "Straße 1",
"postCode": "12345",
"city": "Ort",
"countryCode": "DE"
}
},
"bank": {
"iban": "DE…",
"bic": "…",
"accountHolder": "…"
},
"layout": {
"logoDataUrl": "data:image/png;base64,…",
"headerText": "…",
"footerText": "…"
}
}
Fehlende Felder in der JSON-Datei werden nicht angerührt. Templates werden vom Import nicht angefasst – die bleiben getrennt in der Templates-Tabelle.
8BR-Codes & Validierung – technische Sicht
Die App nutzt den offiziellen KoSIT-Validator (Mustang) mit dem EN-16931-Regelset für ZUGFeRD 2.x und XRechnung 3.x.
Reklassifizierung BR-DE-15 bei ZUGFeRD
KoSIT wirft BR-DE-15 (Buyer Reference) auch bei B2B-ZUGFeRD als Error, wenn die Käufer-Referenz leer ist. In der Praxis ist das im B2B nicht sinnvoll – die App stuft daher BR-DE-15 nur bei ZUGFeRD auf „Warning" zurück. XRechnung bleibt strikt.
Platzhalter „B2B-KEINE-LEITWEG-ID"
Wenn ein Nutzer im Leitweg-Feld den Text B2B-KEINE-LEITWEG-ID stehen lässt (aus Import oder Voreinstellung), erkennt der Server das als bewussten B2B-Marker und lässt die Rechnung passieren, ohne eine echte Leitweg-ID zu erwarten. Bei XRechnung wird das trotzdem als Fehler erkannt – dort ist die echte Leitweg-ID Pflicht.
Relevante BR-Codes im Alltag
| Code | Prüfregel | Häufige Ursache |
|---|---|---|
| BR-CO-26 | Verkäufer muss identifizierbar sein | Firmenprofil unvollständig (weder USt-IdNr. noch Steuernummer) |
| BR-DE-6 | Kontaktdaten Verkäufer | Ansprechpartner/Telefon/E-Mail fehlt bei XRechnung |
| BR-DE-15 | Käufer-Referenz | Bei XR: Leitweg-ID fehlt. Bei ZUGFeRD: reklassifiziert. |
| BR-DE-21 | Rechnungsnummer Pflicht | Feld leer |
| PEPPOL-R001 | Empfänger-Adresse Pflicht | Käufer-Adresse fehlt |
| PEPPOL-R010 | Elektronische Adresse | Käufer-E-Mail fehlt |
9Debug / Batch (/debug.html)
Die Debug-Seite bietet einen client-seitigen Batch-Modus: Du wählst mehrere Dateien aus, die Seite arbeitet sie nacheinander gegen die Extract- und Validate-Routen ab und zeigt eine Tabelle mit Ergebnisstatus pro Datei.
Wozu?
- Regressionscheck nach Deploy: 5–10 typische Rechnungen einwerfen, alle grün?
- Kunden-Support: „Meine Rechnungen laufen alle ins Rot" – hier kannst du en bloc reproduzieren.
- Vor Änderungen an Templates prüfen, ob alte Layouts noch erkannt werden.
Limitierungen
- Ausschließlich client-seitig – jede Datei erzeugt einen echten API-Aufruf und, wenn KI genutzt wird, echte Kosten.
- Kein serverseitiger Bericht: schließt du den Tab, sind die Ergebnisse weg.
- Rate-Limit greift! Für Batch bitte vorher das Limit deines eigenen Kontos hochsetzen (Kap. 2).
10Grenzen der App (Admin-Sicht)
Foto-Scans + viele Positionen
Der aufwendigste Support-Fall. Wenn Nutzer sich melden:
- Frage nach der Herkunft: Fotografiert (Handy) oder Scanner? Handy-Fotos sind fast immer schlechter.
- Empfehle: Vorlagen-Route für wiederkehrende Lieferanten.
- Wenn einmalig: Claude-Extraktion + händisches Nachtragen der Positionen.
Multi-Page-Positionen
KI-Route läuft bis ~3 Seiten Positionen zuverlässig. Für mehr: gelernte Vorlage, oder Nutzer bittet den Rechnungssteller um eine kompaktere Version.
Was kostet ein Template-Fix?
Ein Template neu anlegen kostet den Nutzer erneut ca. 2 ct EK Claude-Kosten. Das ist so wenig, dass „lieber neu machen" fast immer die bessere Antwort ist als „ich versuche die alte Vorlage zu reparieren".
Was die App nicht kann (bewusst)
- Kein Versand von Rechnungen an Empfänger (kein SMTP, kein PEPPOL-Access-Point)
- Keine Archivierung (die App speichert erzeugte Rechnungen nicht dauerhaft – Nutzer laden sie in ihr eigenes DMS/Buchhaltungsprogramm)
- Keine Buchführung, keine Debitorenverwaltung
Admin-Handbuch · E-Rechnungs-Werkstatt · Zugriffsschutz clientseitig via /api/auth/me