bb963a796f
- seedDefaultUsers: generiert kryptographisch zufällige Passwörter (crypto/rand) statt hartkodiertes "archivmailrockz" — Passwörter werden einmalig im Terminal angezeigt und können danach nicht wiederhergestellt werden - generateJTI: verwendet crypto/rand (16 Byte, hex) statt time.UnixNano XOR deadbeef Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.5 KiB
6.5 KiB
PROJ-6: Volltext-Suche & Filterung
Status: In Review
Created: 2026-03-12 Last Updated: 2026-03-17
Dependencies
- Requires: PROJ-1 (Authentifizierung) – Suche nur für eingeloggte Nutzer
- Requires: PROJ-5 (Speicherung & Indexierung) – Suchergebnisse kommen aus dem Index
User Stories
- Als Nutzer möchte ich nach Schlüsselwörtern suchen, damit ich relevante E-Mails schnell finden kann.
- Als Nutzer möchte ich Suchergebnisse nach Absender, Empfänger, Datum und Anhang filtern, damit ich die Treffermenge eingrenzen kann.
- Als Nutzer möchte ich Suchergebnisse nach Datum sortieren können (neueste/älteste zuerst).
- Als Nutzer möchte ich Suchbegriffe in den Ergebnissen hervorgehoben sehen (Highlighting).
- Als Nutzer sehe ich nur E-Mails, auf die ich Zugriffsrecht habe, damit Datenschutz gewahrt bleibt.
Acceptance Criteria
- Sucheingabe mit Echtzeit-Vorschau (oder sofortiger Submit)
- Suche in: Betreff, Absender, Empfänger, Body-Text
- Filteroptionen: Datum (von–bis), Absender-Domain, hat Anhang (ja/nein), Label
- Sortierung: nach Relevanz, nach Datum (auf-/absteigend)
- Suchergebnisse paginiert (Standard: 25 pro Seite)
- Suchbegriff in Betreff und Body-Snippet hervorgehoben
- Suchanfragen liefern Ergebnisse in < 2 Sekunden (bei 100.000+ E-Mails)
- Nutzer sehen nur E-Mails in ihren zugewiesenen Postfächern
Edge Cases
- Suchanfrage ohne Ergebnisse → "Keine Ergebnisse" Meldung mit Vorschlägen
- Sonderzeichen in Suchanfrage (", *, ?) → Escaping oder Query-Syntax erlauben
- Suche bei sehr großem Index (1M+ Mails) → Performance-Test erforderlich
- Gleichzeitige Suchanfragen von vielen Nutzern → kein Query-Blocking
Technical Requirements
- Such-Engine: Xapian – die Web-GUI sucht ausschließlich über den Xapian-Index
- Kein SQL-Fulltext-Query gegen PostgreSQL – DB wird nur für Metadaten-Lookup nach Treffern genutzt
- Suchfluss: Web-GUI → API → Xapian-Query → Treffer-IDs → PostgreSQL-Metadaten-Lookup → Antwort
- Xapian QueryParser: AND, OR, NOT, Phrasen (
"exakter Text"), Wildcards (word*), Feldpräfixe (from:,subject:) - Relevanz-Ranking über Xapian BM25Weight
- Snippet/Highlighting über
Xapian::MSet::snippet() ReadonlyDatabasefür parallele Lesezugriffe (mehrere Nutzer gleichzeitig möglich)
- Antwortzeit < 2 Sekunden für Volltext-Suche über 100.000 E-Mails
- Suchanfragen werden für Audit-Log erfasst (optional, konfigurierbar)
Tech Design (Solution Architect)
Komponentenstruktur
Next.js Frontend:
/search
├── SearchBar ← Eingabefeld, Submit bei Enter oder Button
├── FilterPanel ← aufklappbar
│ ├── DateRangePicker ← von–bis Datum
│ ├── DomainFilter ← Absender-Domain Freitext
│ ├── AttachmentToggle ← nur Mails mit Anhang
│ └── LabelFilter ← Label-Auswahl (Mehrfachauswahl)
├── SortControls ← Relevanz / Datum aufsteigend / absteigend
├── ResultsList
│ └── MailCard (pro Treffer)
│ ├── Betreff (Suchbegriff hervorgehoben)
│ ├── Von / An / Datum / Größe
│ ├── Body-Snippet (Suchbegriff hervorgehoben)
│ └── Anhang-Indikator
├── Pagination ← Seiten-Navigation
└── EmptyState ← "Keine Ergebnisse" mit Suchtipps
Go Backend:
GET /api/search
├── Session Middleware ← Auth prüfen
├── Role Filter Builder ← user: nur eigene Postfächer / auditor: alle
├── Xapian QueryParser ← Nutzer-Query parsen (AND/OR/NOT/Wildcards)
├── Xapian ReadonlyDatabase ← Query ausführen, MSet zurückgeben
│ ├── BM25 Relevanz-Ranking ← beste Treffer zuerst
│ └── MSet::snippet() ← Highlighting-Snippets erzeugen
├── PostgreSQL Metadaten-Lookup ← From, To, Subject, Date, Size, Attachments
└── JSON Response Assembly ← Ergebnis zusammenbauen
Suchfluss
Next.js (Browser) Go Backend
│ │
│ GET /api/search │
│ ?q=Rechnung&date_from=2024-01 │
│ &has_attachments=true&page=2 │
│ ────────────────────────────────► │
│ Session prüfen
│ Rolle ermitteln:
│ user → Filter: nur eigene Postfach-IDs
│ auditor → kein Filter
│ │
│ Xapian QueryParser
│ → Query + Datumsfilter + Anhang-Filter
│ │
│ Xapian ReadonlyDatabase
│ → MSet: [doc_id_1, doc_id_5, ...]
│ → Snippets mit Highlighting
│ │
│ PostgreSQL
│ → Metadaten für doc_ids laden
│ │
│ ◄────────────────────────────────
│ { total, page, mails: [...] } │
│ MailCards rendern + highlighten │
Technische Entscheidungen
| Entscheidung | Begründung |
|---|---|
| Xapian für alles, kein SQL-Fulltext | Optimiert für Volltext – PostgreSQL LIKE-Suche wäre bei 100k+ Mails zu langsam |
| ReadonlyDatabase | Beliebig viele parallele Lesezugriffe – kein Blocking bei gleichzeitigen Nutzern |
| Rolle als Xapian-Term | Postfach-ID beim Indexieren als Term gespeichert – Rollenfilter läuft in Xapian, nicht nachträglich in der DB |
| Snippets aus Xapian | MSet::snippet() hebt Suchbegriff im Originaltext hervor – kein separates Rendering nötig |
| Paginierung über Xapian Offset | Nur angefragter Seitenausschnitt zurückgegeben – kein Full-Scan pro Seite |
| PostgreSQL nur für Metadaten | Nach Xapian-Suche werden nur gefundene IDs nachgeschlagen – minimale DB-Last |
URL-State mit nuqs |
Suchparameter in der URL → Back-Button funktioniert, Suchergebnisse sind verlinkbar |
QA Test Results
To be added by /qa
Deployment
To be added by /deploy