feat(PROJ-56d): archivmail import um --tenant Flag erweitert
archivmail import --dir/--file ordnete importierte Mails bisher immer ohne Mandant zu (Save(..., nil) fest verdrahtet). Neuer --tenant <id> 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b3ff8e6cf9
commit
69c120a268
@@ -33,6 +33,7 @@ func runImport(args []string) {
|
|||||||
recursive := fs.Bool("recursive", false, "recurse into subdirectories (with --dir)")
|
recursive := fs.Bool("recursive", false, "recurse into subdirectories (with --dir)")
|
||||||
dryRun := fs.Bool("dry-run", false, "simulate import without saving")
|
dryRun := fs.Bool("dry-run", false, "simulate import without saving")
|
||||||
jsonOut := fs.Bool("json", false, "machine-readable JSON output")
|
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() {
|
fs.Usage = func() {
|
||||||
fmt.Fprintln(os.Stderr, "Usage: archivmail import [flags]")
|
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
|
imported := 0
|
||||||
skipped := 0
|
skipped := 0
|
||||||
errors := 0
|
errors := 0
|
||||||
@@ -186,7 +193,7 @@ func runImport(args []string) {
|
|||||||
|
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
total++
|
total++
|
||||||
result := importMessage(mailStore, idxMgr, msg, *dryRun)
|
result := importMessage(mailStore, idxMgr, msg, *dryRun, tenantID)
|
||||||
switch result {
|
switch result {
|
||||||
case "imported":
|
case "imported":
|
||||||
imported++
|
imported++
|
||||||
@@ -226,7 +233,10 @@ func runImport(args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// importMessage stores and indexes a single raw message. Returns "imported", "skipped", or "error".
|
// 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)
|
pm, err := mailparser.Parse(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "warning: parse failed: %v\n", err)
|
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"
|
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 {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "warning: save failed: %v\n", err)
|
fmt.Fprintf(os.Stderr, "warning: save failed: %v\n", err)
|
||||||
return "error"
|
return "error"
|
||||||
@@ -259,9 +269,10 @@ func importMessage(mailStore *storage.Store, idxMgr index.TenantIndexer, raw []b
|
|||||||
HasAttachment: len(pm.Attachments) > 0,
|
HasAttachment: len(pm.Attachments) > 0,
|
||||||
Date: pm.Date,
|
Date: pm.Date,
|
||||||
Size: int64(len(raw)),
|
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)
|
fmt.Fprintf(os.Stderr, "warning: index failed for %s: %v\n", id, err)
|
||||||
return "error"
|
return "error"
|
||||||
}
|
}
|
||||||
@@ -299,6 +310,7 @@ archivmail import [flags]
|
|||||||
--file Einzelne EML- oder MBOX-Datei
|
--file Einzelne EML- oder MBOX-Datei
|
||||||
--dir Verzeichnis mit EML/MBOX-Dateien
|
--dir Verzeichnis mit EML/MBOX-Dateien
|
||||||
--recursive Unterverzeichnisse einschließen (mit --dir)
|
--recursive Unterverzeichnisse einschließen (mit --dir)
|
||||||
|
--tenant Mandanten-ID für importierte Mails (0 = keine/global)
|
||||||
--dry-run Simulation ohne Speichern
|
--dry-run Simulation ohne Speichern
|
||||||
--json Maschinenlesbare JSON-Ausgabe
|
--json Maschinenlesbare JSON-Ausgabe
|
||||||
|
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ func runPilerexportMethod(mailStore *storage.Store, idx index.TenantIndexer, bin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
total++
|
total++
|
||||||
result := importMessage(mailStore, idx, raw, dryRun)
|
result := importMessage(mailStore, idx, raw, dryRun, nil)
|
||||||
switch result {
|
switch result {
|
||||||
case "imported":
|
case "imported":
|
||||||
imported++
|
imported++
|
||||||
@@ -312,7 +312,7 @@ func runDirectMethod(mailStore *storage.Store, idx index.TenantIndexer, storeDir
|
|||||||
}
|
}
|
||||||
|
|
||||||
total++
|
total++
|
||||||
result := importMessage(mailStore, idx, emailRaw, dryRun)
|
result := importMessage(mailStore, idx, emailRaw, dryRun, nil)
|
||||||
switch result {
|
switch result {
|
||||||
case "imported":
|
case "imported":
|
||||||
imported++
|
imported++
|
||||||
|
|||||||
Reference in New Issue
Block a user