Commit Graph
100 Commits
Author SHA1 Message Date
sysopsandClaude Sonnet 4.6 33539d281a fix(ui): Uhrzeit in Datumsspalte der Suchergebnisse anzeigen
toLocaleDateString → toLocaleString mit dateStyle+timeStyle, zeigt
jetzt z.B. "11.05.2026, 00:05" statt nur "11.05.2026".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 20:38:54 +02:00
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Opus 4.7 7be73c1041 feat(PROJ-44): Snippet + Quellen-Badge in Such-Trefferliste
- Zweite Zeile unter dem Betreff zeigt Manticore-Snippet
  mit <b>-Highlights, gerendert via dangerouslySetInnerHTML
  ueber sanitizeSnippet
- Quellen-Badge je match_field (Subject, Body, PDF-Anhang,
  Dateiname, Absender, Empfaenger) als kleines Tailwind-Pill
- Nur sichtbar wenn das Backend ein snippet zurueckliefert

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:55 +02:00
sysopsandClaude Opus 4.7 f4403c8e6c feat(PROJ-44): OCR-Badge + OCR-Text-Download auf Mail-Detail-Seite
- OcrBadge neben dem Verifikations-Status im Mail-Header
- "OCR-Text"-Button (lucide FileText) in der Action-Leiste,
  sichtbar nur bei ocr_status=done und ocr_chars>0
- Tooltip via title-Attribut zeigt erkannte Zeichenzahl
- Pending-/Not-Available-Antworten werden als Alert angezeigt
  ("OCR laeuft noch, bitte gleich nochmal versuchen")

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:49 +02:00
sysopsandClaude Opus 4.7 83039dcf8d feat(PROJ-44): OcrBadge-Komponente fuer Mail-Detail-Header
- Rendert OCR-Status als shadcn Badge mit passender Farbe
  (done=gruen, failed=rot, skipped=grau, pending=blau)
- disabled und undefined rendern null, damit die Komponente
  unbedingt eingebunden werden kann

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:43 +02:00
sysopsandClaude Opus 4.7 23a9a7ea37 feat(PROJ-44): API-Types + Sanitize-Helper fuer OCR-GUI
- MailDetail um ocr_status/ocr_chars erweitert
- SearchHit um snippet + match_field erweitert
- Neue API-Funktionen getOCRTextDownloadURL und downloadMailOCRText
  inkl. 202/404-Handling fuer pending/not-available
- src/lib/sanitize.ts: sanitizeSnippet escaped HTML und laesst nur
  <b>-Tags fuer Manticore-Highlights durch
- Re-exports in src/lib/api/index.ts ergaenzt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:38 +02:00
sysops 88e9d0c08c docs(PROJ-35): Status auf Deployed + Bekannte Pitfalls dokumentiert
- 132 + 131 deployed
- hashMailID-Bit-Mask-Fix dokumentiert (negative IDs Problem)
- OCR-Tempo-Constraint (CPU-bound) erklärt
- Boot-Resume-Loop als bekannter, unkritischer Bug vermerkt
2026-05-08 23:10:59 +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 a252ad6f0e fix(PROJ-35): OCR Boot-Resume drosselt nach Queue-Kapazitaet 2026-05-08 22:15: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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 4f366a3634 fix(PROJ-39): exportEDiscovery in api/index.ts re-exportieren
exportEDiscovery war in mail.ts definiert, aber fehlte im Re-Export-Block
von index.ts — Frontend-Build schlug mit "Export doesn't exist" fehl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:57:32 +02:00
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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 887548a9c7 fix(PROJ-38): getThread + ThreadMail/ThreadResponse in api/index.ts re-exportieren 2026-04-05 20:19:36 +02:00
sysops b252172cc7 feat(PROJ-26,PROJ-38): IMAP LDAP-Auth + Mail-Threading 2026-04-05 20:17:41 +02:00
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 4d1bdb6e8b fix(ui): rollenbasierte Navigation + domain_auditor-Support
- useAuth.ts: domain_auditor (Level 3) in roleLevels ergänzt
- navbar.tsx: IMAP/POP3-Links nur noch für user + domain_admin;
  domain_auditor bekommt Zugang zum Admin-Bereich
