feat(PROJ-30): Xapian → Manticore Search Migration
- internal/index/manticore.go: ManticoreTenantManager + manticoreIndex (RT-Indizes, CGO-frei) - internal/index/index.go: TenantIndexer Interface (Xapian + Manticore) - internal/index/tenant_worker.go: mgr-Typ auf TenantIndexer Interface - internal/api/server.go: idxMgr auf TenantIndexer Interface - config/config.go: IndexConfig.ManticoreDSN Feld - cmd/archivmail/cmd_reindex.go: reindex Subkommando - cmd/archivmail/main.go: Manticore-Branch + reindex Case - go.mod: github.com/go-sql-driver/mysql v1.8.1 - update.sh: Manticore auto-install, CGO_ENABLED=0, config.yml migration, auto-reindex fix(IMAP): TCP-Deadline-Wrapper für steckengebliebene Imports fix(auth): Email-Claim in JWT für User-Isolation fix(search): User-Isolation via sess.Email (fail-safe) fix(ui): Admin-Login Auth-Cache, Logout-Redirect, IMAP-Polling-Resilienz Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-10
@@ -23,7 +23,6 @@ type Importer struct {
|
||||
mailStore *storage.Store
|
||||
idx index.Indexer
|
||||
logger *slog.Logger
|
||||
TenantID *int64 // optional tenant assignment for stored mails
|
||||
}
|
||||
|
||||
// NewImporter creates a new Importer wired to the storage and index backends.
|
||||
@@ -88,7 +87,7 @@ func (imp *Importer) doImport(ctx context.Context, acc *Account, password string
|
||||
}
|
||||
|
||||
// List all folders
|
||||
folders, err := ListFolders(c)
|
||||
folders, err := ListFolders(c.Client)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("list folders: %w", err)
|
||||
}
|
||||
@@ -159,11 +158,13 @@ func (imp *Importer) doImport(ctx context.Context, acc *Account, password string
|
||||
}
|
||||
batch := uids[i:end]
|
||||
|
||||
count, err := imp.fetchBatch(ctx, c, batch, log)
|
||||
// Set per-batch deadline to prevent indefinite blocking on stalled connections.
|
||||
c.SetFetchDeadline()
|
||||
count, err := imp.fetchBatch(ctx, c.Client, batch, acc.TenantID, log)
|
||||
c.ClearDeadline()
|
||||
if err != nil {
|
||||
log.Error("batch fetch error", "folder", folder, "offset", i, "err", err)
|
||||
// Continue with the next batch rather than aborting entirely
|
||||
continue
|
||||
log.Error("batch fetch error — aborting import", "folder", folder, "offset", i, "err", err)
|
||||
return imported, fmt.Errorf("fetch batch %d in %q: %w", i, folder, err)
|
||||
}
|
||||
|
||||
imported += count
|
||||
@@ -177,7 +178,7 @@ func (imp *Importer) doImport(ctx context.Context, acc *Account, password string
|
||||
}
|
||||
|
||||
// fetchBatch fetches and stores a batch of messages by UID.
|
||||
func (imp *Importer) fetchBatch(ctx context.Context, c *imapclient.Client, uids []imapv2.UID, log *slog.Logger) (int, error) {
|
||||
func (imp *Importer) fetchBatch(ctx context.Context, c *imapclient.Client, uids []imapv2.UID, tenantID *int64, log *slog.Logger) (int, error) {
|
||||
if len(uids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -212,7 +213,7 @@ func (imp *Importer) fetchBatch(ctx context.Context, c *imapclient.Client, uids
|
||||
continue
|
||||
}
|
||||
|
||||
if err := imp.storeAndIndex(raw, log); err != nil {
|
||||
if err := imp.storeAndIndex(raw, tenantID, log); err != nil {
|
||||
log.Warn("failed to store/index message", "err", err)
|
||||
continue
|
||||
}
|
||||
@@ -229,10 +230,10 @@ func (imp *Importer) fetchBatch(ctx context.Context, c *imapclient.Client, uids
|
||||
}
|
||||
|
||||
// storeAndIndex saves a raw email to storage and indexes it.
|
||||
func (imp *Importer) storeAndIndex(raw []byte, log *slog.Logger) error {
|
||||
func (imp *Importer) storeAndIndex(raw []byte, tenantID *int64, log *slog.Logger) error {
|
||||
ctx := context.Background()
|
||||
// Save to file storage (deduplicates by SHA256 automatically)
|
||||
id, err := imp.mailStore.Save(ctx, raw, time.Now(), imp.TenantID)
|
||||
id, err := imp.mailStore.Save(ctx, raw, time.Now(), tenantID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("save: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user