feat(PROJ-17): Admin Dashboard Systemauslastung immer anzeigen
- Systemauslastungs-Sektion wird immer gerendert (nicht nur bei Erfolg) - Fehlermeldung wenn /api/admin/system/stats nicht erreichbar ist - Feature-Status auf In Review gesetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/archivmail/config"
|
||||
"github.com/archivmail/internal/index"
|
||||
"github.com/archivmail/internal/storage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
configPath := flag.String("config", "/etc/archivmail/config.yml", "path to config file")
|
||||
format := flag.String("format", "eml", "export format: eml or pdf")
|
||||
outDir := flag.String("out", "./export", "output directory")
|
||||
flag.Parse()
|
||||
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
||||
|
||||
if *format == "pdf" {
|
||||
fmt.Fprintln(os.Stdout, "PDF export not yet implemented")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *format != "eml" {
|
||||
fmt.Fprintf(os.Stderr, "unknown format: %s (supported: eml, pdf)\n", *format)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg, err := config.Load(*configPath)
|
||||
if err != nil {
|
||||
logger.Error("failed to load config", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mailStore, err := storage.New(cfg.Storage.StorePath)
|
||||
if err != nil {
|
||||
logger.Error("storage init failed", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
indexBackend := cfg.Index.Backend
|
||||
if indexBackend == "" {
|
||||
indexBackend = "xapian"
|
||||
}
|
||||
batchSize := cfg.Index.BatchSize
|
||||
if batchSize <= 0 {
|
||||
batchSize = 100
|
||||
}
|
||||
idx, err := index.New(cfg.Index.Path, batchSize, indexBackend)
|
||||
if err != nil {
|
||||
logger.Error("index init failed", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer idx.Close()
|
||||
|
||||
if err := os.MkdirAll(*outDir, 0o755); err != nil {
|
||||
logger.Error("cannot create output directory", "dir", *outDir, "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fetch all indexed mails using pagination
|
||||
page := 0
|
||||
pageSize := 500
|
||||
exported := 0
|
||||
errors := 0
|
||||
|
||||
for {
|
||||
result, err := idx.Search(index.SearchRequest{
|
||||
PageSize: pageSize,
|
||||
Page: page,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("search failed", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(result.Hits) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, hit := range result.Hits {
|
||||
raw, err := mailStore.Load(hit.ID)
|
||||
if err != nil {
|
||||
logger.Error("load failed", "id", hit.ID, "err", err)
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
|
||||
outPath := filepath.Join(*outDir, hit.ID+".eml")
|
||||
if err := os.WriteFile(outPath, raw, 0o644); err != nil {
|
||||
logger.Error("write failed", "path", outPath, "err", err)
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
|
||||
exported++
|
||||
}
|
||||
|
||||
logger.Info("export progress", "page", page, "exported", exported, "errors", errors)
|
||||
|
||||
if exported+errors >= result.Total {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
logger.Info("export complete",
|
||||
"format", *format,
|
||||
"out", *outDir,
|
||||
"exported", exported,
|
||||
"errors", errors,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user