- UserNav.tsx: Rollenbezeichnung deutsch + leserlich (domain_auditor →
  "Domain-Auditor"); Badge ausgeblendet wenn Username = Rollenname

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:16:53 +02:00
sysopsandClaude Sonnet 4.6 0db2324c8b fix(ui): Label-Sidebar für auditor/domain_auditor ausblenden
Auditoren haben keine eigenen Mails — Labels machen für sie keinen Sinn.
Die Sidebar (inkl. "+ Neues Label") wird jetzt nur noch für Rollen
mit eigenen Mails angezeigt (user, domain_admin, superadmin).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:13:46 +02:00
sysopsandClaude Sonnet 4.6 23dbab1f47 chore: überflüssigen REPO_URL-Guard im Docker-Installer entfernen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:00:49 +02:00
sysopsandClaude Sonnet 4.6 2900187a0d chore: Xapian/Bleve-Reste aus install.sh, setup.sh und smoke_test.sh entfernen
- install.sh: REPO_URL auf Gitea gesetzt, libxapian-dev entfernt, xapian-
  Verzeichnis entfernt, config.yml-Template auf Manticore umgestellt,
  Manticore-Installationsschritt ergänzt, systemd-Unit auf manticore.service
  als Abhängigkeit, GitHub-URL-Sonderbehandlung entfernt
- setup.sh: "Bleve backend" und "build with Xapian" Hinweise entfernt,
  Projektname von mailarchive auf archivmail korrigiert
