## Inhalt
Read-only Inventar-Export für ERPNext (T8 im ERPNext-Ausbau), Vertrag in `docs/erp-inventar-export.md` (v1).
- Endpunkte `/api/v1/export/inventar/{produkte,chargen,bewegungen,ernten}`, nur mit API-Schlüssel und Scope `erp-inventory:read`
- Produkte und Chargen: vollständiges Neulesen, `remainingQuantity` live; Bewegungen mit 5 Minuten Verzögerung (Cursor)
- Lockout sperrt gültige Schlüssel nie; Cursor-Jahr auf 2000–2100 begrenzt; Warnung bei fehlendem `TRUST_PROXY`
- Schlüssel-Oberfläche bietet nur Scopes an, die der Benutzer selbst hat
## Prüfung
- `origin/main` (Paket 0, 10cbe9b) eingemergt; einziger Konflikt `ARCHITECTURE.md` (Punkte 11/12 von main, Punkt 14 mit `erp-inventory:read`)
- Backend: Typecheck ok, Jest 1165/1165
- Frontend: Typecheck ok, Vitest 269/269
- Zwei Sicherheitsreviews, Befunde behoben
## Test plan
- [ ] CI auf main grün
- [ ] Nach Rollout: API-Schlüssel mit `erp-inventory:read` anlegen, in ERPNext als `APICORE_ERP_KEY` hinterlegen, Abgleich-Job einmal laufen lassen
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Nach 20 abgelehnten API-Schlüsseln (fehlerhaft, unbekannt, widerrufen,
abgelaufen) in 15 Minuten antwortet ApiCore der Absender-IP mit 429, auch
für richtige Schlüssel - vor jeder Datenbankabfrage. Gleiches Budget wie
der ERP-Export des Shops. Die Zählung liegt wie der Throttler im Speicher
des Pods und ist auf 10.000 Adressen begrenzt.
Abgelehnte Schlüssel werden mit Grund, öffentlichem Präfix (ack_xxxxxxxx)
und IP protokolliert, nie mit dem Schlüssel. Die Suche bleibt über den
SHA-256-Hash; neu sind Unit-Tests für authenticate (fehlend, unbekannt,
widerrufen, abgelaufen, Scope-Filter, IP-Allowlist, Sperre).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Datenquelle für den nächtlichen Inventar-Abgleich in ERPNext (ERPNext-
Ausbau T8, Entscheidungen E2/E3). Neues Recht erp-inventory:read, auch als
API-Schlüssel-Scope; es öffnet nur diese vier GET-Endpunkte:
- /api/v1/export/inventar/produkte: Artikel (ohne Beschreibung)
- .../chargen: Abfüllungen und Herstellungschargen mit Losnummer, MHD,
Ausgangsmenge, Rest aus dem Bewegungsbuch, Füllmenge g und Sorte
- .../bewegungen?seit=&nach=&limit=: Shop-Verkauf, Retoure, Entnahmen mit
Grund (HOFLADEN = Vertrauensschrank, EIGENBEDARF, BRUCH, PROBE,
SONSTIGES) und Inventur; Cursor über (createdAt, id), Bewegungen erst
nach 5 Minuten Wartezeit, damit der Cursor keine offene Buchung überholt
- .../ernten?jahr=: Nettogewicht kg je Kalenderjahr, wie die Statistik
Alle Listen stabil sortiert, seitenweise (1-500), Parameter mit
class-validator geprüft, Cursor je Endpunkt streng validiert. Feld-
Allowlists: keine Notizen, Benutzer, Standorte, Rohstoffe. Die Gründe gab
es im Datenmodell schon - keine Migration; das Recht legt der Katalog-
Sync beim Start an und gibt es der Rolle Administrator.
Vertrag mit Feldern, Einheiten, Fehlercodes und Beispielen:
docs/erp-inventar-export.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bisher vergab die Oberfläche fest shop-sync:read. Jetzt sind alle
freigegebenen Rechte ankreuzbar (Shop lesen, Shop-Buchungen,
Inventar-Spiegel für ERPNext); vorausgewählt bleibt wie bisher nur
Shop lesen, ohne Haken lässt sich nichts anlegen. Die Liste zeigt, was
ein Schlüssel darf, und der Hinweis nach dem Anlegen nennt das Zielsystem.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Wie der Throttler seit 886b4e7: die Sperre nach abgelehnten Schlüsseln
zählt über rateLimitSubject, damit wechselnde Adressen im eigenen
IPv6-/64 das Budget nicht zurücksetzen. Im Log steht weiter die Adresse.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Befunde aus dem Sicherheitsreview des Branches:
- MEDIUM: Die Sperre lief vor der Schlüsselsuche und zählte auch falsch
aufgebaute Tokens. Wer die gemeinsame Adresse (Cluster-Ausgang, Proxy)
nutzt oder per X-Forwarded-For vortäuscht, hätte ERPNext und den Shop
15 Minuten aussperren können. Jetzt: erst Suche über den Hash; ein
gültiger, nicht widerrufener, nicht abgelaufener Schlüssel kommt immer
durch; 429 nur für unbekannte, widerrufene und abgelaufene Schlüssel;
falsch aufgebaute Tokens sofort 401 ohne Zählung. Beim Start warnt
ApiCore, wenn TRUST_PROXY in Produktion fehlt (im Cluster gesetzt).
- LOW: Bei vollem Speicher verdrängt der Limiter zuerst nicht gesperrte
Einträge, gesperrte erst zuletzt.
- LOW: Bewegungs-Cursor mit Jahr außerhalb 2000-2100 ergab 500, jetzt 400.
- LOW/INFO: Vertrag legt fest, dass ERPNext Produkte und Chargen bei jedem
Lauf vollständig neu liest (Voll-Spiegel) und Bewegungen inkrementell;
remainingQuantity ist live, Bewegungen kommen 5 Minuten verzögert.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Der Server lehnt Rechte ab, die der Anlegende nicht selbst hat. Das UI
bietet jetzt auch nur diese an (z. B. kein shop-sync:write ohne eigenes
Recht) und erklärt, wenn gar keines übrig bleibt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Inhalt
Read-only Inventar-Export für ERPNext (T8 im ERPNext-Ausbau), Vertrag in
docs/erp-inventar-export.md(v1)./api/v1/export/inventar/{produkte,chargen,bewegungen,ernten}, nur mit API-Schlüssel und Scopeerp-inventory:readremainingQuantitylive; Bewegungen mit 5 Minuten Verzögerung (Cursor)TRUST_PROXYPrüfung
origin/main(Paket 0,10cbe9b) eingemergt; einziger KonfliktARCHITECTURE.md(Punkte 11/12 von main, Punkt 14 miterp-inventory:read)Test plan
erp-inventory:readanlegen, in ERPNext alsAPICORE_ERP_KEYhinterlegen, Abgleich-Job einmal laufen lassen🤖 Generated with Claude Code