Files
archivmail/cmd/archivmail/cmd_reindex.go
T
sysopsandClaude Sonnet 4.6 5f63bfe8d4 chore(PROJ-60): Xapian-Legacy-Backend vollständig aus dem Code entfernt
Xapian war seit der Manticore-Migration (PROJ-30) nur noch ein
build-tag-gatetes, in Produktion nie genutztes Legacy-Backend.
internal/index/xapian.go, xapian_stub.go, xapian_wrapper.cpp/.h entfernt;
index.New() unterstützt jetzt nur noch "manticore". Default-Fallbacks in
Nebenwerkzeugen (archivmail-import/-export, cmd_export/cmd_reindex/...)
von "xapian" auf "manticore" korrigiert, Xapian-spezifische Tests entfernt,
Kommentare bereinigt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:12:06 +02:00

160 lines
4.0 KiB
Go

package main
import (
"context"
"flag"
"log/slog"
"os"
"strings"
"archivmail/config"
"archivmail/internal/index"
"archivmail/internal/storage"
"archivmail/pkg/mailparser"
)
// runReindex re-indexes all (or tenant-specific) emails into the configured index backend.
// Usage: archivmail reindex [-config /path/to/config.yml] [-tenant <id>]
func runReindex(args []string) {
fs := flag.NewFlagSet("reindex", flag.ExitOnError)
configPath := fs.String("config", "/etc/archivmail/config.yml", "path to config file")
tenantIDFlag := fs.Int64("tenant", 0, "tenant ID to reindex (0 = all tenants)")
fs.Parse(args)
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
cfg, err := config.Load(*configPath)
if err != nil {
logger.Error("failed to load config", "err", err)
os.Exit(1)
}
storeCfg := storage.Config{
Dir: cfg.Storage.StorePath,
Keyfile: cfg.Storage.Keyfile,
DSN: cfg.Database.DSN(),
CompressEnabled: cfg.Storage.Compress,
}
mailStore, err := storage.New(storeCfg)
if err != nil {
logger.Error("storage init failed", "err", err)
os.Exit(1)
}
defer mailStore.Close()
indexBackend := cfg.Index.Backend
if indexBackend == "" {
indexBackend = "manticore"
}
batchSize := cfg.Index.BatchSize
if batchSize <= 0 {
batchSize = 100
}
var idxMgr index.TenantIndexer
if indexBackend == "manticore" {
dsn := cfg.Index.ManticoreDSN
if dsn == "" {
dsn = "manticore@tcp(127.0.0.1:9306)/?charset=utf8mb4"
}
m, err := index.NewManticoreTenantManager(dsn)
if err != nil {
logger.Error("manticore init failed", "err", err)
os.Exit(1)
}
idxMgr = m
} else {
m, err := index.NewTenantIndexManager(cfg.Index.Path, batchSize, indexBackend)
if err != nil {
logger.Error("index manager init failed", "err", err)
os.Exit(1)
}
idxMgr = m
}
defer func() { idxMgr.Close() }()
ctx := context.Background()
var ids []string
if *tenantIDFlag > 0 {
tid := *tenantIDFlag
ids, err = mailStore.GetAllIDsByTenant(ctx, &tid)
} else {
ids, err = mailStore.GetAllIDs(ctx)
}
if err != nil {
logger.Error("failed to list mail IDs", "err", err)
os.Exit(1)
}
logger.Info("reindex: starting", "backend", indexBackend, "total", len(ids))
indexed := 0
errors := 0
for i, id := range ids {
raw, err := mailStore.Load(id)
if err != nil {
logger.Warn("reindex: load failed", "id", id, "err", err)
errors++
continue
}
pm, err := mailparser.Parse(raw)
if err != nil {
logger.Warn("reindex: parse failed", "id", id, "err", err)
errors++
continue
}
tenantID, _ := mailStore.GetTenantForMail(ctx, id)
var attachNames []string
for _, a := range pm.Attachments {
if a.Filename != "" {
attachNames = append(attachNames, a.Filename)
}
}
doc := index.MailDocument{
ID: id,
From: pm.From,
To: strings.Join(pm.To, ", "),
CC: strings.Join(pm.CC, ", "),
Subject: pm.Subject,
Body: pm.TextBody,
AttachNames: strings.Join(attachNames, " "),
HasAttachment: len(pm.Attachments) > 0,
Date: pm.Date,
Size: int64(len(raw)),
TenantID: tenantID,
}
idx := idxMgr.ForTenant(tenantID)
// Preserve existing OCR text — IndexSync uses REPLACE which would
// otherwise wipe attachment_text written by the OCR worker.
// Only read from the index when the DB confirms OCR has run (ocr_chars>0).
_, ocrChars, _ := mailStore.GetOCRMeta(ctx, id)
if ocrChars > 0 {
if reader, ok := idx.(index.AttachmentTextReader); ok {
if existing, err := reader.GetAttachmentText(id); err == nil && existing != "" {
doc.AttachmentText = existing
}
}
}
if err := idx.IndexSync(doc); err != nil {
logger.Warn("reindex: index failed", "id", id, "err", err)
errors++
continue
}
indexed++
if (i+1)%100 == 0 {
logger.Info("reindex: progress", "processed", i+1, "indexed", indexed, "errors", errors)
}
}
logger.Info("reindex: complete", "total", len(ids), "indexed", indexed, "errors", errors)
}