- smoke_test.sh: Binary-Namen auf aktuelles CLI-Format korrigiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:00:06 +02:00
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 c8ab4afef0 chore: PROJ-9 als Deployed markiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 10:44:28 +02:00
sysopsandClaude Sonnet 4.6 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 7e1e58e1e4 feat(PROJ-35): Spec — OCR & Anhang-Volltext-Indexierung 2026-04-04 02:06:30 +02:00
sysopsandClaude Sonnet 4.6 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 896f8dceb9 chore: PROJ-29 + PROJ-30 als Deployed markiert 2026-04-04 01:54:29 +02:00
sysops ab7d6aded2 fix: Datumsparser — mehr RFC-2822-Varianten, kein time.Now() Fallback 2026-04-04 01:47:12 +02:00
sysops ecd5ed361b fix(PROJ-30): cmd import-piler — Manticore-Backend support 2026-04-04 01:38:59 +02:00
sysops 44bff147ba fix(PROJ-30): cmd import — Manticore-Backend support (wie cmd_reindex) 2026-04-04 01:38:25 +02:00
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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 36d8db1574 chore: go.sum für go-sql-driver/mysql aktualisiert 2026-04-03 21:35:33 +02:00
sysopsandClaude Sonnet 4.6 7efccda864 chore: Manticore als Standard — Xapian-Referenzen entfernt
- CLAUDE.md: Manticore statt Xapian, CGO_ENABLED=0, /var/lib/manticore/
- .claude/agents/manticore-admin.md: Neuer Projekt-Agent (Security, Backup, Reindex)
- .claude/agents/mailarchiv-architect.md: Xapian → Manticore Search

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:29:12 +02:00
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 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
sysopsandClaude Sonnet 4.6 d7b01504aa fix(ui): Mandanten-Tabelle — Slug unter Name, LDAP-Host anzeigen, Slug-Spalte entfernt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:54:50 +02:00
sysopsandClaude Sonnet 4.6 acc0d93318 feat: getrennte Login-Seiten für User (/), Admins (/admin/login)
- /admin/login: neue Login-Seite nur für auditor, admin, domain_admin, superadmin
- /: blockiert Admin-Rollen mit Hinweis auf /admin
- useAuth: neuer loginPage-Parameter für flexiblen Unauthentifiziert-Redirect
- /admin: leitet bei Nicht-Auth zu /admin/login statt /

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:47:41 +02:00
sysopsandClaude Sonnet 4.6 c1a9004720 feat(PROJ-28): SMTP-Out Relay — DB-Konfiguration + Admin-Tab
- smtpoutconfig.Store: AES-256-GCM verschlüsseltes Passwort in DB (id=1 Singleton)
- Mailer: Reload() für Runtime-Konfigurationswechsel (sync.RWMutex)
- API: GET/PUT/DELETE /api/admin/smtp-out + POST /api/admin/smtp-out/test
- Admin-Tab: Host, Port, User, Passwort, TLS-Switch, From, Test-Button, Status-Badge
- Startup: Lädt DB-Konfiguration und aktiviert Mailer ohne Restart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 22:36:57 +02:00
sysopsandClaude Sonnet 4.6 7371a73b3e fix(SEC): Signup-Enumeration durch Always-Send-Email schließen
Bei doppeltem Signup wird eine "bereits registriert"-Mail gesendet,
sodass jeder Signup-Versuch eine ausgehende E-Mail erzeugt.
Side-Channel-Angriff zur Account-Enumeration nicht mehr möglich.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 22:00:43 +02:00
sysopsandClaude Sonnet 4.6 4583262ea4 feat(PROJ-28): Self-Service Onboarding — Signup, Verify, Password Reset, Invites
- internal/mailer: SMTP-Out via net/smtp (TLS + STARTTLS), HTML+Text-Templates
- internal/tokenstore: auth_tokens Tabelle, SHA-256-Hash, TTL, einmalig verwendbar
- userstore: CreateInactive(), Activate(), GetByEmail(), SetPassword()
- API: POST /signup, GET /verify, POST /forgot-password, POST /reset-password
- API: POST /admin/invite (domain_admin+), GET /auth/invite?token (check)
- Login-Seite: Links zu "Passwort vergessen" und "Registrieren"
- Frontend: /signup, /verify, /forgot-password, /reset-password Seiten
- server.fqdn nicht konfiguriert → Startup-Warnung, Self-Service deaktiviert
- LDAP-Nutzer: Passwort-Reset abgewiesen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:54:11 +02:00
sysopsandClaude Sonnet 4.6 7930b85cde feat(PROJ-29): Tenant-Quotas & Usage-Limits
- DB: max_storage_bytes, max_users, max_emails per Tenant (NULL = unlimited)
- storage/quota.go: CheckQuota() mit 60s-Cache, ErrQuotaExceeded
- Save() prüft Quota vor dem Schreiben — Ablehnung bei Hard-Limit
- tenantstore/quota.go: SetQuota(), GetQuota(), GetUsage()
- API: GET/PUT /api/admin/tenant/{id}/quota, GET /api/admin/quotas
- QuotaTab: Usage-Balken (Speicher/Nutzer/Mails), Edit-Dialog, Warnung ab 80%
- InvalidateQuotaCache() nach Quota-Änderung für sofortige Wirkung

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:21:11 +02:00
sysopsandClaude Sonnet 4.6 ebc9e278ea fix(PROJ-34): Mandanten-Retention ist Opt-in — kein globaler Lock für Mandanten
storage.Save(): retention_days eines Mandanten muss explizit > 0 sein.
Globale config greift nie automatisch auf Mandanten-Mails.
Mails ohne Mandant: globale config als Fallback (unveraendert).
Frontend: Hinweis und Labels klargestellt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:50:54 +02:00
sysopsandClaude Sonnet 4.6 4aadf7a4d2 fix: retention_handlers.go — getSession/audlog.Log-Signatur korrigiert
Ersetzt nicht-existente getSession(r) durch sessionFromCtx(r.Context()) und
passt den audlog.Log-Aufruf auf die korrekte audit.Entry-Struct-Signatur an.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:47:18 +02:00
sysopsandClaude Sonnet 4.6 58bcfb1586 feat: Dashboard-Metriken nach Mailpiler-Vorbild (Uptime, Aktivität, Prognose)
- storage_stats.go (neu): MailActivityStats (60min/24h/7d/30d), StorageEstimateStats
- dashboard_handlers.go: Uptime (/proc/uptime), activity + estimate in System-Stats-Response
- DashboardTab: Uptime in API-Kachel, neue Kacheln "Mail-Eingang" + "Speicherprognose"
- Warnung (Badge "Knapp!") wenn Partition in <90 Tagen voll

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:44:02 +02:00
sysopsandClaude Sonnet 4.6 5bbf6d0ff3 feat(PROJ-34): Retention-Tab + pro-Mandant Aufbewahrungsfristen
- tenantstore: retention_days Spalte, GetRetentionDays/SetRetentionDays
- storage.Save(): per-tenant retention überschreibt globale config
- API: GET /api/admin/retention, PUT /api/admin/tenant/{id}/retention
- Frontend: RetentionTab mit globaler Policy-Anzeige, Mandanten-Tabelle,
  Bearbeiten-Dialog und Purge-Button (superadmin only)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:37:15 +02:00
