# PROJ-81: Anhang-Online-Vorschau (PDF, Bilder) ## Status: In Review **Created:** 2026-08-06 **Last Updated:** 2026-08-06 ## Kontext Mail-Anhänge lassen sich in `/mail/[id]` aktuell nur herunterladen (`downloadMailAttachment`), keine Online-Ansicht im Browser. PROJ-81 ergänzt eine Vorschau direkt im Frontend, damit User Anhänge nicht erst lokal öffnen müssen. ## Dependencies - Baut auf PROJ-7 (E-Mail-Ansicht) auf — Anhang-Liste existiert bereits in `/mail/[id]` ## User Stories - Als User will ich einen PDF-Anhang direkt im Browser ansehen können, ohne ihn herunterzuladen. - Als User will ich Bild-Anhänge (jpg/png/gif) als Vorschau sehen, bevor ich sie herunterlade. - Als User will ich die Vorschau in einem Dialog/Overlay öffnen, ohne die Mail-Ansicht zu verlassen. - Als Admin/Auditor will ich, dass Vorschau nur für Anhänge funktioniert, auf die der jeweilige Tenant/User laut bestehender Zugriffsregeln berechtigt ist (keine neue Sicherheitslücke). ## Acceptance Criteria - [ ] Jede Anhang-Zeile hat einen eigenen "Vorschau"-Button, der die Vorschau als Modal/Dialog über der aktuellen Mail-Ansicht öffnet (bewusst ein separater Button statt Klick auf die ganze Zeile, damit "Vorschau" und "Herunterladen" eindeutig unterscheidbar bleiben — siehe BUG-81-5) - [ ] PDF-Anhänge werden inline gerendert (Browser-natives PDF-Rendering oder eingebetteter Viewer) - [ ] Bild-Anhänge (jpg, png, gif, webp) werden als Bildvorschau angezeigt - [ ] Nicht unterstützte Dateitypen (inkl. Office-Dokumente wie docx/xlsx) zeigen Hinweis "Keine Vorschau verfügbar" + Download-Button bleibt erhalten - [ ] Download-Option bleibt im Dialog zusätzlich verfügbar (Vorschau ersetzt Download nicht) - [ ] Vorschau respektiert bestehende Tenant-Isolation/Zugriffsrechte (kein direkter öffentlicher Link auf Rohdatei) - [ ] Große Anhänge (Performance-Grenze definieren, z.B. > 20 MB) zeigen Warnhinweis statt automatischem Laden ## Edge Cases - Sehr große PDF/Bild-Dateien (>20 MB) → Warnhinweis statt automatischer Vorschau, User muss Laden explizit bestätigen - Passwortgeschützte/verschlüsselte PDFs → Vorschau schlägt fehl, Fehlermeldung + Download-Fallback - Beschädigte/korrupte Anhänge → Fehlermeldung statt Absturz des Dialogs - Sehr viele Anhänge in einer Mail → Performance beim Öffnen mehrerer Vorschauen nacheinander - Anhang wurde per DSGVO-Löschersuchen (PROJ-50) bereits entfernt → Vorschau zeigt "nicht mehr verfügbar" statt Fehler ## Technical Requirements (optional) - Performance: PDF/Bild-Vorschau < 2s Ladezeit bei normaler Dateigröße - Security: Vorschau-Endpunkt muss denselben Auth-/Tenant-Check wie bestehender Download-Endpunkt durchlaufen - Browser Support: Chrome, Firefox, Safari, Edge - **Pflege: `pdfjs-dist` regelmäßig aktuell halten.** Seit dem Fix zu BUG-81-3 läuft das PDF-Parsing über die Bibliothek `pdfjs-dist` im App-Origin (Web Worker, kein iframe/Browser-Sandbox mehr, siehe Implementation Notes unten). Das Sicherheitsniveau der PDF-Vorschau hängt damit direkt an der Code-Qualität dieser Dependency — ein Parser-Bug dort landet im App-Origin, nicht in einer isolierten Browser-Komponente. `pdfjs-dist` ist aktiv gepflegt (Basis des Firefox-eigenen PDF-Viewers) und regelmäßiges CVE-Ziel. Bei Major-/Security-Updates zeitnah aktualisieren, nicht auf den nächsten großen Dependency-Sweep (vgl. PROJ-79) warten. --- ## Tech Design (Solution Architect) ### A) Komponentenstruktur (visuell) ``` Mail-Ansicht (/mail/[id]) +-- Anhang-Liste (bestehend) | +-- Anhang-Zeile | +-- "Vorschau"-Button (NEU) | +-- "Herunterladen"-Button (bestehend, bleibt) +-- Vorschau-Dialog (NEU, Overlay über aktueller Seite) +-- Titelzeile (Dateiname, Schließen-Button) +-- Inhalt (je nach Typ): | +-- PDF-Ansicht (eingebetteter Viewer) | +-- Bild-Ansicht (Vollbild-Bild, zoombar) | +-- "Keine Vorschau verfügbar"-Hinweis (Fallback, u.a. für Office-Dokumente) +-- Ladezustand (Spinner bei großer Datei) +-- Warnhinweis bei sehr großen Dateien ("trotzdem laden?") +-- "Herunterladen"-Button (bleibt zusätzlich verfügbar) ``` ### B) Datenfluss (Klartext, kein neues Datenmodell) - Vorschau nutzt denselben bestehenden Anhang-Endpunkt wie der heutige Download (`/api/mails/{id}/attachments/{index}`), der bereits Tenant-/Zugriffsprüfung durchläuft — kein neuer öffentlicher Link, keine neue Sicherheitsfläche. - PDF und Bilder: Browser stellt Datei direkt dar, keine Serververarbeitung nötig. - Kein neuer Datenbank-Eintrag nötig — Vorschau ist ein reiner Anzeige-Vorgang, keine dauerhaft gespeicherte Information. - Kein Server-seitiger Konvertierungsschritt (Office-Dokumente bewusst außerhalb des Scopes, siehe unten). ### C) Tech-Entscheidungen (Begründung) - **Vorschau als Modal statt neue Seite/Route:** User bleibt im Kontext der Mail, kein Verlust der aktuellen Ansicht/Scroll-Position (laut Klärung des Nutzers gewünscht). - **Bestehenden Anhang-Endpunkt wiederverwenden statt neuen zu bauen:** Zugriffsrechte (Tenant-Isolation, Auth) sind dort bereits korrekt implementiert — Wiederverwendung vermeidet doppelte Sicherheitslogik und Inkonsistenzrisiko. - **Office-Dokumente (docx/xlsx) bewusst außerhalb des Scopes:** Nutzer hat Scope auf PDF+Bilder reduziert. Damit entfällt der größte Aufwandstreiber (Server-seitige Konvertierung, neue Systemabhängigkeit). Reines Frontend-Feature ohne neue Backend-Logik. - **Größenlimit + Warnhinweis statt hartem Blocken:** Verhindert, dass ein einzelner sehr großer Anhang den Browser blockiert, lässt dem User aber die Wahl, ihn trotzdem zu laden. ### D) Abhängigkeiten (Pakete) - PDF-Viewer-Bibliothek im Frontend (Anzeige im Dialog, ohne Download): **`pdfjs-dist@6.2.108`**, ergänzt im dritten Fix-Durchgang (BUG-81-3). Der zunächst genutzte browsereigene Viewer im iframe war nicht browserübergreifend nutzbar. Bilder benötigen keine zusätzliche Bibliothek (natives ``). - Keine neuen Server-/Systemabhängigkeiten. ## Implementation Notes (Frontend, 2026-08-06) Neue Komponente: `src/components/mail/AttachmentPreviewDialog.tsx` Angebunden in `src/app/mail/[id]/page.tsx` (`AttachmentRow`): pro Anhang erscheint ein "Vorschau"-Button, aber nur wenn der Typ vorschaufähig ist; "Herunterladen" bleibt unverändert daneben und zusätzlich im Dialog. Keine neue API-Funktion nötig — der Dialog nutzt das bestehende `downloadMailAttachment(mailId, index)` und damit denselben Endpunkt inkl. bestehender Auth-/Tenant-Prüfung. `src/lib/api/index.ts` musste nicht ergänzt werden. ### Security-Umsetzung (PROJ-61-Kontext) Abweichung vom Tech Design in der Umsetzung, bewusst strenger: - Der Anhang wird per authentifiziertem `fetch` als Blob geholt, **nicht** per direkter Navigation auf die Anhang-URL. - Der vom Server gelieferte `Content-Type` wird **nicht** zum Rendern verwendet. Der Blob wird clientseitig mit einem erzwungenen MIME-Typ aus einer festen Whitelist neu verpackt. Eine als `rechnung.pdf` getarnte HTML-Datei kann dadurch nicht als aktives Dokument im App-Origin ausgeführt werden. - Whitelist: `pdf` → `application/pdf`; `jpg/jpeg/png/gif/webp/bmp` → jeweiliger Bildtyp. **SVG ist bewusst ausgeschlossen** (kann Skripte enthalten — genau der PROJ-61-Vektor). - PDF rendert in `