Backend: - internal/labelstore/ gelöscht (Store, Schema, CRUD) - internal/api/label_handlers.go gelöscht (alle Label-Routen) - internal/api/server.go: labels-Feld + SetLabels() entfernt - internal/api/search_handlers.go: label_id-Filter + Enrichment entfernt - internal/index/index.go: LabelID aus SearchRequest entfernt - internal/imapserver/server.go: labels-Feld + labelbasierte Mailboxen entfernt - cmd/archivmail/main.go: labelstore-Init + SetLabels() entfernt - cmd/archivmail/version.go: labelstore-Modul entfernt, index-Kommentar korrigiert Frontend: - LabelList.tsx, LabelPicker.tsx, LabelsTab.tsx gelöscht - src/lib/api/system.ts: MailLabel/LabelRule-Typen + alle Label-Funktionen entfernt - src/lib/api/index.ts: Label-Exports entfernt - src/app/search/page.tsx: LabelList + selectedLabelId State entfernt - src/app/mail/[id]/page.tsx: LabelPicker + Labels-State entfernt - src/app/admin/page.tsx: LabelsTab + alle Label-Handler/State entfernt Docs: - features/PROJ-9: Status auf Removed gesetzt - features/INDEX.md: PROJ-9 auf Removed gesetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.5 KiB
PROJ-9: Ordner- & Label-Verwaltung
Status: Removed
Created: 2026-03-12 Last Updated: 2026-04-04
Dependencies
- Requires: PROJ-1 (Authentifizierung)
- Requires: PROJ-5 (Speicherung & Indexierung)
User Stories
- Als Nutzer möchte ich E-Mails mit Labels versehen, damit ich sie thematisch organisieren kann.
- Als Admin möchte ich globale Labels definieren, die automatisch beim Import vergeben werden (z.B. nach Absender-Domain oder Import-Quelle).
- Als Nutzer möchte ich meine Suchergebnisse auf ein bestimmtes Label einschränken.
- Als Nutzer möchte ich Labels erstellen, umbenennen und löschen.
Acceptance Criteria
- Nutzer können Labels erstellen (Name, Farbe)
- E-Mails können mit mehreren Labels versehen werden
- Label-Filter in der Suche verfügbar
- Keine IMAP-Ordnerstruktur – das System ist ein Archiv, keine Ordnerhierarchie wird übernommen
- Admin kann Regeln für automatische Label-Vergabe beim Import definieren (z.B. nach Absender-Domain)
- Admin kann globale Labels definieren (für alle Nutzer sichtbar)
- Löschen eines Labels entfernt es von allen E-Mails, löscht E-Mails nicht
- Label-Übersicht in der Seitenleiste mit E-Mail-Anzahl pro Label
Edge Cases
- Label-Name bereits vergeben → Fehlermeldung
- E-Mail wird gelöscht aber Labels bleiben → Labels bleiben erhalten, E-Mail-Referenz entfernt
- Sehr viele Labels (> 100) → Suchfeld in der Label-Auswahl
Technical Requirements
- Labels: n:m-Beziehung zwischen E-Mails und Labels
- Performance: Label-Filter darf Suchantwortzeit nicht verdoppeln
Tech Design (Solution Architect)
Komponentenstruktur
Next.js Frontend:
Seitenleiste (global, alle Seiten)
└── LabelList
├── Label-Eintrag (Name, Farbe, Anzahl) ← klickbar → filtert Suche
├── [+ Label erstellen] Button
└── Suchfeld (bei > 10 Labels)
Label-Verwaltung (Inline / Modal)
├── LabelForm
│ ├── Name (Textfeld)
│ └── Farbe (Color-Picker, 8 Vorschläge)
└── LabelItem-Aktionen
├── Umbenennen
└── Löschen (mit Bestätigung)
E-Mail-Ansicht (PROJ-7, Erweiterung)
└── LabelPicker
├── Aktuelle Labels der Mail (als Badges)
├── Dropdown: Labels hinzufügen/entfernen
└── [+ Neues Label] Shortcut
Admin-Bereich (/admin/labels)
├── Globale Labels verwalten
└── Auto-Label-Regeln
├── RegelListe
└── RegelForm
├── Bedingung: from-Domain / Import-Quelle / Betreff enthält
└── Aktion: Label zuweisen
Go Backend:
Label-API
├── GET /api/labels ← alle Labels des Nutzers + globale
├── POST /api/labels ← neues Label anlegen
├── PATCH /api/labels/{id} ← umbenennen / Farbe ändern
├── DELETE /api/labels/{id} ← löschen (entfernt von allen Mails)
├── POST /api/mails/{id}/labels ← Label einer Mail zuweisen
└── DELETE /api/mails/{id}/labels/{label_id} ← Label entfernen
Admin Label-API
├── POST /api/admin/labels ← globales Label anlegen
├── GET /api/admin/label-rules ← Auto-Label-Regeln
├── POST /api/admin/label-rules ← Regel anlegen
└── DELETE /api/admin/label-rules/{id}
Label-Filter in Suche (Erweiterung PROJ-6)
└── Xapian-Term "label:<label_id>" pro Mail
→ Label-Filter läuft direkt in Xapian
Datenmodell
Tabelle labels:
| Feld | Beschreibung |
|---|---|
id |
Interne ID |
name |
Label-Name (eindeutig pro Nutzer) |
color |
Hex-Farbe (z.B. #e74c3c) |
owner_id |
Nutzer-ID (NULL = globales Admin-Label) |
created_at |
Erstellungszeitpunkt |
Tabelle email_labels – n:m Verknüpfung:
| Feld | Beschreibung |
|---|---|
email_id |
Referenz auf emails |
label_id |
Referenz auf labels |
assigned_at |
Zeitpunkt der Zuweisung |
assigned_by |
user / auto-rule / import |
Tabelle label_rules – Auto-Label beim Import:
| Feld | Beschreibung |
|---|---|
id |
Interne ID |
condition_field |
from_domain / source / subject_contains |
condition_value |
z.B. example.com oder imap-account-1 |
label_id |
Welches Label vergeben |
Label-Filter in Xapian
Beim Indexieren einer Mail werden ihre Labels als Xapian-Terms gespeichert:
Label "Kunde" → Term: "label:42"
Label "Projekt" → Term: "label:17"
Suche mit Label-Filter läuft vollständig in Xapian – kein zusätzlicher DB-Join nötig. Labels werden beim Zuweisen/Entfernen sofort im Xapian-Dokument aktualisiert.
Technische Entscheidungen
| Entscheidung | Begründung |
|---|---|
| Labels statt Ordner | Archiv hat keine Hierarchie – eine Mail kann mehrere Labels haben, aber nicht in mehreren Ordnern gleichzeitig sein |
| Label-Terms in Xapian | Filter läuft direkt bei der Suche – kein nachträglicher DB-Join, keine Verdopplung der Antwortzeit |
| Globale Labels (owner_id NULL) | Admin definiert unternehmensweite Labels – Nutzer können sie nicht löschen, nur zuweisen |
| Auto-Label-Regeln | Importierte Mails werden sofort kategorisiert – kein manueller Aufwand für Bulk-Importe |
assigned_by-Feld |
Nachvollziehbar ob Label manuell, per Regel oder beim Import vergeben wurde |
Abhängigkeiten
Next.js Frontend:
| Paket | Zweck |
|---|---|
shadcn/ui |
Badge, Popover, Color-Picker-Basis (bereits installiert) |
Go Backend: Nur Stdlib + pgx (bereits vorhanden).
Implementation Notes (Backend)
Implemented 2026-03-18:
internal/labelstore/store.go-- Full CRUD for labels, email-label assignments, and auto-label rules. Uses owninitSchema()(same pattern as tenantstore). IncludesGetLabelsForEmails()batch helper for search enrichment.internal/api/label_handlers.go-- All 10 HTTP handlers +SetLabels()wiring method. Routes registered in setter (same pattern as SetLDAP/SetTenants). Input validation: color hex format, allowed condition fields, mail ID via SEC-22 regex.internal/storage/migrations/012_labels.sql-- Reference SQL (actual schema applied by labelstore.initSchema).internal/index/index.go--SearchRequest.LabelIDfield added for label filtering.internal/api/server.go-- Label post-filter in search handler (Go-level, not Xapian CGO). Search results enriched withlabel_idsarray via batch query.labelsfield added to Server struct.cmd/archivmail/main.go-- Labelstore wired viasrv.SetLabels(labelSt).
Design deviation from spec: Label filtering uses Go-level post-filter on search results (same pattern as tenant isolation fallback) instead of Xapian boolean terms. Reason: the Xapian integration goes through CGO with a C wrapper; adding label terms would require modifying both the C wrapper and the Go bridge. The post-filter approach is simpler and consistent with existing patterns.
Implementation Notes (Frontend)
Implemented 2026-03-18:
src/lib/api.ts-- AddedMailLabelandLabelRuleinterfaces plus all label API functions (getLabels, createLabel, updateLabel, deleteLabel, assignLabel, removeLabelFromEmail, getMailLabelIds, createAdminLabel, getAdminLabels, deleteAdminLabel, getLabelRules, createLabelRule, deleteLabelRule). Addedlabel_idparameter tosearchEmails(). Named interfaceMailLabelto avoid conflict with shadcnLabelcomponent.src/components/LabelList.tsx-- Sidebar component showing all labels with colored circles, selection state, create/edit/delete inline forms, color picker (8 preset colors). Global labels show lock icon and cannot be deleted. Hidden on mobile (hidden md:block).src/components/LabelPicker.tsx-- Popover-based label assignment widget for mail detail view. Shows assigned labels as colored badges. Popover lists all labels with checkbox-style toggles for assign/remove.src/app/search/page.tsx-- Integrated LabelList as left sidebar (w-48). AddedselectedLabelIdstate. Label selection triggers re-search withlabel_idparameter. Layout uses flex with sidebar and main content area.src/app/mail/[id]/page.tsx-- Integrated LabelPicker after mail header card. Loads all labels and assigned label IDs on mount. Updates label assignments viaonUpdatecallback.src/app/admin/page.tsx-- Added "Labels" tab (superadmin only) with two sections: Global Labels management (create/delete with color picker) and Auto-Rules management (create/delete with condition field/value/label selection).
QA Test Results
To be added by /qa
Deployment
To be added by /deploy