sysopsandClaude Sonnet 4.6 5f0c7a7e6d feat(PROJ-27): Docker-Support + install.sh v2.0 (native + Docker-Modus)
Adds multi-stage Dockerfiles (Go+Xapian CGO, Next.js standalone),
docker-compose.yml, webhook-basierter Deploy-Flow und erweitertes
install.sh mit interaktiver Modus-Auswahl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:18:11 +02:00
sysopsandClaude Sonnet 4.6 842640c3aa fix: Superadmin wird direkt zu /admin weitergeleitet
- Login: superadmin → /admin, alle anderen → /search
- Cached-Session: superadmin → /admin
- Search-Seite: superadmin wird sofort zu /admin umgeleitet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:05:08 +02:00
sysopsandClaude Sonnet 4.6 27040ffcb1 feat(PROJ-28): FQDN + smtp_out Konfiguration
- config: server.fqdn, SMTPOutConfig, smtp_out Abschnitt
- config: IMAPServerConfig.FQDN (runtime-gesetzt)
- main: FQDN-Fallback für SMTP EHLO, FQDN an IMAP-Server
- imapserver: Greeting nutzt server.fqdn (RFC 3501)
- smtpd/imapserver Modulversion 1.3

fix: Superadmin sieht keine Suche/IMAP/POP3 Nav-Links

- Navbar: Suche, IMAP Import, POP3 Import nur für non-superadmin
- Superadmin landet direkt auf Admin-Dashboard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 09:51:41 +02:00
sysopsandClaude Sonnet 4.6 8d0f685fc9 feat(PROJ-33): IMAP UID-Stabilität + Shared/Personal-Modus
Backend:
- storage: uid BIGSERIAL Migration, MailWithUID, GetMailsWithUID, GetMailsByRecipient
- tenantstore: imap_mode Spalte, GetIMAPMode, SetIMAPMode
- imapserver: stable UIDs aus DB, personal/shared Modus, userEmail in session
- api: GET/PUT /api/admin/settings/imap-mode (domain_admin only, double opt-in)

Frontend:
- IMAPSettingsTab: Modus-Anzeige + Toggle mit Double-Opt-In Dialog
- Admin-Panel: IMAP-Tab für domain_admin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 09:46:52 +02:00
sysopsandClaude Sonnet 4.6 b6856af2eb feat(PROJ-32): Message-ID-basierte Duplikatserkennung
- message_id Spalte + UNIQUE-Index in emails-Tabelle
- Save() prüft Message-ID vor SHA-256-Flow (kein Disk-I/O bei Duplikat)
- lookupByMessageID() als private Hilfsfunktion
- insertMeta() schreibt message_id, gibt error zurück (Race-safe)
- SaveMeta() schreibt message_id idempotent (Backfill)

feat(PROJ-34): Retention-Policy + Löschsperre (GoBD)

- retain_until TIMESTAMPTZ Spalte in emails-Tabelle
- ErrRetentionLock typed error
- Delete() prüft Retention-Frist vor Löschung
- Purge() löscht alle Mails mit abgelaufener Retention
- POST /api/admin/purge Endpunkt (superadmin only)
- config: storage.retention_days

fix: Superadmin-Benutzerübersicht zeigt Mandant-Spalte

