email

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 28, 2026 License: BSD-3-Clause Imports: 28 Imported by: 0

Documentation

Overview

Package email provides helpers for formatting and delivering mail via SMTP.

SendWithClient sanitizing and deduplicates recipients case-insensitively across the To, CC, and BCC lists in that order. The primary recipient is always addressed first, and only one RCPT command is issued per unique address. CC headers include only deduplicated CC recipients while BCC addresses are added to the envelope but never leaked via headers.

Index

Constants

View Source
const DefaultAttachmentCacheLimit int64 = 256 << 20 // 256 MiB

DefaultAttachmentCacheLimit caps the total bytes of base64-encoded attachment payloads held in cache per dispatch. Files whose encoded size would exceed the remaining budget are streamed inline instead.

Variables

View Source
var (
	ErrAttachmentTooLarge        = errors.New("attachment exceeds maximum allowed size")
	ErrUnsupportedAttachmentType = errors.New("unsupported attachment type")
)

Functions

func ConnectSMTP

func ConnectSMTP(cfg config.SMTPConfig) (*smtp.Client, error)

ConnectSMTP establishes a persistent, authenticated SMTP client with TLS and context support.

func ConnectSMTPWithContext added in v1.1.0

func ConnectSMTPWithContext(ctx context.Context, cfg config.SMTPConfig) (*smtp.Client, error)

ConnectSMTPWithContext establishes a persistent, authenticated SMTP client with TLS and context support. This allows for proper cancellation during connection attempts.

func GetMaxBackoff added in v1.1.0

func GetMaxBackoff() time.Duration

GetMaxBackoff returns the current max backoff duration (thread-safe)

func GetRetryLimit added in v1.1.0

func GetRetryLimit() int

GetRetryLimit returns the current retry limit (thread-safe)

func SendWithClient

func SendWithClient(client *smtp.Client, cfg config.SMTPConfig, task Task, cache *AttachmentCache) (err error)

SendWithClient formats and delivers an email using an active SMTP client. Recipients from the To, CC, and BCC fields are trimmed, deduplicated in that order (case-insensitively), and issued RCPT commands exactly once with the primary recipient always first. The CC header is rendered from the unique CC list, while BCC addresses are kept solely on the SMTP envelope. It uses cfg.SMTP.From as both the envelope sender and header From address.

cache may be nil; when supplied, attachments are read and base64-encoded once per dispatch run and reused across all recipients.

func SetMaxBackoff added in v1.1.0

func SetMaxBackoff(d time.Duration)

SetMaxBackoff sets the maximum backoff delay for retries

func SetRetryLimit

func SetRetryLimit(limit int)

SetRetryLimit sets the max retry attempts per failed email, with exponential backoff.

Types

type AttachmentCache added in v1.1.0

type AttachmentCache struct {
	// contains filtered or unexported fields
}

AttachmentCache memoizes base64-encoded attachment payloads keyed by absolute path. Population is lazy and concurrency-safe. A single shared cache should be created per dispatch and torn down with Reset when the run ends.

func NewAttachmentCache added in v1.1.0

func NewAttachmentCache(maxBytes int64) *AttachmentCache

NewAttachmentCache constructs a cache with the given total-size cap. A non-positive cap falls back to DefaultAttachmentCacheLimit.

func (*AttachmentCache) Get added in v1.1.0

func (c *AttachmentCache) Get(path string) (*cachedAttachment, error)

Get returns the cached attachment for path, populating the cache on first access. Returns (nil, errCacheCapExceeded) when the file would push the cache past its byte cap; callers should fall back to streaming.

func (*AttachmentCache) Reset added in v1.1.0

func (c *AttachmentCache) Reset()

Reset releases all cached payloads. Call once when the dispatch run ends.

type AttachmentProcessor added in v1.0.0

type AttachmentProcessor struct {
	// contains filtered or unexported fields
}

AttachmentProcessor handles efficient processing of email attachments

func NewAttachmentProcessor added in v1.0.0

func NewAttachmentProcessor(maxSize int64) *AttachmentProcessor

NewAttachmentProcessor creates a new attachment processor

func (*AttachmentProcessor) ProcessAttachment added in v1.0.0

func (p *AttachmentProcessor) ProcessAttachment(path string) (io.Reader, string, error)

