From 813762f43f0d50206ffa5bd4f643973a1465f1f7 Mon Sep 17 00:00:00 2001 From: DonVoo Date: Tue, 14 Jul 2026 05:53:11 +0200 Subject: [PATCH] Add scoped migration acceptance run --- backend/04-imap-source.go | 6 +++++- backend/07-migrate.go | 27 +++++++++++++++++++++++++-- main.go | 9 +++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/backend/04-imap-source.go b/backend/04-imap-source.go index 8732257..04434d3 100644 --- a/backend/04-imap-source.go +++ b/backend/04-imap-source.go @@ -137,9 +137,13 @@ func (m *imapClientMailbox) listMailboxes() ([]imapListMailbox, error) { } func (m *imapClientMailbox) fetchAll(folder string) ([]RawMessage, error) { - if _, err := m.c.Select(folder, &imap.SelectOptions{ReadOnly: true}).Wait(); err != nil { + selected, err := m.c.Select(folder, &imap.SelectOptions{ReadOnly: true}).Wait() + if err != nil { return nil, err } + if selected.NumMessages == 0 { + return nil, nil + } section := &imap.FetchItemBodySection{Peek: true} seqSet := imap.SeqSet{} diff --git a/backend/07-migrate.go b/backend/07-migrate.go index b46c042..b87cf2d 100644 --- a/backend/07-migrate.go +++ b/backend/07-migrate.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "path/filepath" + "strings" "time" ) @@ -22,6 +23,14 @@ import ( // name = Konto-Name oder "all". watch = true -> Delta-Schleife bis Abbruch // (Cutover-Fenster), sonst einmaliger Durchlauf. func RunMigration(name string, watch bool, interval time.Duration) error { + return runMigration(name, watch, interval, nil) +} + +func RunMigrationFolders(name string, folders []string) error { + return runMigration(name, false, 0, selectedFolderSet(folders)) +} + +func runMigration(name string, watch bool, interval time.Duration, selectedFolders map[string]bool) error { if interval <= 0 { interval = 60 * time.Second } @@ -45,7 +54,7 @@ func RunMigration(name string, watch bool, interval time.Duration) error { if !a.Active { continue } - if err := migrateAccount(a); err != nil { + if err := migrateAccount(a, selectedFolders); err != nil { if !watch { return err } @@ -62,7 +71,7 @@ func RunMigration(name string, watch bool, interval time.Duration) error { } // migrateAccount fuehrt einen einzelnen Umzug aus (ein Durchlauf). -func migrateAccount(a Account) error { +func migrateAccount(a Account, selectedFolders map[string]bool) error { if a.ID == 0 { stored, err := GetAccount(a.Name) if err != nil { @@ -102,6 +111,9 @@ func migrateAccount(a Account) error { } total, done, errs := 0, 0, 0 for _, folder := range folders { + if len(selectedFolders) > 0 && !selectedFolders[folder.Name] { + continue + } dstFolder := MapSourceToTarget(folder.Name, folder.Attrs, folder.Delim, dst.Delim(), targets, "") if dstFolder == "" { dstFolder = folder.Name @@ -165,6 +177,17 @@ func migrateAccount(a Account) error { return nil } +func selectedFolderSet(folders []string) map[string]bool { + out := make(map[string]bool, len(folders)) + for _, folder := range folders { + folder = strings.TrimSpace(folder) + if folder != "" { + out[folder] = true + } + } + return out +} + func CheckAccount(name string) error { a, err := GetAccount(name) if err != nil { diff --git a/main.go b/main.go index b658e7d..4542e88 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "log" "net/http" "os" + "strings" "time" "mail-graveyard/backend" @@ -17,6 +18,7 @@ var frontend embed.FS func main() { runOnce := flag.String("run", "", "einen Umzugs-Job einmalig ausfuehren (Konto-Name oder 'all'), ohne Web-UI") + runFolders := flag.String("folders", "", "optionale kommaseparierte Ordnerliste fuer -run (CLI-Abnahme/Teilumzug)") watch := flag.Bool("watch", false, "Delta-Sync in Schleife bis Stop (Cutover-Fenster)") watchInterval := flag.Duration("watch-interval", 60*time.Second, "Pause zwischen Watch-Durchlaeufen") seedAccount := flag.String("seed-account", "", "Konto aus lokaler JSON-Datei in die DB schreiben (nur CLI/Test)") @@ -44,6 +46,13 @@ func main() { // CLI-Modus: ohne Web laufen lassen (Cronjob / Cutover). if *runOnce != "" { + if strings.TrimSpace(*runFolders) != "" { + if *watch { + log.Fatal("-folders kann nicht mit -watch kombiniert werden") + } + must(backend.RunMigrationFolders(*runOnce, strings.Split(*runFolders, ","))) + return + } must(backend.RunMigration(*runOnce, *watch, *watchInterval)) return }