Skip to content

Shared email delivery ​

pkg/email defines a provider-independent Sender and plain-text Message:

go
type Message struct {
    To      string
    Subject string
    Body    string
}

type Sender interface {
    Send(context.Context, Message) error
}

The API creates one email.NewConsole(os.Stdout) instance and injects it into both identity and notification modules. Services own message content and call this shared boundary; they do not choose a transport or depend on a provider SDK.

The console adapter prints one JSON record per email, for example:

json
{"adapter":"console-email","to":"user@example.com","subject":"Verify your Prescriptoo email","body":"Token: <token>\nExpires in: 60 minutes\n..."}

Each record contains the complete recipient, subject, and body. Writes are serialized across goroutines; JSON encoding escapes newlines and terminal control characters. Cancellation is checked before writing, and output errors are returned to the caller. No SMTP connection or external delivery occurs.

To add a real provider, implement Sender, construct it in the API composition root (services/api/cmd/server/main.go), and inject it in place of the console adapter. No identity endpoint or notification template logic needs to change. Provider credentials belong in that adapter's configuration. Sender errors must not contain message bodies or credentials.

Identity currently sends synchronously and intentionally returns generic email request acknowledgements even on provider failure. Notifications retain their existing sent/failed status handling. This package does not provide an outbox, background worker, or automatic retries. Console emails include usable temporary authentication tokens, so the console adapter is for development.

Run go test -race ./pkg/email/... from the repository root to exercise concurrent output, cancellation, and error handling.

Documentation follows the source. Planned contracts are marked explicitly.