package main import ( "embed" "flag" "io/fs" "log" "net/http" "os" "strings" "time" "mail-graveyard/backend" ) //go:embed frontend-js/dist/* 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)") checkAccount := flag.String("check-account", "", "Konto-Logins pruefen und INBOX zaehlen, ohne zu kopieren") reindex := flag.String("reindex", "", "Archiv-mbox-Index fuer Konto/Archiv oder 'all' neu aufbauen") dedupTarget := flag.String("dedup-target", "", "Ziel-Postfach nach Message-ID-Dubletten scannen (Dry-Run ohne --apply)") snapshot := flag.String("snapshot", "", "konsistenten DB- und mbox-Praefix-Snapshot in ein neues Zielverzeichnis schreiben") verifySnapshot := flag.String("verify-snapshot", "", "Snapshot isoliert pruefen und dessen DB-Kopie vollstaendig reindexieren") apply := flag.Bool("apply", false, "Scharfe Ausfuehrung fuer riskante Wartungsbefehle wie --dedup-target") initDB := flag.Bool("init-db", false, "SQLite-DB neu anlegen, falls db_path noch nicht existiert") flag.Parse() if *snapshot != "" && (*verifySnapshot != "" || *runOnce != "" || *seedAccount != "" || *checkAccount != "" || *reindex != "" || *dedupTarget != "" || *watch) { log.Fatal("-snapshot kann nicht mit einem anderen Betriebsmodus kombiniert werden") } if *verifySnapshot != "" && (*runOnce != "" || *seedAccount != "" || *checkAccount != "" || *reindex != "" || *dedupTarget != "" || *watch) { log.Fatal("-verify-snapshot kann nicht mit einem anderen Betriebsmodus kombiniert werden") } if err := backend.LoadConfig("config.json"); err != nil { log.Fatalf("config: %v", err) } if *verifySnapshot != "" { must(backend.VerifySnapshot(*verifySnapshot)) return } if err := backend.ConnectDB(*initDB); err != nil { log.Fatalf("database: %v", err) } if *snapshot != "" { must(backend.CreateSnapshot(*snapshot)) return } if err := backend.InitAuth(); err != nil { log.Fatalf("auth: %v", err) } if *seedAccount != "" { must(backend.SeedAccountFromFile(*seedAccount)) return } if *checkAccount != "" { must(backend.CheckAccount(*checkAccount)) return } if *reindex != "" { must(backend.ReindexArchives(*reindex)) return } if *dedupTarget != "" { report, err := backend.DedupTarget(*dedupTarget, splitOptionalList(*runFolders), *apply) if err != nil { must(err) } backend.LogTargetDedupReport(report) return } // 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 } dist, err := fs.Sub(frontend, "frontend-js/dist") if err != nil { log.Fatal(err) } mux := http.NewServeMux() mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(dist)))) backend.RegisterRoutes(mux) addr := backend.Cfg.Bind + ":" + backend.Cfg.Port log.Printf("Mail-Graveyard listening on %s", addr) log.Fatal(http.ListenAndServe(addr, backend.AuthMiddleware(mux))) } func must(err error) { if err != nil { log.Print(err) os.Exit(1) } } func splitOptionalList(value string) []string { if strings.TrimSpace(value) == "" { return nil } return strings.Split(value, ",") }