feat(smtp): initial implementation — SMTP client with no-op fallback and template rendering (v1.0.0)
Introduces code.nochebuena.dev/einherjar/smtp — the email delivery starter for the Einherjar framework. New module with no micro-lib counterpart. Pure stdlib — no external dependencies beyond contracts and core. Interfaces (CT-6: one TypeSpec per file): - Sender — Send(ctx context.Context, msg Message) error - Component — lifecycle.Component + observability.Checkable + Sender - Message — To, CC, BCC, ReplyTo, Subject, Body, ContentType, Attachments - Attachment — Name, ContentType string; Data io.Reader (consumed once on Send) - Template — wraps html/template for email rendering Implementation: - New(logger, cfg) Component — returns noopClient when cfg.Host is empty; SMTP absence must never block a transaction (e.g. account creation) - noopClient: all lifecycle and Send methods return nil; Send logs Warn; Priority LevelDegraded; Name "smtp" - smtpClient: OnInit/OnStart/OnStop are no-ops (stateless stdlib client); HealthCheck: TCP dial to Host:Port; Priority LevelDegraded - Send: buildRawMessage → netsmtp.SendMail; BCC passed in SMTP envelope (RCPT TO) but never written to message headers (RFC 5321/5322 compliance) - buildRawMessage: plain message without attachments; multipart/mixed with quoted-printable body + base64 attachments when Attachments non-empty; multipart.NewWriter created first to obtain boundary before headers are written - ParseFS(fsys, patterns...) (*Template, error) — wraps html/template.ParseFS - Template.Render(name, data) (string, error) — executes named template - net/smtp aliased as netsmtp to avoid package-name collision in package smtp Config (EINHERJAR_SMTP_* env vars): Host, Port(587), User, Password, From - Component interface embeds observability.Identifiable; identifiable.go implements ModulePath and ModuleVersion on both smtpClient and noopClient via runtime/debug.ReadBuildInfo() — prints in launcher banner
This commit is contained in:
52
doc.go
Normal file
52
doc.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Package smtp provides a lifecycle-managed SMTP client for sending email
|
||||
// in Einherjar applications.
|
||||
//
|
||||
// # Overview
|
||||
//
|
||||
// [New] returns a [Component] that satisfies [lifecycle.Component] lifecycle
|
||||
// hooks, [observability.Checkable] with degraded priority, and the [Sender]
|
||||
// interface for dispatching email. When [Config.Host] is empty, [New] returns
|
||||
// a no-op implementation that logs a warning and silently discards every message —
|
||||
// email failure must never block a transaction.
|
||||
//
|
||||
// # Lifecycle Registration
|
||||
//
|
||||
// client := smtp.New(logger, cfg)
|
||||
// launcher.Register(client)
|
||||
// health.Register(client)
|
||||
//
|
||||
// # Sending
|
||||
//
|
||||
// Services depend only on [Sender]. Build a [Message] with a pre-rendered body,
|
||||
// then call Send:
|
||||
//
|
||||
// msg := smtp.Message{
|
||||
// To: []string{"alice@example.com"},
|
||||
// Subject: "Your receipt",
|
||||
// Body: rendered, // pre-rendered string from Template.Render
|
||||
// ContentType: "text/html",
|
||||
// }
|
||||
// if err := mailer.Send(ctx, msg); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// # Template Rendering
|
||||
//
|
||||
// [ParseFS] wraps stdlib html/template for HTML email rendering. Rendering is
|
||||
// intentionally separate from Send — callers render to a string and assign it
|
||||
// to Message.Body. This keeps Send unit-testable without template involvement.
|
||||
//
|
||||
// tmpl, err := smtp.ParseFS(os.DirFS("templates"), "*.html")
|
||||
// body, err := tmpl.Render("receipt.html", data)
|
||||
// msg := smtp.Message{Body: body, ContentType: "text/html", ...}
|
||||
//
|
||||
// # Configuration
|
||||
//
|
||||
// All fields are read from environment variables with the EINHERJAR_SMTP_* prefix:
|
||||
//
|
||||
// - EINHERJAR_SMTP_HOST — SMTP server hostname (empty = no-op mode)
|
||||
// - EINHERJAR_SMTP_PORT — default: 587 (STARTTLS)
|
||||
// - EINHERJAR_SMTP_USER — auth username (optional)
|
||||
// - EINHERJAR_SMTP_PASSWORD — auth password (optional)
|
||||
// - EINHERJAR_SMTP_FROM — envelope sender address
|
||||
package smtp
|
||||
Reference in New Issue
Block a user