# 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 `