Files
archivmail/features/PROJ-7-email-ansicht.md
T
sysops d360c9a5ba feat(PROJ-17): Admin Dashboard Systemauslastung immer anzeigen
- Systemauslastungs-Sektion wird immer gerendert (nicht nur bei Erfolg)
- Fehlermeldung wenn /api/admin/system/stats nicht erreichbar ist
- Feature-Status auf In Review gesetzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:43:19 +01:00

185 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROJ-7: E-Mail-Ansicht (Lesen & Anhänge)
## Status: In Progress
**Created:** 2026-03-12
**Last Updated:** 2026-03-12
## Dependencies
- Requires: PROJ-1 (Authentifizierung) nur eingeloggte Nutzer mit Zugriffsrecht
- Requires: PROJ-5 (Speicherung & Indexierung) E-Mail-Daten aus der Datenbank
## User Stories
- Als Nutzer möchte ich eine E-Mail aus den Suchergebnissen öffnen und lesen, damit ich den vollständigen Inhalt sehe.
- Als Nutzer möchte ich Anhänge herunterladen, damit ich auf angefügte Dokumente zugreifen kann.
- Als Nutzer möchte ich die originalen E-Mail-Header einsehen (technische Details), damit ich Routing und Authentizität prüfen kann.
- Als Nutzer möchte ich E-Mails im HTML-Format sehen (mit sanitizierten externen Inhalten), damit die Formatierung erhalten bleibt.
- Als Nutzer möchte ich die Originalmail als EML herunterladen können.
## Acceptance Criteria
- [ ] E-Mail-Detailansicht zeigt: Von, An, CC, Datum, Betreff, Body
- [ ] HTML-Body wird **original** dargestellt kein Entfernen oder Verändern von Inhalten
- [ ] Darstellung in einem vollständig isolierten `<iframe sandbox>` kein JavaScript aus der Mail kann ausgeführt werden
- [ ] Externe Bilder/Ressourcen standardmäßig blockiert, Nutzer kann per Button "externe Inhalte laden" freischalten
- [ ] Fallback auf Plain-Text wenn kein HTML vorhanden
- [ ] Anhang-Liste mit Dateiname, Typ und Größe
- [ ] Anhänge einzeln herunterladbar
- [ ] Header-Ansicht (klappbar) zeigt alle Original-MIME-Header
- [ ] Download der Original-E-Mail als .eml Datei
- [ ] Zugriffsschutz: Nutzer kann nur E-Mails aus eigenen Postfächern öffnen
- [ ] Jeder Zugriff auf eine E-Mail wird im Audit-Log erfasst
## Edge Cases
- E-Mail mit nur Plain-Text → normales Rendering ohne HTML
- HTML mit JavaScript → Script wird durch iframe-Sandbox blockiert, HTML-Inhalt bleibt unverändert sichtbar
- Externe Tracker (Pixel, Links) → standardmäßig blockiert durch CSP, auf Wunsch des Nutzers freischaltbar
- E-Mail mit sehr großen Anhängen (> 100 MB) → Download-Streaming, kein Speicher-Overflow
- E-Mail mit verschachteltem MIME (E-Mail in E-Mail als Anhang) → als EML-Anhang anzeigen
- Nicht unterstützte Zeichenkodierung → graceful Fallback mit Hinweis
## Technical Requirements
- **Kein HTML-Sanitizing** originale Darstellung ohne Veränderung des Inhalts
- Isolation über `<iframe sandbox="allow-same-origin">` JavaScript blockiert, Inhalt originalgetreu
- Externe Ressourcen über CSP (`Content-Security-Policy`) serverseitig blockiert, opt-in per Nutzer-Aktion
- Anhang-Downloads als Stream (kein vollständiges In-Memory-Laden)
- Audit-Log-Eintrag: Nutzer-ID, E-Mail-ID, Zeitstempel bei jedem Lesezugriff
---
## Tech Design (Solution Architect)
### Komponentenstruktur
**Next.js Frontend:**
```
/mail/[message_id]
├── MailHeader
│ ├── Betreff
│ ├── Von / An / CC / Datum / Größe
│ └── HeaderToggle (klappbar)
│ └── RawHeaderView ← alle Original-MIME-Header als Text
├── ActionBar
│ ├── EML-Download Button ← lädt Original-Mail herunter
│ ├── Externe Inhalte laden ← Button, standardmäßig deaktiviert
│ └── Zurück zur Suche
├── MailBody
│ ├── HtmlView ← originales HTML in <iframe sandbox>
│ │ └── ExternalContentBanner ← Hinweis "Externe Inhalte blockiert [Laden]"
│ └── PlainTextView ← Fallback wenn kein HTML vorhanden
└── AttachmentList
└── AttachmentItem (pro Anhang)
├── Icon (nach MIME-Type)
├── Dateiname + Typ + Größe
└── Download-Button ← direkter Stream vom Go-Backend
```
**Go Backend:**
```
GET /api/mails/{message_id}
├── Session Middleware ← Auth prüfen
├── Zugriffsrecht prüfen ← user: nur eigenes Postfach / auditor: alle
├── .m-Datei von Disk lesen ← Pfad aus PostgreSQL
├── AES-256-GCM entschlüsseln ← Schlüssel aus Prozessspeicher
├── MIME-Parser ← Body + Header + Anhang-Metadaten extrahieren
└── JSON-Antwort ← Metadaten + originaler HTML-Body + Anhang-Liste
GET /api/mails/{message_id}/attachments/{index}
├── Session Middleware
├── Zugriffsrecht prüfen
├── Hash aus PostgreSQL ← welche astore/-Datei?
├── astore/-Datei öffnen
├── AES-256-GCM entschlüsseln (stream)
└── HTTP-Streaming-Response ← Content-Disposition: attachment
GET /api/mails/{message_id}/raw
├── Session Middleware
├── Zugriffsrecht prüfen
├── .m-Datei entschlüsseln
└── HTTP-Streaming-Response ← Content-Type: message/rfc822
```
### Datenabruf-Fluss
```
Browser klickt MailCard aus Suchergebnissen
│ GET /api/mails/<message_id>
Zugriffsrecht prüfen
PostgreSQL → store_path
.m-Datei lesen + AES-256-GCM entschlüsseln
MIME-Parser → body_html (original, unverändert), body_plain, headers[], attachments[]
JSON-Antwort an Next.js
Next.js:
├── HTML → <iframe sandbox="allow-same-origin">
│ └── CSP-Header blockiert externe Ressourcen
│ Nutzer klickt "Externe Inhalte laden"
│ → iframe neu laden ohne CSP-Restriction
└── Anhang-Liste → Download-Links
```
### Technische Entscheidungen
| Entscheidung | Begründung |
|---|---|
| **Kein HTML-Sanitizing** | Originale Darstellung kein Inhalt wird verändert oder entfernt |
| **`<iframe sandbox>`** | JavaScript aus der Mail wird blockiert ohne den HTML-Inhalt zu verändern Inhalt bleibt originalgetreu |
| **CSP für externe Ressourcen** | Tracking-Pixel und externe Bilder standardmäßig blockiert Nutzer kann bewusst freischalten |
| **Entschlüsselung nur im Backend** | Verschlüsselte Rohdaten verlassen den Server nie |
| **Anhang-Download als Stream** | Große Anhänge (>100 MB) nie komplett in RAM direkt von Disk zum Browser |
| **Kein Audit-Log bei Lesezugriff** | Bewusste Entscheidung (PROJ-11): Lesezugriffe werden nicht geloggt |
### Abhängigkeiten
**Go Backend:**
| Paket | Zweck |
|---|---|
| `mime`, `mime/multipart` | MIME-Parsing (Stdlib) |
**Next.js Frontend:** Nur shadcn/ui (bereits installiert), kein zusätzliches Paket nötig.
## QA Test Results
_To be added by /qa_
## Deployment
### Lokal bauen
```bash
# Im Projektverzeichnis
npm run build
```
Build-Artefakt liegt danach in `.next/`.
### Auf Server übertragen (192.168.1.131)
```bash
# Next.js-Build + Abhängigkeiten übertragen
rsync -avz --delete \
.next/ \
package.json \
package-lock.json \
next.config.ts \
root@192.168.1.131:/opt/archivmail/frontend/
# Auf dem Server: Abhängigkeiten installieren & Dienst neu starten
ssh root@192.168.1.131 "cd /opt/archivmail/frontend && npm ci --omit=dev && systemctl restart archivmail-frontend"
```
### Voraussetzungen auf dem Server
- Node.js ≥ 20 installiert (`node -v`)
- Verzeichnis `/opt/archivmail/frontend/` existiert
- Systemd-Unit `archivmail-frontend` läuft `npm run start` (Port 3000)
- Go-Backend läuft auf Port 8080, Next.js proxied `/api/*` dorthin (siehe `next.config.ts`)