feat: Inventar-Export für ERPNext (erp-inventory:read) #1

Merged
Nick.Rosenloecher merged 8 commits from feat/erp-inventar-export into main 2026-10-13 05:39:01 +00:00
8 Commits
Author SHA1 Message Date
Nick Szaunig 9c332d4716 Merge remote-tracking branch 'origin/main' into feat/erp-inventar-export
# Conflicts:
#	ARCHITECTURE.md
2026-10-13 05:58:30 +02:00
Nick SzaunigandClaude Opus 5.5 6505411761 fix(ui): API-Schlüssel nur mit Rechten anbieten, die der Nutzer selbst hat
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]>
2026-10-12 12:00:03 +02:00
Nick SzaunigandClaude Opus 5.5 fe7b61c3c1 fix(api-keys): gültige Schlüssel nie durch die Fehlversuchs-Sperre abweisen
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]>
2026-10-12 12:00:03 +02:00
Nick SzaunigandClaude Opus 5.5 e28cc8e0f3 fix(api-keys): Fehlversuche je Client-Netz zählen (IPv6 /64)
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]>
2026-10-12 11:47:05 +02:00
Nick Szaunig e357e8be9a Merge remote-tracking branch 'origin/main' into feat/erp-inventar-export 2026-10-12 11:44:31 +02:00
Nick SzaunigandClaude Opus 5.5 c4cdb63414 feat(ui): Berechtigungen beim Anlegen eines API-Schlüssels wählbar
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]>
2026-10-12 11:43:57 +02:00
Nick SzaunigandClaude Opus 5.5 68f81fabd4 feat(erp-export): nur lesender Inventar-Spiegel für ERPNext (T8)
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]>
2026-10-12 11:43:57 +02:00
Nick SzaunigandClaude Opus 5.5 1a1e70c94b feat(api-keys): Fehlversuchs-Sperre je IP und Log ohne Schlüssel
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]>
2026-10-12 11:43:56 +02:00