Add scoped migration acceptance run

This commit is contained in:
DonVoo 2026-07-14 05:53:11 +02:00
parent 253435d87f
commit 813762f43f
3 changed files with 39 additions and 3 deletions

View file

@ -137,9 +137,13 @@ func (m *imapClientMailbox) listMailboxes() ([]imapListMailbox, error) {
} }
func (m *imapClientMailbox) fetchAll(folder string) ([]RawMessage, 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 return nil, err
} }
if selected.NumMessages == 0 {
return nil, nil
}
section := &imap.FetchItemBodySection{Peek: true} section := &imap.FetchItemBodySection{Peek: true}
seqSet := imap.SeqSet{} seqSet := imap.SeqSet{}

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"path/filepath" "path/filepath"
"strings"
"time" "time"
) )
@ -22,6 +23,14 @@ import (
// name = Konto-Name oder "all". watch = true -> Delta-Schleife bis Abbruch // name = Konto-Name oder "all". watch = true -> Delta-Schleife bis Abbruch
// (Cutover-Fenster), sonst einmaliger Durchlauf. // (Cutover-Fenster), sonst einmaliger Durchlauf.
func RunMigration(name string, watch bool, interval time.Duration) error { 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 { if interval <= 0 {
interval = 60 * time.Second interval = 60 * time.Second
} }
@ -45,7 +54,7 @@ func RunMigration(name string, watch bool, interval time.Duration) error {
if !a.Active { if !a.Active {
continue continue
} }
if err := migrateAccount(a); err != nil { if err := migrateAccount(a, selectedFolders); err != nil {
if !watch { if !watch {
return err return err
} }
@ -62,7 +71,7 @@ func RunMigration(name string, watch bool, interval time.Duration) error {
} }
// migrateAccount fuehrt einen einzelnen Umzug aus (ein Durchlauf). // 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 { if a.ID == 0 {
stored, err := GetAccount(a.Name) stored, err := GetAccount(a.Name)
if err != nil { if err != nil {
@ -102,6 +111,9 @@ func migrateAccount(a Account) error {
} }
total, done, errs := 0, 0, 0 total, done, errs := 0, 0, 0
for _, folder := range folders { for _, folder := range folders {
if len(selectedFolders) > 0 && !selectedFolders[folder.Name] {
continue
}
dstFolder := MapSourceToTarget(folder.Name, folder.Attrs, folder.Delim, dst.Delim(), targets, "") dstFolder := MapSourceToTarget(folder.Name, folder.Attrs, folder.Delim, dst.Delim(), targets, "")
if dstFolder == "" { if dstFolder == "" {
dstFolder = folder.Name dstFolder = folder.Name
@ -165,6 +177,17 @@ func migrateAccount(a Account) error {
return nil 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 { func CheckAccount(name string) error {
a, err := GetAccount(name) a, err := GetAccount(name)
if err != nil { if err != nil {

View file

@ -7,6 +7,7 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"strings"
"time" "time"
"mail-graveyard/backend" "mail-graveyard/backend"
@ -17,6 +18,7 @@ var frontend embed.FS
func main() { func main() {
runOnce := flag.String("run", "", "einen Umzugs-Job einmalig ausfuehren (Konto-Name oder 'all'), ohne Web-UI") 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)") 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") 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)") 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). // CLI-Modus: ohne Web laufen lassen (Cronjob / Cutover).
if *runOnce != "" { 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)) must(backend.RunMigration(*runOnce, *watch, *watchInterval))
return return
} }