From 69c120a26883739ee3682f4e53f7520c7af230f5 Mon Sep 17 00:00:00 2001 From: sysops Date: Wed, 24 Jun 2026 17:03:15 +0200 Subject: [PATCH] feat(PROJ-56d): archivmail import um --tenant Flag erweitert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit archivmail import --dir/--file ordnete importierte Mails bisher immer ohne Mandant zu (Save(..., nil) fest verdrahtet). Neuer --tenant Flag übergibt die Tenant-ID an Store.Save() und an idxMgr.ForTenant(), sodass Mail sowohl in der DB als auch im richtigen Tenant-Suchindex landet — und automatisch die Retention-Regeln des Tenants greifen (applyRetention nutzt bereits die übergebene tenantID). Relevant für den geplanten Watch-Folder-Import pro Mandant. Co-Authored-By: Claude Sonnet 4.6 --- cmd/archivmail/cmd_import.go | 20 ++++++++++++++++---- cmd/archivmail/cmd_import_piler.go | 4 ++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cmd/archivmail/cmd_import.go b/cmd/archivmail/cmd_import.go index 9a46eeb..14e9ecd 100644 --- a/cmd/archivmail/cmd_import.go +++ b/cmd/archivmail/cmd_import.go @@ -33,6 +33,7 @@ func runImport(args []string) { recursive := fs.Bool("recursive", false, "recurse into subdirectories (with --dir)") dryRun := fs.Bool("dry-run", false, "simulate import without saving") jsonOut := fs.Bool("json", false, "machine-readable JSON output") + tenantFlag := fs.Int64("tenant", 0, "tenant ID to assign imported mails to (0 = none/global)") fs.Usage = func() { fmt.Fprintln(os.Stderr, "Usage: archivmail import [flags]") @@ -161,6 +162,12 @@ func runImport(args []string) { } } + var tenantID *int64 + if *tenantFlag > 0 { + tid := *tenantFlag + tenantID = &tid + } + imported := 0 skipped := 0 errors := 0 @@ -186,7 +193,7 @@ func runImport(args []string) { for _, msg := range messages { total++ - result := importMessage(mailStore, idxMgr, msg, *dryRun) + result := importMessage(mailStore, idxMgr, msg, *dryRun, tenantID) switch result { case "imported": imported++ @@ -226,7 +233,10 @@ func runImport(args []string) { } // importMessage stores and indexes a single raw message. Returns "imported", "skipped", or "error". -func importMessage(mailStore *storage.Store, idxMgr index.TenantIndexer, raw []byte, dryRun bool) string { +// tenantID, when non-nil, assigns the imported mail to that tenant (both in +// storage and in the tenant-specific search index) instead of the +// global/tenant-less context. +func importMessage(mailStore *storage.Store, idxMgr index.TenantIndexer, raw []byte, dryRun bool, tenantID *int64) string { pm, err := mailparser.Parse(raw) if err != nil { fmt.Fprintf(os.Stderr, "warning: parse failed: %v\n", err) @@ -237,7 +247,7 @@ func importMessage(mailStore *storage.Store, idxMgr index.TenantIndexer, raw []b return "imported" } - id, err := mailStore.Save(context.Background(), raw, pm.Date, nil) + id, err := mailStore.Save(context.Background(), raw, pm.Date, tenantID) if err != nil { fmt.Fprintf(os.Stderr, "warning: save failed: %v\n", err) return "error" @@ -259,9 +269,10 @@ func importMessage(mailStore *storage.Store, idxMgr index.TenantIndexer, raw []b HasAttachment: len(pm.Attachments) > 0, Date: pm.Date, Size: int64(len(raw)), + TenantID: tenantID, } - if err := idxMgr.ForTenant(nil).IndexSync(doc); err != nil { + if err := idxMgr.ForTenant(tenantID).IndexSync(doc); err != nil { fmt.Fprintf(os.Stderr, "warning: index failed for %s: %v\n", id, err) return "error" } @@ -299,6 +310,7 @@ archivmail import [flags] --file Einzelne EML- oder MBOX-Datei --dir Verzeichnis mit EML/MBOX-Dateien --recursive Unterverzeichnisse einschließen (mit --dir) + --tenant Mandanten-ID für importierte Mails (0 = keine/global) --dry-run Simulation ohne Speichern --json Maschinenlesbare JSON-Ausgabe diff --git a/cmd/archivmail/cmd_import_piler.go b/cmd/archivmail/cmd_import_piler.go index 22f5856..111dbde 100644 --- a/cmd/archivmail/cmd_import_piler.go +++ b/cmd/archivmail/cmd_import_piler.go @@ -242,7 +242,7 @@ func runPilerexportMethod(mailStore *storage.Store, idx index.TenantIndexer, bin return nil } total++ - result := importMessage(mailStore, idx, raw, dryRun) + result := importMessage(mailStore, idx, raw, dryRun, nil) switch result { case "imported": imported++ @@ -312,7 +312,7 @@ func runDirectMethod(mailStore *storage.Store, idx index.TenantIndexer, storeDir } total++ - result := importMessage(mailStore, idx, emailRaw, dryRun) + result := importMessage(mailStore, idx, emailRaw, dryRun, nil) switch result { case "imported": imported++