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
- Variables
- func Reason(err error) string
- func WithLanguage(ctx context.Context, lang string) context.Context
- type Config
- type Service
- func (s *Service) Load(ctx context.Context) Config
- func (s *Service) PrimeFromStore(ctx context.Context)
- func (s *Service) Ready(ctx context.Context) bool
- func (s *Service) Send(ctx context.Context, to, subject, body string) error
- func (s *Service) Verified() bool
- func (s *Service) Verify(ctx context.Context) error
Constants ¶
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 ¶
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 ¶
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 ¶
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 (*Service) PrimeFromStore ¶
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 ¶
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 ¶
Send delivers a plain-text UTF-8 email. Returns ErrNotVerified when the config hasn't passed verification (callers fall back to on-screen link).