Commit Graph

105 Commits

Author SHA1 Message Date
sysops 730099d2aa fix(sec): Cross-Tenant-IDOR bei IMAP-Konten schließen
domain_admin sah und konnte IMAP-Konten (inkl. Credentials) fremder
Tenants auflisten, löschen, synchronisieren und umkonfigurieren, da
Store.List() für Admins ungefiltert alle Konten lieferte und die
Einzelhandler nur den Owner, nicht den Tenant prüften.

- Store.List() filtert jetzt nach tenant_id, außer für superadmin
- Store.Create() setzt tenant_id beim Anlegen
- Alle Einzelhandler (delete/start-import/progress/sync/update)
  prüfen zusätzlich tenantAccessAllowed()
2026-06-12 23:26:31 +02:00
sysops d07e65021f fix(tenants): NULL-Scan-Fehler in tenantstore.List() beheben
ldap_url kommt via LEFT JOIN tenant_ldap und ist NULL für Mandanten
ohne LDAP-Konfiguration. Scan in *string schlug fehl ("cannot scan
NULL into *string") und ließ GET /api/admin/quotas mit 500 fehlschlagen
("Quota-Daten konnten nicht geladen werden").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 22:38:28 +02:00
sysops a3bab16f07 fix(imap): IMAP/POP3-Import in korrekten Tenant-Index schreiben
IMAP- und POP3-Importer haben Mails immer nur in emails_global
indexiert (TenantID nie gesetzt, idxMgr.Global() statt
ForTenant(tenantID)). Dadurch fehlten neue Mails ab dem letzten
Server-Neustart im Tenant-Index (Suche zeigte veraltete Ergebnisse).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 21:15:10 +02:00
sysops f32f83ff8e fix(PROJ-28): Invite-Token Pflicht bei Signup — TOCTOU + Enumeration-Leak schließen
- Signup ohne Invite-Token gibt 400 zurück (war: optional)
- Use() statt Peek() vor User-Erstellung: verhindert TOCTOU bei parallelen
  Requests mit demselben Token und Enumeration via "Token noch gültig?"
- invite_used Audit-Eintrag ergänzt
- Doppeltes IsConfigured()-Check entfernt
- Frontend: ohne ?invite= im URL wird Formular nicht gerendert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 18:20:07 +02:00
sysops 15a5da33fd feat(PROJ-13): OpenAPI 3.0 Spec + GET /api/v1/docs Endpoint
Serves the static OpenAPI YAML via go:embed. Completes the last
open acceptance criterion for PROJ-13. PROJ-44 marked Deployed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 18:03:58 +02:00
sysops fa9f77782c fix(sec): Authorization-Bypässe und Path-Traversal schließen, Xapian-Doku bereinigen
- SEC: requireMailAccess auf GET /api/threads/{threadID} — superadmin/domain_admin konnten Mail-Metadaten lesen
- SEC: requireMailAccess auf POST /api/export/ediscovery — superadmin/domain_admin konnten bis zu 10k EML exportieren
- SEC: V1-API user-role Keys müssen 'contact=' angeben — verhindert vollständige Tenant-Enumeration
- SEC: Domain-Regex-Validierung in handleCertACME vor filepath.Join und certbot-Aufruf
- docs: README und config.test.yml auf Manticore Search aktualisiert (kein Xapian mehr)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 23:55:31 +02:00
sysops a1c4e59fff fix: Date-Parsing-Fallback für nicht-standard MTA-Datumsformate
mailparser: weitere Layouts (Timezone +02:00 mit Doppelpunkt, ohne Sekunden)
storage: GetReceivedAts() für Batch-Lookup von received_at
search_handlers: received_at als Fallback wenn pm.Date.IsZero()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 23:36:18 +02:00
sysops 726dd78f3a fix(PROJ-4): SMTP tenant routing: header fallback for BCC journaling
resolveTenant() now tries RCPT TO first, then falls back to parsing
To/Cc/From headers. Needed because BCC-journaled mails arrive with
RCPT TO = the archive's own BCC address, not the real recipient's domain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 14:56:32 +02:00
sysops 799c828548 feat(PROJ-45): IMAP per-folder UID-tracking, UIDVALIDITY-check + reindex OCR protection
- scheduler.go: BUG-1 fix — preserve stored uid_validity when server returns 0
- scheduler.go: BUG-2 fix — replace inline switch with DecideResync() call
- scheduler.go: SetAuditLogger wired; imap_uidvalidity_reset audit event
- cmd_reindex.go: read existing attachment_text before IndexSync to prevent
  Manticore REPLACE INTO from wiping OCR text written by the OCR worker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 14:56:28 +02:00
sysops 4151b6f8c5 feat(PROJ-45): IMAP Per-Folder UID-Tracking + UIDVALIDITY-Check
- FolderState: GetFolderState, UpsertFolderState, ListFolderStates, DecideResync
- syncFolder nutzt per-folder UID-Tracking statt globalem highest_uid
- UIDVALIDITY-Check loest automatisch Full-Resync aus
- imap_folder_state Tabelle in initSchema (CREATE TABLE IF NOT EXISTS)
- SetAuditLogger in main.go verdrahtet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 10:49:14 +02:00
sysops 16013e8b66 fix(PROJ-44): OCR-Tenant-Routing nutzt kanonische DB-tenant_id
Strukturbug auf 132 gefunden: Tenant-User (Rolle user) sahen ihren
OCR-Text nicht, obwohl ocr_chars>0 in PostgreSQL stand. Ursache:

- OCR-Worker hat in den per Job.TenantID gewaehlten Index geschrieben.
  Beim Reprocess via CLI kam TenantID aus dem Submitter-Kontext und
  konnte vom in emails.tenant_id gespeicherten Wert abweichen.
- /ocr-text-Endpoint hat fuer die Index-Auswahl session.TenantID
  benutzt. Bei Admin/Auditor (nil Session-Tenant) wurde immer global
  gelesen, auch wenn die Mail einem Tenant gehoert.

Fix: Beide Stellen lesen jetzt die TenantID **immer** aus
storage.GetTenantForMail(emails.tenant_id) und routen den
Manticore-Index entsprechend. ACL-Check im Endpoint bleibt
unveraendert auf session.TenantID == mail.tenant_id — die
Tenant-Isolation wird nicht aufgeweicht.

Edge cases:
- Mail mit tenant_id NULL: GetTenantForMail liefert nil -> globaler
  Index (vorher und nachher gleich).
- DB-Fehler beim Lookup: faellt auf nil zurueck -> globaler Index,
  liefert leeren Text fuer Tenant-Mails -> 404. Safe (keine
  Querleckage zwischen Tenants).
2026-05-10 23:13:57 +02:00
sysops 5e1a51b028 fix(PROJ-44): SNIPPET via SELECT statt CALL SNIPPETS (Go MySQL-Treiber-Kompatibilitaet)
CALL SNIPPETS liefert einen anderen MySQL-Pakettyp als SELECT, den der
Go-Treiber (go-sql-driver/mysql) mit "malformed packet" ablehnt.
SELECT SNIPPET(text, query) FROM table ist die korrekte Alternative
fuer Manticore 25.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:53:27 +02:00
sysops bb71ef2fd1 fix(PROJ-44): snippet + match_field in enrichedHit-Struct und JSON-Response verdrahten
Die enrichedHit-Struct in search_handlers.go fehlten die PROJ-44-Felder
Snippet und MatchField, sodass die vom Index berechneten Snippets in der
API-Response verworfen wurden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:48:20 +02:00
sysops a4fe2c6b64 fix(PROJ-44): CALL SNIPPETS entfernt Options-Args (Manticore 25 akzeptiert exakt 3)
Manticore 25.0.0 wirft SNIPPETS() expects exactly 3 arguments wenn
zusätzliche Options-Strings übergeben werden. Standard-Marker <b>/<b/>
sind Manticore-Default, daher sind keine Options nötig.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:44:05 +02:00
sysops 032892bc2b fix(PROJ-44): IMAP+POP3 Live-Import triggert OCR-Worker
Bisher haben nur der SMTP-Pfad und der Boot-Backfill ocrWorker.Submit
gerufen. IMAP- und POP3-Importer riefen nur idx.IndexSync auf —
neue Mails blieben dadurch dauerhaft in ocr_status='pending' (auf 132
44 Tage 54 Mails so haengen geblieben).

Fix: Importer-Strukturen bekommen einen optionalen ocrSubmit-Callback,
in main.go via SetOCRSubmit gehookt. Kein Import von internal/ocr in
die Importer-Packages -> kein Risiko von Cycles. Submit ist
non-blocking; bei Mails ohne Attachments markiert der Worker selbst
'skipped'.
2026-05-10 22:23:24 +02:00
sysops a44fd1ae44 feat(PROJ-44): ocr_status/ocr_chars in MailDetail + v1 API
GET /api/mails/{id} liefert jetzt ocr_status + ocr_chars fuer das GUI-
Badge und den konditionalen Download-Button. Die externe v1-API
(/api/v1/mails/{id}) bekommt zusaetzlich ocr_status, damit CRM-
Integrationen entscheiden koennen, ob ein OCR-Text-Pull lohnt.
2026-05-10 22:21:05 +02:00
sysops 62a130d208 feat(PROJ-44): GET /api/mails/{id}/ocr-text + Audit-Event
Neuer Endpoint liefert den OCR-extrahierten Reintext als text/plain
mit Content-Disposition. ACL identisch zu /raw (Tenant-Isolation,
Auditor-Regeln, User-Ownership-Check). Status-Mapping:
  done + chars>0 -> 200, attachment "<id16>.ocr.txt"
  pending        -> 202 JSON {"error":"ocr_pending"}
  skipped/failed/disabled/empty -> 404 JSON {"error":"ocr_not_available"}
Jeder erfolgreiche Download landet im Audit-Log als mail:ocr_download.
2026-05-10 22:20:59 +02:00
sysops 7b75433999 feat(PROJ-44): Snippet + match_field fuer Suche, GetAttachmentText
Hit-Struct um Snippet + MatchField erweitert. enrichHitsWithSnippets
fuellt diese pro Treffer: detectMatchField probt subject>body>
attachment_text>attachment_names>from_addr>to_addr; buildSnippet ruft
CALL SNIPPETS mit <b>-Markern. Snippet-Fehler droppen den Treffer nicht.

AttachmentTextReader-Interface + Manticore-Implementation
GetAttachmentText liefert den indexierten OCR-Text fuer den neuen
/ocr-text-Endpoint.
2026-05-10 22:20:52 +02:00
sysops 5078830469 feat(PROJ-44): ocr_chars-Spalte + SetOCRResult-Helper
DB-Schema bekommt eine idempotente ocr_chars BIGINT-Spalte (Default 0).
SetOCRResult schreibt status und chars atomar; GetOCRMeta liest beide
mit COALESCE-Defaults. Der OCR-Worker ersetzt jeden SetOCRStatus-Call
durch SetOCRResult und uebergibt die extrahierte Zeichenzahl bei 'done'.
2026-05-10 22:20:46 +02:00
sysops d71d20d869 fix(PROJ-35): hashMailID maskiert Top-Bit für positive int64
Vorheriger Fix (int64-Cast) erzeugte für die obere uint64-Hälfte negative
Werte. Manticore weist negative IDs beim INSERT/REPLACE zurück
("Negative document ids are not allowed"), nur SELECT akzeptiert sie.

Lösung: Bit-Mask 0x7FFFFFFFFFFFFFFF — Top-Bit immer 0, Result in
[0, 2^63-1]. 63-Bit-Hash-Space reicht für jede realistische Mail-Anzahl.
2026-05-08 22:53:20 +02:00
sysops 7ba677e4b5 fix(PROJ-35): hashMailID liefert int64 statt uint64
Manticore akzeptiert in `id`-bigint nur signed int64. Der mysql-Treiber
serialisiert Parameter als Dezimal-String → uint64-Werte > int64.MaxValue
führten zu "number ... is out of range". Fix: int64(h.Sum64())
verlustfreier Bit-Cast — bestehende Dokumente bleiben erreichbar.

Auch: PROJ-35-Spec auf In Progress + Implementation Notes/Pitfalls/QA-Block,
INDEX.md-Status-Update.
2026-05-08 22:40:25 +02:00
sysops 6d835aefac fix(PROJ-35): OCR Tempdir auf storage_dir umleiten
Mit systemd ProtectSystem=strict ist /tmp fuer den Service read-only.
ocr.SetTempDir(storage_path/ocr-tmp) nutzt einen RW-Pfad innerhalb der
ohnehin freigegebenen ReadWritePaths.
2026-05-08 22:19:20 +02:00
sysops 0bda21033e feat(PROJ-35): OCR & Anhang-Volltext-Indexierung
Asynchrone OCR fuer PDF- und Bild-Anhaenge via tesseract + poppler-utils.
Extrahierter Text wird in Manticore (attachment_text) gespeichert und ist
ueber die normale Volltextsuche auffindbar.

- internal/ocr: ExtractText + Worker (queue + drain)
- internal/storage/ocr.go: SetOCRStatus, OCREnabled, GetMailsByOCRStatus
- emails.ocr_status (pending|done|failed|skipped|disabled)
- tenants.ocr_enabled (Default TRUE, opt-out)
- Manticore: attachment_text-Feld + UpdateAttachmentText
- Boot-resume: pending Jobs nach Restart automatisch in die Queue
- CLI: archivmail ocr-reprocess --tenant N --status pending|failed|all
- update.sh: tesseract-ocr + poppler-utils optional installieren
2026-05-08 22:11:17 +02:00
sysops 2a91f6e249 fix: IMAP-Serveradresse dynamisch aus Backend laden
Hardcodierte 192.168.1.131 in settings/page.tsx ersetzt durch
dynamischen API-Call GET /api/system/info → {fqdn, imap_port}.
Fallback auf window.location.hostname wenn API nicht antwortet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:06:58 +02:00
sysops 3b05e949dd feat(PROJ-13,PROJ-42): REST API v1 + Gespeicherte Suchanfragen
PROJ-13: Externe REST API für CRM/ERP-Anbindung
- API-Key Middleware mit SHA-256-Hash-Lookup + Token-Bucket Rate-Limiter
- GET /api/v1/mails — Suche mit Paginierung (max 100/Seite)
- GET /api/v1/mails/{id} — Mail-Metadaten als JSON
- GET /api/v1/mails/{id}/raw — Original-EML Download
- Admin-Endpoints: POST/GET/DELETE /api/admin/apikeys
- Tenant-Isolation, Audit-Log, 405 für non-GET Methoden

PROJ-42: Gespeicherte Suchanfragen
- Tabelle saved_searches (user_id, tenant_id, name, query_json)
- GET/POST/DELETE /api/searches/saved mit Ownership-Check
- Frontend: "Suche speichern"-Button + Popover mit gespeicherten Suchen
- shadcn/ui Komponenten, Loading/Empty States

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 10:54:26 +02:00
sysops 9298216ce0 feat(PROJ-40,PROJ-41): Prometheus Metriken + Dashboard Zeitreihe
- PROJ-40: /api/health mit Version+Uptime, /metrics Prometheus-Format
  (mails_last_60min/24h/7d/30d, mails_total, storage_bytes, tenants_total,
   users_total, uptime_seconds) — Token-Schutz optional konfigurierbar
- PROJ-41: GET /api/admin/stats/timeseries (30-Tage tagesgenau, Tenant-scoped)
  + SVG-Balkendiagramm im Dashboard (Mail-Eingang letzte 30 Tage)
- storage.DBQueryRow() Helper für Metrics-Queries ohne Pool-Exposition
- config.MetricsConfig (enabled, token) in config.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 21:10:42 +02:00
sysops 963a324476 fix(PROJ-39): sanitizeFilename-Konflikt beheben — in ediscovery.go umbenannt
sanitizeFilename war doppelt deklariert (server.go + ediscovery.go).
Funktion in ediscovery.go zu sanitizeExportFilename umbenannt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:56:27 +02:00
sysops a82e854cfc feat(PROJ-39): eDiscovery Export + Feature-Specs PROJ-40–43
- Neuer POST /api/export/ediscovery Handler (internal/api/ediscovery.go)
- Route in server.go registriert
- Feature-Specs PROJ-39 bis PROJ-43 angelegt
- INDEX.md aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:55:37 +02:00
sysops 2bab61209c chore: Modulname github.com/archivmail → archivmail
Go-Modul in go.mod und allen 45 Go-Dateien umbenannt.
2026-04-05 20:37:35 +02:00
sysops eb48081c5e feat(PROJ-38): rethread — rückwirkendes Mail-Threading 2026-04-05 20:28:50 +02:00
sysops b252172cc7 feat(PROJ-26,PROJ-38): IMAP LDAP-Auth + Mail-Threading 2026-04-05 20:17:41 +02:00
sysops 956b5b6d5f feat(PROJ-36): archivmail recompress — Nachkomprimierung bestehender Mails
Neuer CLI-Subcommand: archivmail recompress [--dry-run]
Komprimiert alle unkomprimierten Dateien im Store atomisch (temp + rename).
Überspringt bereits komprimierte Dateien (Magic-Byte 0x01).
Aktualisiert storage_objects und emails.storage_id in der DB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 01:30:42 +02:00
sysops 27d45f58e8 feat(PROJ-36,PROJ-37): gzip-Kompression + Attachment-Deduplication
Sprint 1: Emails werden vor AES-256-GCM optional gzip-komprimiert (compress: true).
Magic-Byte 0x01 als Prefix ermöglicht backward-kompatibles Load() für Legacy-Dateien.
Neue DB-Tabelle storage_objects trackt Kompressions-Metadaten.

Sprint 2: Attachments werden via SHA-256 dedupliziert — gleicher Anhang in N Mails
wird nur einmal gespeichert. Neue Tabellen: attachments, email_attachments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 01:19:51 +02:00
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
sysops bde291901a fix: Manticore als Standard-Backend, Xapian-Cleanup in update.sh
- main.go: Default-Backend von "xapian" auf "manticore" geändert
- index.go: Kommentar und Fehlermeldung aktualisiert
- update.sh: Xapian-Verzeichnis wird nach erfolgreichem Manticore-Reindex
  automatisch entfernt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 20:42:58 +02:00
sysops 48cfb7cfa6 fix: auditor immer globalen Index nutzen, tenant_id ignorieren
auditor-Rolle hat evtl. tenant_id gesetzt (historisch), soll aber
trotzdem immer den globalen Index durchsuchen und nur No-Tenant-Mails
sehen. tenant_id auf auditor-User per DB-Migration auf NULL gesetzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 19:51:42 +02:00
sysops 994e5d16fc feat: auditor sieht Mails ohne Tenant-Zuordnung
- auditor-Rolle sieht jetzt Mails wo tenant_id IS NULL und kein
  email_refs-Eintrag existiert (statt nur eigene Mails)
- Neues storage.IsWithoutTenant() für effizienten Direktzugriff
- Neues storage.GetAllIDsWithoutTenant() für Suche + ZIP-Export
- Konsistente Prüfung in Search, GetMail, GetAttachment, GetRaw, Export

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 19:34:45 +02:00
sysops 030752157b fix(PROJ-9): Superadmin-Tenant-NULL, GET admin/labels, from_domain-Allowlist
- labelTenantID() returns *int64 (nil for superadmin) statt int64(0)
  → verhindert FK-Constraint-Fehler bei tenant_id = 0
- CreateAdminLabel/CreateLabelRule: nil-Check, 400 wenn kein Tenant
- GET /api/admin/labels Route + handleGetAdminLabels Handler ergänzt
- from_domain in condition_field Allowlist für Label-Regeln hinzugefügt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:42:06 +02:00
sysops e1f25f2287 fix(security): emailsFromHeader fail-closed, domain_auditor-Block, Manticore-Tabellenvalidierung
- emailsFromHeader gibt bei Parse-Fehler nil zurück (fail-closed) statt raw-Header-String;
  verhindert Authorization-Bypass via malformiertem From-Header
- mailBelongsToUser: strings.Contains-Fallback entfernt (war dead code nach dem fix-closed-Fix)
- handleSearch: domain_auditor ohne TenantID wird mit 403 abgewiesen, bevor der globale Index
  abgefragt wird
- manticoreTableName: Regex-Validierung ^emails_(global|tenant_\d+)$ mit panic bei Abweichung

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 02:01:50 +02:00
sysops ab7d6aded2 fix: Datumsparser — mehr RFC-2822-Varianten, kein time.Now() Fallback 2026-04-04 01:47:12 +02:00
sysops 4ef5897e68 feat(PROJ-29): Tenant-Quotas & Usage-Limits vollständig implementiert
- storage/quota.go: SQL-Bug gefixt (emails.size → size_bytes, email_refs JOIN)
- tenantstore/quota.go: GetUsage nutzt jetzt email_refs JOIN für korrekte Tenant-Isolation
- smtpd: ErrQuotaExceeded → SMTP 452 statt 554 (MTA-retry statt permanent reject)
- admin_handlers: handleCreateUser prüft max_users-Quota → HTTP 402 bei Überschreitung
- quota_handlers: handleGetTenantUsage gibt jetzt warnings-Feld mit soft-limit-Prozenten zurück
- server.go: spec-konforme Alias-Route GET /api/admin/tenants/{id}/usage registriert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 01:27:59 +02:00
sysops 22cbfb5df6 fix(security): Email-Matching, LDAP-Validierung, Auditor-Isolation
- mailBelongsToUser: net/mail.ParseAddressList statt strings.Contains
  verhindert False-Positives durch Display-Namen in Mail-Headern
- LDAP mail-Attribut: net/mail.ParseAddress-Validierung vor Übernahme,
  Fallback auf username / username@ldap.local bei ungültiger Adresse
- handleSearch: Auditor-Rolle in userEmailFilter-Check eingeschlossen,
  sodass Auditoren im Search-Pfad dieselbe Mail-Isolation erhalten wie User

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 01:18:34 +02:00
sysops b568c73887 fix(PROJ-30): Stemmer stem_de → lemmatize_de_all (Manticore 25.0.0 MySQL-Protokoll)
stem_de wird vom nativen MySQL-Handler in Manticore 25.0.0 nicht erkannt.
lemmatize_de_all nutzt das mitgelieferte de.pak und funktioniert korrekt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:26:06 +02:00
sysops 825e4db7c1 fix(PROJ-30): ListFolders — c.Client statt c uebergeben (imap.Conn vs *imapclient.Client)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:21:20 +02:00
sysops 0969f76da6 fix(PROJ-30): imapclient.New — Rueckgabewert an go-imap/v2 beta.8 API anpassen
imapclient.New gibt in beta.8 nur *Client zurueck (kein error).
Alle drei Aufrufe in Connect() korrigiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:21:03 +02:00
sysops a93a843506 feat(PROJ-30): Xapian → Manticore Search Migration
- internal/index/manticore.go: ManticoreTenantManager + manticoreIndex (RT-Indizes, CGO-frei)
- internal/index/index.go: TenantIndexer Interface (Xapian + Manticore)
- internal/index/tenant_worker.go: mgr-Typ auf TenantIndexer Interface
- internal/api/server.go: idxMgr auf TenantIndexer Interface
- config/config.go: IndexConfig.ManticoreDSN Feld
- cmd/archivmail/cmd_reindex.go: reindex Subkommando
- cmd/archivmail/main.go: Manticore-Branch + reindex Case
- go.mod: github.com/go-sql-driver/mysql v1.8.1
- update.sh: Manticore auto-install, CGO_ENABLED=0, config.yml migration, auto-reindex

fix(IMAP): TCP-Deadline-Wrapper für steckengebliebene Imports
fix(auth): Email-Claim in JWT für User-Isolation
fix(search): User-Isolation via sess.Email (fail-safe)
fix(ui): Admin-Login Auth-Cache, Logout-Redirect, IMAP-Polling-Resilienz

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:19:36 +02:00
sysops e90d588e30 fix: UpsertLDAPUser — email-basierter Match vor Insert
Verhindert duplicate-key-Fehler wenn LDAP-uid (z.B. "patrick") vom
gespeicherten username ("patrick@perlbach24.de") abweicht. Erst per
Email matchen und updaten, dann neu anlegen falls nicht vorhanden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 00:41:19 +02:00
sysops d01df2a810 fix: Debug-Logging entfernt, tenant_domains auf 132 wiederhergestellt 2026-04-02 00:22:06 +02:00
sysops caac522e3c debug: erweitertes LDAP-Logging (upsert + success) 2026-04-01 00:26:08 +02:00
sysops 5c3a9b55ff debug: temporäres LDAP-Logging für Fehlerdiagnose 2026-04-01 00:05:35 +02:00