Fundament: atomare Mandanten-Transaktionen, iframe-Vorschau, Uploads bis 25 MB, DSGVO-Felder

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 12:29:57 +02:00
co-authored by Claude Opus 5
parent 3e16689b2f
commit 4a25f2cc3b
8 changed files with 243 additions and 13 deletions
+29
View File
@@ -20,4 +20,33 @@ export interface PiiReference {
export const PII_REFERENCE_FIELDS: readonly PiiReference[] = [
{ model: "AuditLog", field: "actorId" },
{ model: "NotificationPreference", field: "userId" },
// ── Craftvia domain (0002_craftvia_domain) ──
{ model: "Customer", field: "createdById" },
{ model: "Team", field: "leaderUserId" },
{ model: "TeamMember", field: "userId" },
{ model: "WorkOrder", field: "createdById" },
{ model: "WorkOrder", field: "teamLeadUserId" },
{ model: "WorkOrderAssignee", field: "userId" },
{ model: "WorkOrderStatusChange", field: "actorId" },
{ model: "ChecklistItem", field: "checkedById" },
{ model: "MaterialUsage", field: "recordedById" },
{ model: "WorkSession", field: "userId" },
{ model: "TimeEntry", field: "userId" },
{ model: "TimeEntry", field: "correctedById" },
{ model: "ActivityNote", field: "authorId" },
{ model: "Document", field: "uploadedById" },
{ model: "Photo", field: "takenById" },
{ model: "VoiceNote", field: "recordedById" },
{ model: "Report", field: "createdById" },
{ model: "Report", field: "teamApprovedById" },
{ model: "Report", field: "approvedById" },
{ model: "Signature", field: "capturedById" },
{ model: "ImportJob", field: "importedById" },
{ model: "ImportJob", field: "confirmedById" },
{ model: "Notification", field: "userId" },
{ model: "SyncOperation", field: "userId" },
{ model: "SyncOperation", field: "resolvedById" },
{ model: "AiGeneration", field: "createdById" },
// Free-text person data of END CUSTOMERS (Customer/Contact/Site/Signature.signerName) is
// tenant business data under data processing — not part of the employee subject export.
];