ProcessAttachment handles a single attachment efficiently. It opens the file only once and reuses the handle for both MIME detection and reading.

type DispatchOptions added in v1.1.0

type DispatchOptions struct {
	Context context.Context
	Monitor monitor.Monitor
	Tracker OffsetTracker
	// StartOffset is retained for API compatibility but is no longer consulted
	// for offset arithmetic — Task.Index is treated as an absolute position
	// and Tracker.MarkComplete handles concurrency-safe advancement.
	StartOffset int
	// AttachmentCache is reused across all sends in this dispatch. When nil,
	// the dispatcher creates one with DefaultAttachmentCacheLimit. Pass a
	// pre-populated cache to share across multiple dispatches.
	AttachmentCache *AttachmentCache
	// OffsetSaveInterval controls how often the tracker is flushed to disk.
	// Zero falls back to 200ms. Negative disables periodic flushing (only the
	// final save at dispatch end is performed).
	OffsetSaveInterval time.Duration
	// PendingEmails seeds the monitor dashboard with all expected recipients
	// before any worker runs. StartDispatcher populates this from the task
	// slice automatically; streaming callers should supply it from their
	// recipient list so the dashboard shows everyone in Pending state.
	PendingEmails []string
}

DispatchOptions contains optional parameters for email dispatching

type DispatchResult added in v1.1.0

type DispatchResult struct {
	Sent   int
	Failed int
}

DispatchResult holds summary statistics from a dispatch run.

func StartDispatcher

func StartDispatcher(tasks []Task, cfg config.SMTPConfig, concurrency int, batchSize int, opts *DispatchOptions) DispatchResult

StartDispatcher sends emails using a worker pool. It is the bulk entry point for slice-based task lists; for streaming sources see StartDispatcherStream.

Per-success offset persistence is coalesced through a background flusher so the disk syncs do not serialize the worker hot path. Offset semantics use a contiguous high-water mark (Tracker.MarkComplete) so resume is correct under concurrency.

func StartDispatcherStream added in v1.1.0

func StartDispatcherStream(ctx context.Context, taskCh <-chan Task, cfg config.SMTPConfig, concurrency int, batchSize int, opts *DispatchOptions) DispatchResult

StartDispatcherStream consumes tasks from a channel rather than a slice. Use it when the recipient list is large enough that holding all rendered tasks in RAM is undesirable. The caller is responsible for closing taskCh when no more tasks will be produced.

All workers will return once taskCh is drained or ctx is cancelled.

type OffsetTracker added in v1.1.0

type OffsetTracker interface {
	UpdateOffset(offset int)
	MarkComplete(idx int)
	Save() error
}

OffsetTracker interface for tracking email delivery progress.

MarkComplete records that the absolute task index `idx` was successfully delivered. Implementations are expected to maintain a contiguous high-water mark — the saved offset advances only past indices that have all been completed, even when workers finish out of order.

UpdateOffset and Save remain on the interface for backward compatibility with external callers (CLI cleanup paths, tests).

type Task

type Task struct {
	Recipient   parser.Recipient
	Subject     string
	Body        string // HTML body (from --template)
	PlainText   string // Plain-text body (from --text, for multipart/alternative)
	Retries     int
	Attachments []string
	CC          []string
	BCC         []string
	Index       int // Position in original task list for offset tracking
}

Task represents an email send job with recipient data.

type TemplateCache added in v1.0.0

type TemplateCache struct {
	// contains filtered or unexported fields
}

TemplateCache provides thread-safe caching of parsed email templates

func NewTemplateCache added in v1.0.0

func NewTemplateCache(maxAge time.Duration, maxSize int) *TemplateCache

NewTemplateCache creates a new template cache with given configuration

func (*TemplateCache) Clear added in v1.0.0

func (c *TemplateCache) Clear()

Clear removes all templates from cache

func (*TemplateCache) Get added in v1.0.0

func (c *TemplateCache) Get(path string) (*template.Template, error)

Get retrieves a template from cache (checking age) or parses it if missing/expired.

func (*TemplateCache) GetCurrentSize added in v1.1.0

func (c *TemplateCache) GetCurrentSize() int

GetCurrentSize returns the current number of cached templates

func (*TemplateCache) Stop added in v1.1.0

func (c *TemplateCache) Stop()

Stop stops cleanup goroutine and cleans up resources

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL