Files
archivmail/internal/index/index.go
T
sysops 472ba6a087 feat(PROJ-53): Konfigurierbare Listenanzahl pro Seite
- users.list_page_size (Default 25), PATCH /api/auth/preferences,
  Whitelist 25/50/100/200, Wert in login/me-Response
- Settings-UI mit Select, /search nutzt gespeicherte Seitengröße
- /api/search page_size serverseitig auf max. 500 gecappt

fix(PROJ-46): login_attempts-Migration nutzte s.db statt s.pool
(Backend kompilierte nicht)

feat(PROJ-50): DSGVO-Löschersuchen Backend (dsgvo_requests, Handler,
cc_addr/bcc_addr Indexerweiterung) — noch nicht QA'd/deployed
2026-06-14 22:25:02 +02:00

102 lines
3.4 KiB
Go

package index
import (
"fmt"
"time"
)
// MailDocument is the indexed representation of a stored email.
type MailDocument struct {
ID string
From string
To string
CC string // PROJ-50: CC addresses (space-joined) for DSGVO address search
BCC string // PROJ-50: BCC addresses (space-joined) for DSGVO address search
Subject string
Body string
AttachNames string
AttachmentText string // PROJ-35: OCR-extracted text from PDF/image attachments
HasAttachment bool
Date time.Time
Size int64
TenantID *int64 // nil = global / superadmin context
}
// SearchRequest specifies search parameters.
type SearchRequest struct {
Query string
From string
To string
OwnEmail string
AnyAddress string // PROJ-50: match address in from/to/cc/bcc (DSGVO Löschersuchen)
DateFrom *time.Time
DateTo *time.Time
HasAttachment *bool // nil=no filter, true=only with, false=only without
Sort string // "relevance", "date_asc", "date_desc" (default: date_desc)
PageSize int
Page int
}
// Hit is a single search result.
//
// PROJ-44: Snippet and MatchField are populated by the Manticore Search path
// when a full-text query was provided. They remain empty for filter-only
// searches (e.g. date range without query) and when the per-hit highlight
// pass fails — the hit is still returned in that case (no hard error).
type Hit struct {
ID string `json:"id"`
Score float64 `json:"score"`
Snippet string `json:"snippet,omitempty"` // HTML-marked excerpt with <b>match</b> tags
MatchField string `json:"match_field,omitempty"` // subject|body|attachment_text|attachment_names|from_addr|to_addr
}
// SearchResult holds paginated search results.
type SearchResult struct {
Total int
Hits []Hit
}
// Indexer is the interface for full-text email indexing.
type Indexer interface {
IndexSync(doc MailDocument) error
Search(req SearchRequest) (*SearchResult, error)
Delete(id string) error
Close() error
}
// AttachmentTextUpdater is implemented by indexers that support partial
// updates of the OCR-extracted attachment text. Optional add-on to Indexer:
// callers should type-assert and degrade gracefully if not supported.
//
// PROJ-35: Manticore implements this; legacy Xapian does not.
type AttachmentTextUpdater interface {
UpdateAttachmentText(mailID, text string) error
}
// AttachmentTextReader is implemented by indexers that can return the stored
// OCR-extracted attachment text for a mail. Optional add-on to Indexer.
//
// PROJ-44: Manticore implements this for the /api/mails/{id}/ocr-text endpoint.
type AttachmentTextReader interface {
GetAttachmentText(mailID string) (string, error)
}
// TenantIndexer manages per-tenant Indexer instances.
// Implemented by ManticoreTenantManager (primary) and TenantIndexManager (legacy Xapian).
type TenantIndexer interface {
ForTenant(tenantID *int64) Indexer
Global() Indexer
Close() error
}
// New creates an Indexer for the specified backend.
// Deprecated: use NewManticoreTenantManager instead.
func New(dir string, batchSize int, backend string) (Indexer, error) {
switch backend {
case "xapian":
return newXapian(dir)
default:
return nil, fmt.Errorf("unknown index backend: %q (use manticore via NewManticoreTenantManager)", backend)
}
}