Tabelle imap_account_mails, Importer schreibt die Zuordnung auch bei Duplikaten, Zugriffsregel und Index-Tokens nutzen sie, archivmail imap-link befüllt den Bestand (Trockenlauf Standard, --apply schreibt nur die Zuordnung). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
49 lines
1.5 KiB
Go
49 lines
1.5 KiB
Go
package imap
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// Same tenant rule as linkAccountMailSQL, evaluated read-only so the dry run
|
|
// reports exactly what --apply would write.
|
|
const linkLookupSQL = `
|
|
SELECT e.message_id, e.id,
|
|
(e.tenant_id IS NOT DISTINCT FROM a.tenant_id
|
|
OR (a.tenant_id IS NOT NULL AND EXISTS (
|
|
SELECT 1 FROM email_refs r WHERE r.email_id = e.id AND r.tenant_id = a.tenant_id))) AS tenant_ok,
|
|
EXISTS (SELECT 1 FROM imap_account_mails m WHERE m.account_id = a.id AND m.mail_id = e.id) AS linked
|
|
FROM emails e
|
|
CROSS JOIN imap_accounts a
|
|
WHERE a.id = $1 AND e.message_id = ANY($2)
|
|
`
|
|
|
|
func NewLinkLookup(pool *pgxpool.Pool) LinkLookup {
|
|
return func(ctx context.Context, accountID int64, ids []string) (map[string]LinkCandidate, error) {
|
|
out := make(map[string]LinkCandidate, len(ids))
|
|
for i := 0; i < len(ids); i += verifyLookupBatch {
|
|
end := min(i+verifyLookupBatch, len(ids))
|
|
rows, err := pool.Query(ctx, linkLookupSQL, accountID, ids[i:end])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query emails: %w", err)
|
|
}
|
|
for rows.Next() {
|
|
var msgID string
|
|
var c LinkCandidate
|
|
if err := rows.Scan(&msgID, &c.MailID, &c.TenantOK, &c.Linked); err != nil {
|
|
rows.Close()
|
|
return nil, fmt.Errorf("scan link candidate: %w", err)
|
|
}
|
|
out[msgID] = c
|
|
}
|
|
rows.Close()
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("rows: %w", err)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
}
|