L10b Betrieb & Aufräumen: /api/v1 über gemeinsamen Adapter, einheitliches Fehlerformat, Rate Limiting

Aufräumpunkt a: Die lane-lokalen API-Kontexte (imports/_context.ts, sync/api-context.ts,
reports/http.ts, work-orders/_http.ts mit moduleGuard) sind entfernt. Alle v1-Routen laufen über
requireApiContext (DB-autoritative Rechte, 401/403) und withApi/toErrorResponse (respond.ts):
- Fehlerformat überall { error: { code, message, details? } }; invalid und blocked → 422,
  conflict → 409, payload_too_large → 413, rate_limited → 429 + Retry-After.
- Same-Origin-Prüfung in withApi für jede Mutation vor der Anmeldung (vorher fehlte sie bei
  imports, reports und work-orders).
- Rate Limiting je Nutzer mit rate-limit.ts: api (API_RATE_LIMIT_PER_MINUTE, 300/min) und
  apiField für sync/uploads/field (API_FIELD_RATE_LIMIT_PER_MINUTE, 1200/min).
- Clients angepasst: Import-Uploader liest das neue Fehlerformat, Upload/Outbox werten 422 als
  endgültig ungültig (429 bleibt transient mit Backoff).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 18:19:19 +02:00
co-authored by Claude Opus 5
parent a7d4b02a13
commit fb993a7730
29 changed files with 333 additions and 483 deletions
+4 -2
View File
@@ -43,12 +43,14 @@ export function ImportUploader() {
if (e.lengthComputable) setState({ kind: "uploading", percent: Math.round((e.loaded / e.total) * 100), name: file.name });
};
xhr.onload = () => {
const res = (xhr.response ?? {}) as { id?: string; error?: string; message?: string };
// Unified /api/v1 error format: { error: { code, message, details? } }
const res = (xhr.response ?? {}) as { id?: string; error?: { code?: string; message?: string } };
if (xhr.status === 201 && res.id) {
setState({ kind: "done", name: file.name, id: res.id });
router.refresh();
} else {
const code = res.message && KNOWN_ERRORS.has(res.message) ? res.message : res.error && KNOWN_ERRORS.has(res.error) ? res.error : "error";
const { code: errCode, message } = res.error ?? {};
const code = message && KNOWN_ERRORS.has(message) ? message : errCode && KNOWN_ERRORS.has(errCode) ? errCode : "error";
setState({ kind: "error", code });
}
if (inputRef.current) inputRef.current.value = "";