// package snailmail sends email through a pluggable provider (SMTP or Cloudflare). // The active provider and its credentials are injected via Configure(Settings), // so the framework never reads application config. Branded message composition // (templates, logos, copy) stays app-side: apps build an Email and call SendMail. package snailmail import ( "bytes" "log" "strings" ) const ( TYPE_TEXT = iota TYPE_HTML ) // Email is a composed message ready to send. Body holds the already-rendered // text or HTML. type Email struct { Recipients []string Subject string Body *bytes.Buffer } // SMTPSettings holds credentials for the SMTP provider. type SMTPSettings struct { Server string Port string Username string Password string DisplayFrom string RequireAuth bool } // CloudflareSettings holds credentials for the Cloudflare email provider. type CloudflareSettings struct { AccountID string APIToken string FromAddress string } // Settings selects and configures the active email provider. Provider is "smtp" // (default) or "cloudflare". type Settings struct { Provider string SMTP SMTPSettings Cloudflare CloudflareSettings } type sender interface { Send(message Email, mailtype int) error } var ( settings Settings activeSender sender ) // Configure stores the provider credentials and selects the active provider. // Apps call this once at startup with values from their own config. func Configure(s Settings) { settings = s switch strings.ToLower(strings.TrimSpace(s.Provider)) { case "cloudflare": activeSender = &cloudflareSender{} log.Println("mailer: using Cloudflare email provider") default: activeSender = &smtpSender{} log.Println("mailer: using SMTP email provider") } } // SendMail sends message using the configured provider (defaulting to SMTP if // Configure was never called). func SendMail(message Email, mailtype int) error { if activeSender == nil { activeSender = &smtpSender{} } return activeSender.Send(message, mailtype) }