- UsersTab: Mandant-Spalte wenn isSuperAdmin
- domain_auditor Rolle im Create-Dialog ergänzt
- storage Modulversion → 1.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 01:29:25 +02:00
sysopsandClaude Sonnet 4.6 cb31c48ce8 fix: version → AppVersion in cmd_import.go (Build-Fehler nach 0.9.1-Umbenennung)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 01:14:27 +02:00
sysopsandClaude Sonnet 4.6 4f7a7d6946 feat: App-Version 0.9.1 + Modulversionsnummern
- version.go: AppVersion + Modules-Map (pro Modul interne Versionsnummer)
- GET /api/version: liefert App- und Modulversionen (ohne Auth)
- archivmail version: zeigt App- und Modulversionen in CLI
- version-Konstante aus cmd_import.go entfernt (war falsche Stelle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 01:12:51 +02:00
sysopsandClaude Sonnet 4.6 64433aa847 fix(SEC-29): Rollen-Trennung Admins/Auditoren, domain_auditor Rolle
- superadmin + domain_admin haben keinen Mail-Zugriff mehr (requireMailAccess)
- Neue Rolle domain_auditor: alle Tenant-Mails, kein Admin-Zugriff
- auditor + user: nur eigene Mails
- ZIP-Export: kein separates Attachment-Entpacken mehr, nur EML
- roleLevel() um domain_auditor (Level 3) erweitert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 01:09:19 +02:00
sysopsandClaude Sonnet 4.6 e0f6a818eb feat: LDAP-Test zeigt klickbare Filter-Vorschläge (UCS/AD-Erkennung)
Nach erfolgreichem Verbindungstest werden passende Filter-Vorschläge
angezeigt. Erkennt automatisch Univention UCS (posixAccount) vs.
Active Directory (sAMAccountName). Klick übernimmt den Filter direkt
ins Formular. Vorschläge berücksichtigen mailPrimaryAddress (UCS).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 14:50:20 +01:00
sysopsandClaude Sonnet 4.6 2fa7104605 fix: User-Struct JSON-Tags ergänzen (active/source/id etc. waren Großbuchstaben)
Frontend erhielt 'Active'/'Source' statt 'active'/'source' →
Inaktiv-Badge und local-Badge zeigten falsche Werte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 13:46:47 +01:00
sysopsandClaude Sonnet 4.6 0b435d8d1a fix: LDAP FetchUsers Filter ohne Klammern erzeugt Compile Error
UserFilter wie 'uid=%s' wurde zu 'uid=*' — kein gültiger LDAP-Filter.
Fix: Klammern ergänzen wenn der Filter nicht mit '(' beginnt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 13:38:19 +01:00
sysopsandClaude Sonnet 4.6 a6a66beaa8 chore: weitere Code-Aufteilung (api.ts, hooks, ldap_sync)
- src/lib/api.ts (1085 Zeilen) → 5 thematische Module unter src/lib/api/
  (core, users, ldap, tenants, mail, system) + index.ts Re-Export
- useLDAPConfig / useTenantLDAPConfig / useTenantUsers Hooks extrahiert;
  admin/page.tsx nutzt diese statt roher useState-Blöcke
- handleSyncTenantLDAP, handleAdminSyncTenantLDAP, doSyncTenantLDAP,
  buildTenantTestConfig, syncResult aus ldap_tenants.go in ldap_sync.go verschoben

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 13:05:19 +01:00
sysopsandClaude Sonnet 4.6 bc4a98de0d chore: admin/page.tsx in Einzelkomponenten aufteilen (3917 → 1304 Zeilen)
- Tab-Sektionen → src/components/admin/tabs/ (11 Dateien)
- Dialoge → src/components/admin/ (TenantLDAPDialog, UserDialogs)
- Keine Verhaltensänderungen, TypeScript fehlerfrei

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:30:16 +01:00
sysopsandClaude Sonnet 4.6 38f8cdddc7 fix: TypeScript-Fehler bei tenantUsersSyncResult errors-Pruefung
Optional chaining auf errors.length fuehrt zu 'possibly undefined'
in strict mode -- explizite null-Guard + nullish coalescing fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:36:35 +01:00
sysopsandClaude Sonnet 4.6 72b023b598 feat: LDAP-User-Sync in Mandanten-Benutzerliste
Neuer Endpoint POST /api/admin/tenants/{id}/ldap/sync importiert alle
LDAP-User (source=ldap) per UpsertLDAPUser in die Tenant-Benutzerliste.
Im Nutzer-Dialog erscheint ein "LDAP-Benutzer synchronisieren"-Button
wenn LDAP für den Mandanten aktiv ist. Unterstützt Univention UCS
(mailPrimaryAddress, inetOrgPerson). Benutzertabelle zeigt jetzt auch
die Quelle (local/ldap).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:35:02 +01:00
sysopsandClaude Sonnet 4.6 2da61689ea feat: LDAP-Test zeigt User-Tabelle mit Univention-UCS-Support
Nach erfolgreichem Verbindungstest werden bis zu 50 Benutzer (UID,
Name, E-Mail) in einer scrollbaren Tabelle angezeigt. Unterstützt
mailPrimaryAddress (Univention UCS) als Fallback für mail sowie
inetOrgPerson objectClass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:23:12 +01:00
sysopsandClaude Sonnet 4.6 30c6694dff feat(PROJ-24): Mandanten-Logo Upload
- DB: logo_data (BYTEA) + logo_content_type Spalten in tenants-Tabelle
- Backend: SetLogo/GetLogo/DeleteLogo im tenantstore
- API: Logo-Endpunkte für superadmin (beliebiger Mandant) und
  domain_admin (eigener Mandant), max. 2 MB, PNG/JPEG/GIF/WebP/SVG
- Frontend: Logo-Dialog in Mandantentabelle (superadmin),
  Logo-Upload-Sektion im LDAP-Tab (domain_admin)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 03:15:34 +01:00
sysopsandClaude Sonnet 4.6 cd2781bdff fix: Mandanten-Nutzerdialog Fehlerhandling + LDAP-Hinweis
- Fehlerstate für Nutzer-Dialog: API-Fehler werden jetzt angezeigt statt silent ignoriert
- LDAP-Hinweis: wenn LDAP aktiv aber keine lokalen User da sind, erklärender Text
  "LDAP ist aktiv — Benutzer erscheinen hier nach ihrem ersten Login"
- tenantUsersDialogLdap State: speichert ob Tenant LDAP aktiviert hat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 02:18:41 +01:00
sysopsandClaude Sonnet 4.6 9db433c4c1 fix: Mandantenverwaltung LDAP-Status und Nutzer-Listing
- tenantstore.List(): LEFT JOIN tenant_ldap hinzugefügt — ldap_enabled + ldap_url
  werden jetzt im GET /api/tenants Response mitgeliefert
- Tenant-Struct: Felder LDAPEnabled *bool + LDAPURL string ergänzt
- Neuer Endpunkt GET /api/tenants/{id}/users → listet Nutzer eines Mandanten
- api.ts: getTenantUsers() Funktion + tenant_id Feld im User Interface
- Admin-Panel: "Nutzer"-Button im Mandanten-Tab öffnet Dialog mit Nutzerliste

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 02:01:12 +01:00
sysopsandClaude Sonnet 4.6 b3074b303e fix: LDAP leere BindPassword schlägt Login fehl + install.sh Passwort-Extraktion
- auth.Manager.Login: LDAP-Fallback überspringen wenn URL oder BindPassword leer
  (verhindert go-ldap ErrorEmptyPassword Code 206 bei fehlerhaftem LDAP-Config-Eintrag)
- install.sh: grep-Muster von variablenbreitem Lookbehind auf \K umgestellt
  (PCRE unterstützt keine variablen Lookbehinds — Passwörter wurden nie korrekt extrahiert)
- install.sh: Wartezeit auf Backend-Start erhöht (bis zu 15s statt 2s)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 01:47:48 +01:00
sysopsandClaude Sonnet 4.6 fd46f0de9a fix: superadmin beim ersten Start anlegen + Installer zeigt echte Passwörter
- seedDefaultUsers: superadmin (Rolle superadmin) zu Default-Usern hinzugefügt
  → wird beim ersten Backend-Start mit Zufallspasswort angelegt
- Installer: Passwörter werden nach dem Deployment aus dem Journal gelesen
  (journalctl -u archivmail) statt falschen Platzhalter 'archivmailrockz'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 01:23:33 +01:00
sysopsandClaude Sonnet 4.6 32032b49d2 fix(installer): echte Web-Passwörter aus Journal lesen statt Platzhalter
Der Backend generiert beim ersten Start zufällige Passwörter und gibt
sie im Journal aus. Der Installer liest sie nun nach dem Deployment
und trägt sie korrekt in install-summary.txt und Terminal-Output ein.
Zuvor wurden fälschlicherweise 'archivmailrockz' als Passwörter angezeigt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 01:22:50 +01:00
sysopsandClaude Sonnet 4.6 33eafc1624 feat(installer): Zusammenfassung aller angelegten User & Passwörter
- Abschlussbericht zeigt alle Credentials in übersichtlicher Tabelle:
  DB-Passwort, API-Secret, Web-Logins (superadmin/admin/auditor)
- Speichert vollständige Zusammenfassung als /etc/archivmail/install-summary.txt
  (chmod 600, nur root lesbar) mit allen Pfaden, Diensten und Hinweisen
- Tabelle im Terminal-Output mit allen wichtigen Zugangsdaten auf einen Blick

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 01:10:19 +01:00
sysopsandClaude Sonnet 4.6 d262253faf fix(installer): DB-Passwort-Mismatch bei Re-Install verhindern
Bei vorhandener config.yml wird das DB-Passwort daraus gelesen und
PostgreSQL entsprechend aktualisiert — verhindert Auth-Fehler beim
zweiten Installer-Durchlauf auf demselben Server.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 01:05:51 +01:00
sysopsandClaude Sonnet 4.6 48e04f36fd fix(installer): Lücken aus Gap-Report geschlossen
- rsync zum apt-Paket-Block hinzugefügt
- DEBIAN_FRONTEND=noninteractive gesetzt (keine debconf-Interaktion)
- go PATH-Fix: Debian-spezifische Pfade in /usr/lib/go-*/bin ergänzen
  (update.sh + install.sh symlink-Fallback)
- TLS-Zertifikat: selbstsigniertes RSA-4096 Cert via hostname -f,
  /etc/ssl/archivmail/ anlegen + Permissions (640, root:archivmail)
- nginx: HTTPS von Anfang an (HTTP→HTTPS Redirect + TLS-Block statt HTTP-only)
- config.yml-Template vollständig:
  - smtp.enabled, smtp.domain (hostname -f), smtp.tls_cert/tls_key
  - imap_server (enabled, bind :993, tls_cert/tls_key)
  - api.trusted_proxies, api.secure_cookies: true
  - Vorhandenes config.yml wird nicht überschrieben
- systemd archivmail.service: AmbientCapabilities=CAP_NET_BIND_SERVICE
  (für privilegierten Port 993 als non-root User)
  NoNewPrivileges=false (erforderlich für AmbientCapabilities)
  ReadOnlyPaths um /etc/ssl/archivmail ergänzt
- systemctl enable am Ende des Installers (ohne --now)
- update.sh nach /opt/archivmail/ kopieren + erstes Deployment direkt anstoßen
- Abschlussbericht: FQDN, HTTPS/IMAP/SMTP URLs, TLS-Hinweis

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 01:03:57 +01:00
sysopsandClaude Sonnet 4.6 c59cad92be fix: IMAP-Konto bearbeiten + Löschen auch bei sync_running
- Store: UpdateCredentials() — Zugangsdaten + Passwort neu verschlüsseln,
  setzt status='idle', error_msg='', sync_running=false zurück
- Handler: PATCH /api/imap/{id} unterstützt nun Credential-Update
  (name/host/username vorhanden = Credential-Update, sonst sync_interval)
- Frontend: "Bearbeiten"-Button öffnet Edit-Dialog mit allen Feldern;
  Passwort-Feld leer = unverändertes Passwort
- Frontend: Löschen-Button nicht mehr durch sync_running blockiert
  (nur noch bei status=running gesperrt)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:49:37 +01:00
sysopsandClaude Sonnet 4.6 4a4136e4a6 feat: FQDN-Support, IMAP-TLS und SMTP-TLS via Master-Cert
- config: IMAPServerConfig um TLSCert/TLSKey erweitert
- imapserver: TLS-Support (tls.Listen) mit automatischem Port 993-Default
- Server-Konfiguration: FQDN archivmail.scahome.local
  - nginx server_name auf archivmail.scahome.local gesetzt
  - SMTP domain auf archivmail.scahome.local
  - SMTP TLS aktiviert (/etc/ssl/archivmail/archivmail.crt)
  - IMAP-Server TLS auf Port 993 aktiviert
  - secure_cookies: true (HTTPS)
  - Firewall: Port 993 geöffnet
  - Zertifikat neu ausgestellt (SAN: archivmail.scahome.local + archivmail + 192.168.1.131)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:32:25 +01:00