mail

package
v2.16.1 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package mail sends email from a web app: a Mailer service apps inject, configured from the environment (or .env), with a log driver for development and tests and an SMTP driver for production.

app := web.New(mail.Provide())   // MAIL_DRIVER, MAIL_HOST, … from the environment

@web.Post("/signup")
func Signup(ctx context.Context, in NewUser, m *mail.Mailer) error {
	return m.Send(ctx, mail.Message{
		To:      []string{in.Email},
		Subject: "Welcome",
		HTML:    <Welcome name={in.Name} />,   // the text part is made from it
	})
}

The default driver, log, sends nothing: it logs each message and keeps it for Sent. In development the toolbar's Mail panel shows the messages a request sent, the HTML in a preview.

Index

Constants

This section is empty.

Variables

View Source
var ErrQueueFull = errors.New("mail: the queue is full")

ErrQueueFull is SendLater's error when Config.Queue messages are waiting already.

Functions

func Provide

func Provide(cfg ...Config) web.Option

Provide provides a *Mailer to the app: from cfg when given, else from the environment, read when the Mailer is first needed (after web.New loaded the env files).

Types

type Attachment

type Attachment struct {
	Filename    string
	ContentType string // from the file name's extension when empty
	Data        []byte
}

Attachment is a file sent with a message.

type Config

type Config struct {
	// Driver is "log" (the default: log the message, send nothing) or
	// "smtp". Transport, when set, sends instead of either.
	Driver    string
	Transport Transport

	Host     string
	Port     int // 0: 465 with tls, 587 with starttls, 25 with none
	Username string
	Password string
	// Encryption is "starttls" (the default: upgrade the connection, and
	// fail when the server can't), "tls" (TLS from the start, port 465) or
	// "none" (local catchers only; the password isn't sent unencrypted
	// except to localhost).
	Encryption string
	TLS        *tls.Config // a private CA, or a test server's certificate
	LocalName  string      // the name the client gives in EHLO; "localhost"
	// Timeout bounds dialing the server, and each send when the context has
	// no deadline of its own (six times as long). Default 10s.
	Timeout time.Duration

	From     string // the sender when a message names none: "app@example.com" or "App <app@example.com>"
	FromName string // its name, when From has none

	// SendLater's retries after the first attempt (default 3) and the wait
	// before the first, doubling after each (default 2s). Only temporary
	// failures are retried: a 4xx answer, a connection that fails.
	Retries int
	Backoff time.Duration
	// Queue bounds the messages SendLater holds at once, sending or
	// waiting (default 1000): past it, SendLater refuses with ErrQueueFull
	// rather than hold more in memory.
	Queue int

	Logger *slog.Logger // the log driver's and SendLater's; slog's default
}

Config configures a Mailer. ConfigFromEnv reads it from the environment.

func ConfigFromEnv

func ConfigFromEnv() Config

ConfigFromEnv reads the configuration from the environment (and the env files web.New loaded): MAIL_DRIVER, MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_ENCRYPTION, MAIL_FROM, MAIL_FROM_NAME. A MAIL_PORT that isn't a number fails the app's next Build or Start, naming it.

type InvalidError

type InvalidError struct{ Reason string }

InvalidError is a message refused before sending: what is wrong with it.

func (*InvalidError) Error

func (e *InvalidError) Error() string

type Mailer

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

Mailer sends messages. It is safe for concurrent use.

func New

func New(cfg Config) (*Mailer, error)

New makes a Mailer, checking cfg.

func (*Mailer) Driver

func (m *Mailer) Driver() string

Driver is the driver sending: "log", "smtp" or "custom" (a Transport).

func (*Mailer) Flush

func (m *Mailer) Flush(ctx context.Context) error

Flush waits until the messages SendLater queued are sent or given up, or ctx is done: for tests.

func (*Mailer) Reset

func (m *Mailer) Reset()

Reset forgets what the log driver kept.

func (*Mailer) Send

func (m *Mailer) Send(ctx context.Context, msg Message) error

Send builds the message and sends it with the Mailer's driver. A message that isn't valid — no recipient, an address that doesn't parse, a line break in a header — is refused before anything is sent.

func (*Mailer) SendLater

func (m *Mailer) SendLater(ctx context.Context, msg Message) error

SendLater builds the message now — so it is refused now when invalid, and its HTML renders with ctx — and sends it in the background, retrying temporary failures (Config.Retries, Config.Backoff). A failure after the last attempt is logged. Stop waits for the messages still queued. Past Config.Queue messages held at once, it refuses with ErrQueueFull.

func (*Mailer) Sent

func (m *Mailer) Sent() []Sent

Sent is what the log driver kept, oldest first (the latest 1000); nil for another driver.

func (*Mailer) Stop

func (m *Mailer) Stop(ctx context.Context) error

Stop refuses SendLater from now on and waits for the messages queued until ctx is done; then it cancels them. The app calls it on Stop.

type Message

type Message struct {
	From    string // the Mailer's From when empty
	To      []string
	Cc      []string
	Bcc     []string // receive it without being listed in it
	ReplyTo string
	Subject string
	// Text is the plain-text body; made from HTML when empty.
	Text string
	// HTML is the HTML body: a JSX or templ component, or ui.Safe(html)
	// for HTML you have as a string. It renders with Send's context.
	HTML        ui.Node
	Headers     map[string]string // more headers: List-Unsubscribe, X-Campaign, …
	Attachments []Attachment
}

Message is an email. Every address is "user@example.com" or "Name <user@example.com>".

type Sent

type Sent struct {
	Message
	From       string   // the sender, as written in the message
	Recipients []string // every address it went to, Bcc included
	HTML, Text string   // the bodies, rendered
	Raw        []byte   // the MIME message
	At         time.Time
}

Sent is a message the log driver kept: as given, and as built.

type Transport

type Transport interface {
	Send(ctx context.Context, from string, to []string, msg []byte) error
}

Transport sends a message built by the Mailer: from is the envelope sender, to every recipient (Bcc included), msg the whole MIME message. A Config's Transport replaces the driver, for an HTTP API, say.

Directories

Path Synopsis
Package mailtest tests the mail an app sends: a quiet log-driver Mailer to provide, and helpers reading what it kept.
Package mailtest tests the mail an app sends: a quiet log-driver Mailer to provide, and helpers reading what it kept.

Jump to

Keyboard shortcuts

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