From 4a25f2cc3bba91577cdaa6da031b0b7bb1c1a705 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 14 Sep 2026 12:29:57 +0200 Subject: [PATCH] Fundament: atomare Mandanten-Transaktionen, iframe-Vorschau, Uploads bis 25 MB, DSGVO-Felder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - db.ts: tenantTransaction() – atomar auch bei RLS_ENFORCED=true (AsyncLocalStorage bindet Operationen an eine craftvia_app-Transaktion, Kontext einmal gesetzt, verschachtelte Aufrufe treten bei, fremder Mandant wird abgewiesen) - services/context.ts: inTransaction(ctx, fn); imports/confirm.ts umgestellt - next.config.ts: EMBEDDABLE_FILE_ROUTES mit frame-ancestors 'self'/SAMEORIGIN (PDF-Vorschau Prüfmaske), proxyClientMaxBodySize 26mb (Import bis 25 MB) - test-rls-enforcement: RLS-URL-Default aus DATABASE_URL (Lane-DBs) - dsgvo/pii-fields: 26 Personenreferenzen des Craftvia-Domänenmodells - ARCHITEKTUR §4.8: Transaktions-, Header-, Upload-, Versions- und PII-Regeln - Test test-tenant-transaction (Commit/Rollback/Fremdmandant/Verschachtelung), grün im Owner- und im RLS-Modus Gate: tsc, lint, build, 31/31 Tests grün. Co-Authored-By: Claude Opus 5 --- docs/craftvia/ARCHITEKTUR.md | 7 ++ next.config.ts | 25 +++++- scripts/test-rls-enforcement.ts | 14 +++- scripts/test-tenant-transaction.ts | 102 +++++++++++++++++++++++++ src/server/db.ts | 60 +++++++++++++++ src/server/dsgvo/pii-fields.ts | 29 +++++++ src/server/services/context.ts | 11 ++- src/server/services/imports/confirm.ts | 8 +- 8 files changed, 243 insertions(+), 13 deletions(-) create mode 100644 scripts/test-tenant-transaction.ts diff --git a/docs/craftvia/ARCHITEKTUR.md b/docs/craftvia/ARCHITEKTUR.md index 1cde6df..6ef129d 100644 --- a/docs/craftvia/ARCHITEKTUR.md +++ b/docs/craftvia/ARCHITEKTUR.md @@ -101,6 +101,13 @@ Konfiguration per Env (`AI_EXTRACTION_PROVIDER=anthropic`, `ANTHROPIC_API_KEY`, ### 4.7 Berichtsinhalt `src/lib/reports/content.ts#ReportContent` (Zod): Snapshot aller Berichtsdaten (Spec §16.2/§17.2) – wird beim Erstellen/Freigeben aus DB gebaut (`services/reports/build-content.ts`) und ist nach `approved` unveränderlich. Änderungen nach Freigabe = neue Version (`lineageId`, `version+1`, alte → `superseded`). +### 4.8 Transaktionen, Header, Uploads (Fundament-Nachträge) +- **Mehrschritt-Schreibvorgänge nur über `inTransaction(ctx, fn)`** (`src/server/services/context.ts`, basiert auf `tenantTransaction` in `db.ts`). Direktes `ctx.db.$transaction(...)` ist bei `RLS_ENFORCED=true` nicht atomar. +- Datei-Routen, die im eigenen iframe angezeigt werden dürfen, stehen in `EMBEDDABLE_FILE_ROUTES` (`next.config.ts`, `frame-ancestors 'self'` / `SAMEORIGIN`); alles andere bleibt `DENY`. +- Request-Bodies über den Proxy: `experimental.proxyClientMaxBodySize = 26mb` (größter Upload 25 MB). +- Berichtsversionen (§4.7): Eine freigegebene Version wird erst `superseded`, wenn die NEUE Version freigegeben wird – so existiert immer ein gültiges freigegebenes PDF. +- Neue Personenreferenz-Felder (`…ById`, `userId`) in `src/server/dsgvo/pii-fields.ts` eintragen. + ## 5. Routen | Bereich | Route | Modul | diff --git a/next.config.ts b/next.config.ts index 5be4174..ab580d4 100644 --- a/next.config.ts +++ b/next.config.ts @@ -31,6 +31,13 @@ const csp = [ "object-src 'none'", ].join("; "); +// Same-origin embedding for inline document previews (PDF viewer in the import review +// mask, document previews). Everything else stays frame-ancestors 'none' / DENY. +const cspEmbeddable = csp.replace("frame-ancestors 'none'", "frame-ancestors 'self'"); + +// Routes that stream stored files and may be shown in a same-origin