Help us shape the future of Go! Take the 2026 Go Developer Survey

mailer

package
v0.56.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package mailer sends transactional email (share/invite notices) over an operator-configured SMTP server. The config lives in the settings table (smtp.*) and is validated by a periodic job (server.Start runs Verify every 5 minutes). Send is a no-op error (ErrNotVerified) until a verification succeeds, so the invite flow can fall back to showing the link on-screen.

Index

Constants

View Source
const (
	KeyHost       = "smtp.host"
	KeyPort       = "smtp.port"
	KeyUser       = "smtp.username"
	KeyPass       = "smtp.password"
	KeyFrom       = "smtp.from"
	KeyTLS        = "smtp.tls" // "starttls" | "tls" | "none"
	KeyVerifiedAt = "smtp.verified_at"
)

Settings keys (read via db.Store.GetSetting / written by the admin settings UI).

Variables

View Source
var (
	ErrNotConfigured = errors.New("smtp not configured")
	ErrNotVerified   = errors.New("smtp config not verified")
)

ErrNotConfigured / ErrNotVerified let callers distinguish "no SMTP set up" from "set up but not yet confirmed working".

Functions

func Reason

func Reason(err error) string

Reason names why an SMTP check or send failed, for a screen that has to SAY it: the admin settings' "Send test" printed the Go error ("dial tcp 10.0.0.5:587: connect: connection refused", "535 5.7.8 Authentication credentials invalid") as the only answer (QA, 2026-09-21). The screen now says the reason in the reader's language and keeps the error text as a second line.

The reasons, in the order a connection meets them:

not_configured  no host/port saved
host            the name does not resolve
connect         nothing answered at host:port (refused, timed out, unreachable)
tls             the TLS handshake or the certificate failed
starttls        STARTTLS was required and the server does not offer it
auth            the server rejected the username or password
recipient       the server refused the address (5xx on RCPT)
rejected        any other SMTP refusal
other           anything else

func WithLanguage

func WithLanguage(ctx context.Context, lang string) context.Context

WithLanguage tags the mail sent under ctx with the language it is written in (a BCP 47 tag, as internal/srvtext picked it). Send writes it as the message's Content-Language, which mail clients use for spell-checking, "translate this message" offers and screen-reader voices.

⚠ A context value rather than a Send parameter: Send has callers in five packages and fakes in their tests, and the language is a label ON the message, not an input to delivery — an untagged send is still correct.

⚠ Right-to-left languages need nothing more here: filex sends text/plain only, and a plain-text body carries no direction — clients lay each paragraph out by its own letters (the Unicode bidirectional algorithm). A `dir="rtl"` belongs on an HTML body, and there is none; if an HTML part is ever added, it must carry dir from srvtext.IsRTL(lang).

Types

type Config

type Config struct {
	Host, Port, Username, Password, From, TLS string
}

Config is the resolved SMTP connection config.

type Service

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

Service reads config from the store, verifies it, and sends mail.

func New

func New(store db.Store) *Service

New constructs the mailer service.

func (*Service) Load

func (s *Service) Load(ctx context.Context) Config

Load reads the current SMTP config from settings.

func (*Service) PrimeFromStore

func (s *Service) PrimeFromStore(ctx context.Context)

PrimeFromStore optimistically restores the verified flag from the persisted smtp.verified_at setting. The in-memory flag resets to false on every process restart (deploy), which would make sends fall back to "show the link" until the next verification tick — priming from last-known-good closes that window instantly; the boot verify then refreshes/corrects it.

func (*Service) Ready

func (s *Service) Ready(ctx context.Context) bool

Ready reports whether Send can deliver at all right now: SMTP is configured AND its last verification succeeded — exactly the two preconditions Send checks. The capabilities answer publishes it (as a boolean, no host) so the explorer does not offer a "Send by email" that is certain to fail: an administrator sees it greyed with where to set mail up, everybody else is not offered it (owner, 2026-09-21).

func (*Service) Send

func (s *Service) Send(ctx context.Context, to, subject, body string) error

Send delivers a plain-text UTF-8 email. Returns ErrNotVerified when the config hasn't passed verification (callers fall back to on-screen link).

func (*Service) Verified

func (s *Service) Verified() bool

Verified reports whether the last verification succeeded.

func (*Service) Verify

func (s *Service) Verify(ctx context.Context) error

Verify performs an SMTP handshake (+ auth when credentials are set) and records the result. Called on boot and every 5 minutes by server.Start.

Jump to

Keyboard shortcuts

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