fix(PROJ-86): Manticore-Index-Drift (emails_global) + Datumssortierung bei fehlendem Date-Header
emails_global bekam nie Tenant-Mails gespiegelt, Superadmin-Suche sah nur
~30% aller Mails. Zusätzlich sanken Mails ohne gültigen Date-Header
(date_ts=0) beim datumssortierten Listing ans Ende aller Ergebnisse und
waren dadurch praktisch unauffindbar ("GUI zeigt neue Mails nicht") -
Import/Indexierung liefen technisch korrekt, nur Sortierung/Spiegelung
waren kaputt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPFC6Jk2ke1Pq9XcuVGP1R
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
7727ad5cdf
commit
26c0e04c75
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"archivmail/config"
|
||||
"archivmail/internal/index"
|
||||
@@ -129,7 +130,7 @@ func main() {
|
||||
Body: pm.TextBody + " " + pm.HTMLBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, time.Now()),
|
||||
Size: int64(len(raw)),
|
||||
}
|
||||
|
||||
|
||||
@@ -243,6 +243,8 @@ func reindexOne(ctx context.Context, store *storage.Store, mgr index.TenantIndex
|
||||
}
|
||||
}
|
||||
|
||||
receivedAt, _ := store.GetReceivedAt(ctx, id)
|
||||
|
||||
doc := index.MailDocument{
|
||||
ID: id,
|
||||
From: pm.From,
|
||||
@@ -252,7 +254,7 @@ func reindexOne(ctx context.Context, store *storage.Store, mgr index.TenantIndex
|
||||
Body: pm.TextBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, receivedAt),
|
||||
Size: int64(len(raw)),
|
||||
TenantID: tenantID,
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ func importMessage(mailStore *storage.Store, idxMgr index.TenantIndexer, raw []b
|
||||
Body: pm.TextBody + " " + pm.HTMLBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, time.Now()),
|
||||
Size: int64(len(raw)),
|
||||
TenantID: tenantID,
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ func runIndexPending(args []string) {
|
||||
Body: pm.TextBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, m.ReceivedAt),
|
||||
Size: int64(len(raw)),
|
||||
TenantID: m.TenantID,
|
||||
}
|
||||
|
||||
@@ -111,6 +111,11 @@ func runPurge(args []string) {
|
||||
if err := idxMgr.ForTenant(tenantID).Delete(id); err != nil {
|
||||
logger.Warn("purge: index cleanup failed", "id", id, "err", err)
|
||||
}
|
||||
if tenantID != nil {
|
||||
if err := idxMgr.Global().Delete(id); err != nil {
|
||||
logger.Warn("purge: global index cleanup failed", "id", id, "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if audlog != nil {
|
||||
|
||||
@@ -107,6 +107,7 @@ func runReindex(args []string) {
|
||||
}
|
||||
|
||||
tenantID, _ := mailStore.GetTenantForMail(ctx, id)
|
||||
receivedAt, _ := mailStore.GetReceivedAt(ctx, id)
|
||||
|
||||
var attachNames []string
|
||||
for _, a := range pm.Attachments {
|
||||
@@ -124,7 +125,7 @@ func runReindex(args []string) {
|
||||
Body: pm.TextBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, receivedAt),
|
||||
Size: int64(len(raw)),
|
||||
TenantID: tenantID,
|
||||
}
|
||||
@@ -148,6 +149,13 @@ func runReindex(args []string) {
|
||||
errors++
|
||||
continue
|
||||
}
|
||||
// Superadmin/global search reads emails_global — mirror every
|
||||
// tenant-scoped mail there too, not just untenanted ones.
|
||||
if tenantID != nil {
|
||||
if err := idxMgr.Global().IndexSync(doc); err != nil {
|
||||
logger.Warn("reindex: global index failed", "id", id, "err", err)
|
||||
}
|
||||
}
|
||||
indexed++
|
||||
|
||||
if (i+1)%100 == 0 {
|
||||
|
||||
@@ -433,7 +433,7 @@ func main() {
|
||||
smtpDaemon.SetIndexCallback(func(raw []byte, id string) {
|
||||
// Look up the tenant_id for this email from DB metadata.
|
||||
tenantID, _ := mailStore.GetTenantForMail(context.Background(), id)
|
||||
submitToWorker(tenantWorker, mailStore, raw, id, tenantID, logger, ocrWorker, cfg.Index.BatchMode, cfg.OCR.BatchMode)
|
||||
submitToWorker(tenantWorker, mailStore, raw, id, tenantID, logger, ocrWorker, cfg.Index.BatchMode, cfg.OCR.BatchMode, time.Now())
|
||||
})
|
||||
// Wire tenant routing into SMTP daemon
|
||||
if cfg.SMTP.TenantRouting == "domain" {
|
||||
@@ -592,7 +592,7 @@ func reloadOCRPauseWindow(configPath string, ocrWorker *ocr.Worker, logger *slog
|
||||
// in-memory submit is skipped so the unstarted batch-mode worker queue does
|
||||
// not fill up and log spurious "queue full" warnings. The mail still gets its
|
||||
// indexed_at / ocr_status state so the cron batch jobs pick it up.
|
||||
func submitToWorker(worker *index.TenantIndexWorker, store *storage.Store, raw []byte, id string, tenantID *int64, logger *slog.Logger, ocrWorker *ocr.Worker, indexBatchMode, ocrBatchMode bool) {
|
||||
func submitToWorker(worker *index.TenantIndexWorker, store *storage.Store, raw []byte, id string, tenantID *int64, logger *slog.Logger, ocrWorker *ocr.Worker, indexBatchMode, ocrBatchMode bool, dateFallback time.Time) {
|
||||
pm, err := mailparser.Parse(raw)
|
||||
if err != nil {
|
||||
logger.Warn("index: parse failed, skipping indexing", "id", id, "err", err)
|
||||
@@ -615,7 +615,7 @@ func submitToWorker(worker *index.TenantIndexWorker, store *storage.Store, raw [
|
||||
Body: pm.TextBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, dateFallback),
|
||||
Size: int64(len(raw)),
|
||||
TenantID: tenantID,
|
||||
}
|
||||
@@ -688,9 +688,10 @@ func runBackfill(ctx context.Context, store *storage.Store, idx index.Indexer, w
|
||||
if !alreadyIndexed {
|
||||
needIndex++
|
||||
tenantID, _ := store.GetTenantForMail(ctx, id)
|
||||
receivedAt, _ := store.GetReceivedAt(ctx, id)
|
||||
// runBackfill only runs when index.batch_mode is off; the OCR
|
||||
// batch case is handled by passing a nil ocrWorker from the caller.
|
||||
submitToWorker(worker, store, raw, id, tenantID, logger, ocrWorker, false, false)
|
||||
submitToWorker(worker, store, raw, id, tenantID, logger, ocrWorker, false, false, receivedAt)
|
||||
}
|
||||
|
||||
if count%100 == 0 {
|
||||
@@ -731,6 +732,8 @@ func reindexTenant(ctx context.Context, store *storage.Store, mgr index.TenantIn
|
||||
continue
|
||||
}
|
||||
|
||||
receivedAt, _ := store.GetReceivedAt(ctx, id)
|
||||
|
||||
pm, parseErr := mailparser.Parse(raw)
|
||||
if parseErr != nil {
|
||||
logger.Warn("reindex tenant: parse failed", "tenant_id", tenantID, "id", id, "err", parseErr)
|
||||
@@ -754,7 +757,7 @@ func reindexTenant(ctx context.Context, store *storage.Store, mgr index.TenantIn
|
||||
Body: pm.TextBody,
|
||||
AttachNames: strings.Join(attachNames, " "),
|
||||
HasAttachment: len(pm.Attachments) > 0,
|
||||
Date: pm.Date,
|
||||
Date: index.EffectiveDate(pm.Date, receivedAt),
|
||||
Size: int64(len(raw)),
|
||||
TenantID: &tid,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user