Documentation
¶
Overview ¶
Package mail sends email. A Mailable describes one email (envelope and content); a Mailer renders it and hands the Message to a Transport: SMTP, Resend, log (development) or Array (tests).
type Welcome struct{ User models.User }
func (m Welcome) Envelope() mail.Envelope {
return mail.Envelope{To: []string{m.User.Email}, Subject: "Welcome!"}
}
func (m Welcome) Content() mail.Content { return mail.View("mail/welcome", m) }
err := app.Mailer().Send(ctx, Welcome{User: u})
The app builds its Mailer from MAIL_* variables; Preview renders mailables in the browser during development.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/subsystems.md#mail.
Example ¶
package main
import (
"context"
"fmt"
"github.com/AlexGalhardo/vukem/mail"
)
type Welcome struct{ Email string }
func (m Welcome) Envelope() mail.Envelope {
return mail.Envelope{To: []string{m.Email}, Subject: "Welcome!"}
}
func (m Welcome) Content() mail.Content { return mail.Text("Thanks for signing up.") }
func main() {
// Array keeps messages in memory (tests); apps configure SMTP or
// Resend through MAIL_* variables.
sent := mail.NewArray()
m := mail.New(sent, "Shop <hello@shop.test>", nil)
if err := m.Send(context.Background(), Welcome{Email: "ada@example.com"}); err != nil {
fmt.Println(err)
return
}
msgs := sent.SentTo("ada@example.com")
for i := range msgs {
fmt.Println(msgs[i].From, "|", msgs[i].Subject, "|", msgs[i].Text)
}
}
Output: "Shop" <hello@shop.test> | Welcome! | Thanks for signing up.
Index ¶
- Variables
- func Build(msg *Message) ([]byte, error)
- func Preview(m *Mailer, mailables map[string]Mailable) http.Handler
- type Array
- type Attachable
- type Attachment
- type Content
- type ContextEnveloper
- type Enqueue
- type EnqueueTx
- type Envelope
- type Log
- type Mailable
- type Mailer
- func (m *Mailer) Render(ctx context.Context, mailable Mailable) (*Message, error)
- func (m *Mailer) Send(ctx context.Context, mailable Mailable) error
- func (m *Mailer) SendLater(ctx context.Context, mailable Mailable, opts ...queue.Option) error
- func (m *Mailer) SendLaterTx(ctx context.Context, ex database.Executor, mailable Mailable, ...) error
- func (m *Mailer) SetEnqueue(fn Enqueue)
- func (m *Mailer) SetEnqueueTx(fn EnqueueTx)
- func (m *Mailer) Transport() Transport
- type Mailtrap
- type MailtrapConfig
- type MailtrapError
- type Message
- type Resend
- type ResendConfig
- type ResendError
- type SMTP
- type SMTPConfig
- type Transport
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoQueue = errors.New("mail: SendLater needs a queue (use app.Mailer or Mailer.SetEnqueue)")
ErrNoQueue is returned by SendLater (SendLaterTx) on a mailer without an Enqueue (EnqueueTx).
var ErrNoRecipients = errors.New("mail: no recipients")
ErrNoRecipients is returned for messages without To, Cc or Bcc.
Functions ¶
func Build ¶
Build renders msg as RFC 5322 bytes: multipart/mixed (attachments) over multipart/related (inline parts) over multipart/alternative (text, HTML). Bcc is never written as a header.
func Preview ¶
Preview serves rendered mailables for development: the index lists names, "<name>" shows the HTML part and "<name>?text=1" the text part. Mount it only outside production:
if !app.Config.IsProduction() {
r.Handle("/_mail/", http.StripPrefix("/_mail/", mail.Preview(mailer, map[string]mail.Mailable{...})))
}
Types ¶
type Array ¶
type Array struct {
// contains filtered or unexported fields
}
Array keeps sent messages in memory (tests; vukemtest uses it).
type Attachable ¶ added in v0.21.0
type Attachable interface {
Attachments() []Attachment
}
Attachable is implemented by mailables that attach files.
type Attachment ¶
type Attachment struct {
Filename string
ContentType string
Data []byte
Inline bool
ContentID string
}
Attachment is a file sent with the message; Inline parts are referenced from HTML as cid:<ContentID>.
type Content ¶
type Content struct {
// contains filtered or unexported fields
}
Content is the body: a view (rendered by the Mailer) or literal parts.
type ContextEnveloper ¶ added in v0.25.0
ContextEnveloper is implemented by mailables whose envelope depends on the send's context, such as a subject in the request locale:
func (m Welcome) EnvelopeContext(ctx context.Context) mail.Envelope {
return mail.Envelope{To: []string{m.User.Email}, Subject: i18n.T(ctx, "mail.welcome")}
}
The Mailer calls it instead of Envelope, which stays the fallback for code without a context. SendLater renders before queueing, so the subject uses the context of the SendLater call.
type Enqueue ¶ added in v0.24.0
Enqueue stores a rendered message in a queue job that sends it later. The app's mailer (app.Mailer) enqueues on the app queue with the payload encrypted, so mail bodies (reset links, tokens) never sit in the jobs tables in clear text.
type EnqueueTx ¶ added in v0.25.0
type EnqueueTx func(ctx context.Context, ex database.Executor, msg *Message, opts ...queue.Option) error
EnqueueTx is Enqueue tied to the transaction of ex (Queue.DispatchTx): the mail is queued only if the transaction commits.
type Envelope ¶
type Envelope struct {
From string
To []string
Cc []string
Bcc []string
ReplyTo []string
Subject string
// Tags are provider metadata (Resend tags; X-Tag headers over SMTP;
// Mailtrap category and custom variables). Names must not be empty and
// neither may contain a line break or colon; Resend also limits them
// to ASCII letters, digits, "_" and "-".
Tags map[string]string
}
Envelope holds addresses and headers. Addresses accept "Name <a@b.c>".
type Log ¶
Log writes messages to a logger instead of sending them (development). Bodies are logged at debug level only.
type Mailer ¶
type Mailer struct {
// contains filtered or unexported fields
}
Mailer renders mailables and sends them.
func New ¶
New returns a Mailer sending from the default from address through t. views renders View contents (the app's view engine); it may be nil when only literal contents are used.
func (*Mailer) SendLater ¶ added in v0.24.0
SendLater renders mailable now (so rendering errors reach the caller) and sends it from a queue job, retried with the queue's backoff. The message ID stays the same across retries, so providers that deduplicate by it send once. opts select the queue, delay or retries of this mail. With QUEUE_CONNECTION=sync the job runs during the call.
func (*Mailer) SendLaterTx ¶ added in v0.25.0
func (m *Mailer) SendLaterTx(ctx context.Context, ex database.Executor, mailable Mailable, opts ...queue.Option) error
SendLaterTx is SendLater inside the transaction of ex: the mail is queued only if the transaction commits, so rolled-back work (a failed payment) never emails anyone. Rendering errors are returned at once. On the database queue the job is inserted with the transaction; on other drivers it is queued right after the commit (with QUEUE_CONNECTION=sync the mail is sent then, and a send error is logged, not returned).
func (*Mailer) SetEnqueue ¶ added in v0.24.0
SetEnqueue sets how SendLater queues messages. Call it before the mailer is used; app.Mailer sets it.
func (*Mailer) SetEnqueueTx ¶ added in v0.25.0
SetEnqueueTx sets how SendLaterTx queues messages. Call it before the mailer is used; app.Mailer sets it.
type Mailtrap ¶ added in v0.26.0
type Mailtrap struct {
// contains filtered or unexported fields
}
Mailtrap sends through the Mailtrap Email API over HTTPS, for hosts that block outbound SMTP. The API has no idempotency key, so a retried 5xx answered after delivery may send twice.
func NewMailtrap ¶ added in v0.26.0
func NewMailtrap(cfg MailtrapConfig) *Mailtrap
NewMailtrap returns the Mailtrap transport. Send fails without an API token.
type MailtrapConfig ¶ added in v0.26.0
type MailtrapConfig struct {
APIToken config.Secret
// InboxID > 0 sends to that Email Sandbox inbox
// (sandbox.api.mailtrap.io/api/send/{id}) instead of delivering.
InboxID int64
// BaseURL defaults to https://send.api.mailtrap.io, or
// https://sandbox.api.mailtrap.io with InboxID (tests use a fake).
BaseURL string
// HTTP is the client (10s timeout and no redirects by default).
HTTP *http.Client
// MaxRetries for 429 and 5xx responses and failed connections (default 2).
MaxRetries int
// MaxBackoff caps waits between retries, including Retry-After (default 5s).
MaxBackoff time.Duration
}
MailtrapConfig configures the Mailtrap Email API transport (https://docs.mailtrap.io/developers/email-sending).
type MailtrapError ¶ added in v0.26.0
MailtrapError is an error answered by the Mailtrap API. It never carries the API token or the message body; Errors may contain addresses quoted by the API.
func (*MailtrapError) Error ¶ added in v0.26.0
func (e *MailtrapError) Error() string
Error returns the status and the first errors Mailtrap listed, on one line.
func (*MailtrapError) Retryable ¶ added in v0.26.0
func (e *MailtrapError) Retryable() bool
Retryable reports whether the request may succeed later.
type Message ¶
type Message struct {
// ID identifies the message (Message-ID, Resend idempotency key).
ID string
From string
To []string
Cc []string
Bcc []string
ReplyTo []string
Subject string
HTML string
Text string
Attachments []Attachment
Tags map[string]string
}
Message is a rendered email ready for a Transport.
type Resend ¶
type Resend struct {
// contains filtered or unexported fields
}
Resend sends through the Resend HTTP API. The message ID is the Idempotency-Key, so retries never send twice.
type ResendConfig ¶
type ResendConfig struct {
APIKey config.Secret
// BaseURL defaults to https://api.resend.com (tests use a fake).
BaseURL string
// HTTP is the client (10s timeout by default).
HTTP *http.Client
// MaxRetries for 429 and 5xx responses and network errors (default 2).
MaxRetries int
// MaxBackoff caps waits between retries, including Retry-After (default 5s).
MaxBackoff time.Duration
}
ResendConfig configures the Resend transport (https://resend.com).
type ResendError ¶
ResendError is an error answered by the Resend API. It never contains the API key.
func (*ResendError) Error ¶
func (e *ResendError) Error() string
Error returns the status, Resend error name and message.
func (*ResendError) Retryable ¶
func (e *ResendError) Retryable() bool
Retryable reports whether the request may succeed later.
type SMTP ¶
type SMTP struct {
// contains filtered or unexported fields
}
SMTP sends through an SMTP server with STARTTLS (or implicit TLS on 465).
type SMTPConfig ¶
type SMTPConfig struct {
Host string
Port int // 587 (STARTTLS, default) or 465 (implicit TLS)
Username string
Password config.Secret
// InsecureSkipTLS sends without TLS (local catchers such as Mailpit
// only); by default STARTTLS is required.
InsecureSkipTLS bool
// Timeout bounds dialing and the whole exchange (default 30s).
Timeout time.Duration
// TLSConfig overrides the TLS settings (tests).
TLSConfig *tls.Config
}
SMTPConfig configures the SMTP transport.