Files
archivmail/features/PROJ-9-ordner-und-labels.md
sysops fdb25cb16a feat: Labels-Feature vollständig entfernen (PROJ-9)
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>
2026-04-04 21:32:13 +02:00

8.5 KiB
Raw Permalink Blame History

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 own initSchema() (same pattern as tenantstore). Includes GetLabelsForEmails() 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.LabelID field added for label filtering.
  • internal/api/server.go -- Label post-filter in search handler (Go-level, not Xapian CGO). Search results enriched with label_ids array via batch query. labels field added to Server struct.
  • cmd/archivmail/main.go -- Labelstore wired via srv.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 -- Added MailLabel and LabelRule interfaces plus all label API functions (getLabels, createLabel, updateLabel, deleteLabel, assignLabel, removeLabelFromEmail, getMailLabelIds, createAdminLabel, getAdminLabels, deleteAdminLabel, getLabelRules, createLabelRule, deleteLabelRule). Added label_id parameter to searchEmails(). Named interface MailLabel to avoid conflict with shadcn Label component.
  • 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). Added selectedLabelId state. Label selection triggers re-search with label_id parameter. 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 via onUpdate callback.
  • 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