mailpen

package module
v0.0.0-...-f393a87 Latest Latest
Warning

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

Go to latest
Published: Jan 6, 2025 License: Apache-2.0 Imports: 14 Imported by: 0

README

Mailpen Package Documentation - Experimental

⚠️ EXPERIMENTAL: This project is under active development and the API changes frequently. Not recommended for production use unless you're willing to vendor the code.

Overview

Mailpen is a flexible email composition and sending system for Go applications. It provides a modular architecture for sending emails with support for templates, layouts, components, and multiple email providers.

Core Concepts

Message Structure

Messages in Mailpen consist of:

  • Recipients (To, CC, BCC)
  • Subject
  • Content (HTML and/or Text)
  • Optional attachments
  • Optional template data
Templates Organization

Templates are organized into four main directories:

  • layouts/ - Base email layouts
  • components/ - Reusable email components
  • partials/ - Shared template fragments
  • emails/ - Individual email templates
Template Sources

Multiple template sources can be configured, allowing for template overrides and customization. Sources are processed in order, with later sources taking precedence.

Basic Usage

// Create a configuration
config := &mailpen.Config{
    From: "sender@example.com",
    CompanyName: "ACME Corp",
    Sources: []mailpen.TemplateSource{
        {
            Name: "base",
            FS: os.DirFS("templates/base"),
        },
    },
}

// Initialize a provider (e.g., SMTP)
provider := smtp.New(&smtp.Config{
    Host: "smtp.example.com",
    Port: 587,
})

// Create Mailpen instance
mp, err := mailpen.New(provider, config)
if err != nil {
    log.Fatal(err)
}

// Send an email using a template
msg := mailpen.NewMessage().
    To("recipient@example.com").
    Template("welcome").
    WithData(map[string]any{
        "Name": "John Doe",
    }).
    Must()

err = mp.Send(context.Background(), msg)

Adding New Components

1. Create Component Template

Components should be added to the components/ directory. Each component should have both HTML and text versions:

components/
  ├── alert/
  │   ├── alert.html
  │   └── alert.txt
  └── button/
      ├── button.html
      └── button.txt
2. Define Component Structure

Create a struct in components.go to define the component's data structure:

type AlertData struct {
    Title    string
    Message  string
    Type     string
    ButtonURL string
}
3. Implement Component Templates

HTML template example (components/alert/alert.html):

{{define "component:alert"}}
<div class="alert alert-{{.Type}}" style="
    border-left: 4px solid {{theme "colors.warning"}};
    padding: {{theme "components.notification.padding"}};
">
    <h4>{{.Title}}</h4>
    <p>{{.Message}}</p>
    {{if .ButtonURL}}
    <a href="{{.ButtonURL}}" class="btn">Fix Now</a>
    {{end}}
</div>
{{end}}

Text template example (components/alert/alert.txt):

{{define "component:alert"}}
! {{.Title}} !
{{.Message}}
{{if .ButtonURL}}
Fix Now: {{.ButtonURL}}
{{end}}
{{end}}

Adding New Layouts

1. Create Layout Files

Layouts should be added to the layouts/ directory with both HTML and text versions:

layouts/
  ├── base/
  │   ├── base.html
  │   └── base.txt
  └── marketing/
      ├── marketing.html
      └── marketing.txt
2. Implement Layout Structure

HTML layout example (layouts/marketing/marketing.html):

{{define "layout:marketing"}}
<!DOCTYPE html>
<html>
<head>
    <title>{{.Subject}}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body {
            font-family: {{theme "typography.font.family"}};
            line-height: {{theme "typography.font.lineHeight.normal"}};
            color: {{theme "colors.text.primary"}};
        }
    </style>
</head>
<body>
    <div class="container" style="max-width: {{theme "layout.maxWidth"}}">
        {{template "content" .}}
        {{template "component:footer" .FooterData}}
    </div>
</body>
</html>
{{end}}

Text layout example (layouts/marketing/marketing.txt):

{{define "layout:marketing"}}
{{template "content" .}}

---
{{template "component:footer" .FooterData}}
{{end}}

Theming

Mailpen includes a theming system that can be customized through the configuration. Theme values can be accessed in templates using the theme function:

<div style="color: {{theme "colors.primary"}}">
    Themed content
</div>

Default theme values include:

  • Colors (primary, secondary, success, danger, warning)
  • Typography (font families, sizes, weights)
  • Spacing
  • Border styles
  • Component-specific styles

Best Practices

  1. Template Organization

    • Keep templates modular and reusable
    • Use components for repeated elements
    • Maintain consistent naming conventions
  2. Theme Usage

    • Use theme values for consistent styling
    • Avoid hardcoding colors or dimensions
    • Define new theme values for custom components
  3. Testing

    • Test both HTML and text renderings
    • Verify template data handling
    • Check responsive layouts
  4. Error Handling

    • Always check template parsing errors
    • Validate required fields
    • Handle attachment errors properly

Common Issues

  1. Template Not Found

    • Verify template path matches the name used in code
    • Check template source order for overrides
    • Ensure both HTML and text versions exist
  2. Style Inconsistencies

    • Use theme values consistently
    • Test email rendering in multiple clients
    • Follow email HTML best practices
  3. Missing Data

    • Validate required template data
    • Provide default values where appropriate
    • Use template functions for data formatting

Advanced Features

Custom Template Functions

Add custom template functions using AddFunc or AddFuncs:

manager.AddFunc("formatDate", func(t time.Time) string {
    return t.Format("2006-01-02")
})
Multiple Template Sources

Configure multiple sources for template overrides:

config := &mailpen.Config{
    Sources: []mailpen.TemplateSource{
        {Name: "base", FS: baseFS},
        {Name: "custom", FS: customFS},
    },
}
HTML Processing

Implement custom HTML processing:

type CustomProcessor struct{}

func (p *CustomProcessor) Process(html string) (string, error) {
    // Process HTML
    return html, nil
}

config.HTMLProcessor = &CustomProcessor{}

Documentation

Overview

Package mailpen provides a flexible email composition and sending system

Index

Constants

View Source
const (
	LayoutsDir    = "layouts"
	PartialsDir   = "partials"
	ComponentsDir = "components"
	EmailsDir     = "emails"
)

Variables

View Source
var (
	ErrNoContent = errors.New("email must have either plain text or HTML body")
	ErrNoSubject = errors.New("email must have a subject")
)

Functions

func DefaultFuncMap

func DefaultFuncMap() template.FuncMap

DefaultFuncMap returns the complete function map for the templates package.

func DefaultTheme

func DefaultTheme() map[string]any

DefaultTheme returns a theme map that works with built-in templates

func GetThemeValue

func GetThemeValue(theme map[string]any, path string) any

GetThemeValue safely traverses a theme map using dot notation

func MergeFuncMaps

func MergeFuncMaps(maps ...template.FuncMap) template.FuncMap

MergeFuncMaps merges the provided function maps into a single function map.

func OpenFileAttachment

func OpenFileAttachment(filepath string) (string, io.Reader, func() error, error)

OpenFileAttachment is a helper that returns a file reader and a cleanup function for an attachment file. The filename is extracted from the filepath. It returns the filename, a reader for the file, a cleanup function, and an error if the file cannot be opened. It is the caller's responsibility to close the file reader after sending the email using the cleanup function.

Example:

filename, reader, cleanup, err := OpenFileAttachment("path/to/file.txt")

if err != nil {
    return err
}

defer cleanup()

msg, err := NewMessage().To("foo@example.com").Template("templates.tmpl").Attach(filename, reader).Build()

Types

type Attachment

type Attachment struct {
	Filename    string
	Data        io.Reader
	ContentType ContentType
}

Attachment represents an email attachment

type Builder

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

Builder provides a fluent interface for constructing emails

func NewMessage

func NewMessage() *Builder

NewMessage creates an email builder

func (*Builder) Attach

func (b *Builder) Attach(filename string, data io.Reader) *Builder

Attach adds an attachment to the email. The data is read from the provided reader and the content type is inferred from the filename.

func (*Builder) AttachWithContentType

func (b *Builder) AttachWithContentType(filename string, data io.Reader, contentType ContentType) *Builder

AttachWithContentType adds an attachment to the email with a specific content type. The data is read from the provided reader.

func (*Builder) Bcc

func (b *Builder) Bcc(addresses ...string) *Builder

func (*Builder) Build

func (b *Builder) Build() (*Message, error)

func (*Builder) Cc

func (b *Builder) Cc(addresses ...string) *Builder

func (*Builder) From

func (b *Builder) From(address string) *Builder

func (*Builder) Layout

func (b *Builder) Layout(name string) *Builder

func (*Builder) Must

func (b *Builder) Must() *Message

func (*Builder) ReplyTo

func (b *Builder) ReplyTo(address string) *Builder

func (*Builder) Subject

func (b *Builder) Subject(subject string) *Builder

func (*Builder) Template

func (b *Builder) Template(name string) *Builder

func (*Builder) To

func (b *Builder) To(addresses ...string) *Builder

func (*Builder) WithData

func (b *Builder) WithData(data map[string]any) *Builder

type Capabilities

type Capabilities struct {
	MaxRecipients      int
	MaxAttachmentSize  int64
	SupportsTemplates  bool
	SupportsHTMLOnly   bool
	SupportsScheduling bool
}

Capabilities defines what features a provider supports

type Card

type Card struct {
	ImageURL    string
	ImageAlt    string
	Title       string
	Description string
	LinkURL     string
	LinkText    string
}

Card represents a card in a card grid

type CardGridData

type CardGridData struct {
	Cards []Card
}

CardGridData represents the data needed to render a card grid

type Config

type Config struct {
	// From address
	From    string // From address
	ReplyTo string // Reply-to address

	// Company/Branding
	BaseURL         string // Base URL of the website
	CompanyAddress1 string // The first line of the company address (usually the street address)
	CompanyAddress2 string // The second line of the company address (usually the city, state, and ZIP code)
	CompanyName     string // Company name
	LogoURL         string // URL to the company logo
	SupportEmail    string // Support email address
	SupportPhone    string // Support phone number
	WebsiteName     string // Name of the website
	WebsiteURL      string // URL to the company website.

	// HTML processor for processing HTML content
	HTMLProcessor HTMLProcessor // HTML processor for processing HTML content

	// Links
	SiteLinks        map[string]string // Site links
	SocialMediaLinks map[string]string // Social media links

	// Template configuration
	FuncMap       template.FuncMap // Additional template functions to add to the template engine. These will be merged with the default functions.
	Sources       []TemplateSource // Template sources
	Theme         map[string]any   // Theme configuration
	DefaultLayout string           // Default layout to use for emails (defaults to "base")
}

Config holds the mailpen configuration

type ContentType

type ContentType string

ContentType is a type wrapper for a string and represents the MIME type of the content being handled.

const (
	// TypeAppOctetStream represents the MIME type for arbitrary binary data.
	TypeAppOctetStream ContentType = "application/octet-stream"

	// TypeMultipartAlternative represents the MIME type for a message body that can contain multiple alternative
	// formats.
	TypeMultipartAlternative ContentType = "multipart/alternative"

	// TypeMultipartMixed represents the MIME type for a multipart message containing different parts.
	TypeMultipartMixed ContentType = "multipart/mixed"

	// TypeMultipartRelated represents the MIME type for a multipart message where each part is a related file
	// or resource.
	TypeMultipartRelated ContentType = "multipart/related"

	// TypePGPSignature represents the MIME type for PGP signed messages.
	TypePGPSignature ContentType = "application/pgp-signature"

	// TypePGPEncrypted represents the MIME type for PGP encrypted messages.
	TypePGPEncrypted ContentType = "application/pgp-encrypted"

	// TypeTextHTML represents the MIME type for HTML text content.
	TypeTextHTML ContentType = "text/html"

	// TypeTextPlain represents the MIME type for plain text content.
	TypeTextPlain ContentType = "text/plain"
)

func (ContentType) String

func (c ContentType) String() string

String returns the string representation of the ContentType and implements the Stringer interface.

type DefaultProcessor

type DefaultProcessor struct{}

DefaultProcessor provides a pass-through implementation

func (*DefaultProcessor) Process

func (p *DefaultProcessor) Process(html string) (string, error)

Process provides a pass-through implementation for HTMLProcessor

type FooterData

type FooterData struct {
	CompanyName   string
	SupportEmail  string
	CopyrightText string // e.g., "© 2024 Crystal Springs Foundation. All rights reserved."
	AddressLine1  string // e.g., "1234 Business Street, Suite 500"
	AddressLine2  string // e.g., "San Francisco, CA 94111"
}

FooterData represents the data needed to render a footer

type HTMLProcessor

type HTMLProcessor interface {
	Process(html string) (string, error)
}

HTMLProcessor defines the interface for processing HTML content

type Mailpen

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

Mailpen handles email sending operations

func New

func New(provider Provider, config *Config, opts ...Option) (*Mailpen, error)

New creates a new Mailpen instance using the provided configuration and the default SMTP client

func (*Mailpen) Config

func (m *Mailpen) Config() *Config

Config returns the mailpen configuration

func (*Mailpen) NewTemplateData

func (m *Mailpen) NewTemplateData() TemplateData

NewTemplateData creates a new templates data map with default values

func (*Mailpen) Send

func (m *Mailpen) Send(ctx context.Context, msg *Message) error

Send sends an email using the provided templates and data

type Manager

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

Manager handles templates loading, caching, and rendering

func NewManager

func NewManager(config *ManagerConfig) (*Manager, error)

NewManager creates a new templates manager

func (*Manager) AddFunc

func (m *Manager) AddFunc(name string, fn interface{}) error

AddFunc adds a function to the templates manager

func (*Manager) AddFuncs

func (m *Manager) AddFuncs(funcs template.FuncMap) error

AddFuncs adds multiple functions to the templates manager

func (*Manager) AddSource

func (m *Manager) AddSource(source TemplateSource) error

AddSource adds a new template source to the manager

func (*Manager) ClearCache

func (m *Manager) ClearCache()

ClearCache clears the email template cache

func (*Manager) RenderEmail

func (m *Manager) RenderEmail(name string, data interface{}, layout string) (*RenderedEmail, error)

RenderEmail renders an email template with optional layout

type ManagerConfig

type ManagerConfig struct {
	FuncMap       template.FuncMap
	Processor     HTMLProcessor
	Sources       []TemplateSource
	Theme         map[string]any
	DefaultLayout string
}

ManagerConfig configures the templates manager

type Message

type Message struct {
	From        string         // Sender email address
	To          []string       // List of recipient email addresses
	Cc          []string       // List of CC email addresses
	Bcc         []string       // List of BCC email addresses
	ReplyTo     string         // Reply-to email address
	Subject     string         // Email subject
	Data        map[string]any // Data to be passed to the templates
	Layout      string         // Layout name to process
	Template    string         // Template name to process
	TextBody    string         // Text body of the email
	HTMLBody    string         // HTML body of the email
	Attachments []Attachment   // List of attachments
}

Message represents the content and recipients of an email message

type Module

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

func NewModule

func NewModule(provider Provider, config *Config) *Module

func (*Module) ID

func (m *Module) ID() string

func (*Module) Init

func (m *Module) Init() error

func (*Module) Mailpen

func (m *Module) Mailpen() *Mailpen

func (*Module) Start

func (m *Module) Start(_ context.Context) error

func (*Module) Stop

func (m *Module) Stop(_ context.Context) error

type NotificationBoxData

type NotificationBoxData struct {
	BgColor     string // e.g., "#FFF3CD" for warning
	BorderColor string // e.g., "#FFA500" for warning
	Icon        string // Optional icon URL
	IconAlt     string
	Title       string
	TitleColor  string
	Message     string
	TextColor   string
	Button      *NotificationButton
}

NotificationBoxData represents the data needed to render a notification box

type NotificationButton

type NotificationButton struct {
	BgColor     string
	BorderColor string
	TextColor   string
	Text        string
	URL         string
}

NotificationButton represents the type of button to render in a notification box

type Option

type Option func(mailpen *Mailpen) error

Option is a functional option for configuring a Mailpen instance

type Provider

type Provider interface {
	// Send sends an email message
	Send(ctx context.Context, msg *Message) error

	// Name returns the provider name
	Name() string

	// Validate validates a message before sending
	Validate(msg *Message) error

	// Capabilities returns the provider capabilities
	Capabilities() Capabilities
}

Provider defines the interface for email providers

type RenderedEmail

type RenderedEmail struct {
	Text string
	HTML string
}

RenderedEmail represents a rendered email

type SMTPClient

type SMTPClient interface {
	DialAndSend(messages ...*gomail.Msg) error
}

SMTPClient defines the interface for an SMTP client, mainly used for testing

type StringList

type StringList = []string

StringList is an alias for a slice of strings

type TableCell

type TableCell struct {
	Text  string
	Width string
}

TableCell represents a cell in a table

type TableData

type TableData struct {
	Headers []TableHeader
	Rows    []TableRow
}

TableData represents the data needed to render a table

type TableHeader

type TableHeader struct {
	Text  string
	Width string
}

TableHeader represents a header in a table

type TableRow

type TableRow struct {
	Cells []TableCell
}

TableRow represents a row in a table

type TemplateData

type TemplateData map[string]any

func NewTemplateData

func NewTemplateData(cfg *Config) TemplateData

func (TemplateData) Merge

func (td TemplateData) Merge(data map[string]any) TemplateData

Merge combines the current TemplateData with the provided data map.

func (TemplateData) MergeKeys

func (td TemplateData) MergeKeys(data map[string]any) TemplateData

MergeKeys merges data into the templates data, combining maps for existing keys instead of overwriting the entire map. This allows for more granular updates. It can merge maps for existing keys as well as add new keys.

type TemplateFormat

type TemplateFormat string

TemplateFormat represents the format of a template

const (
	FormatText TemplateFormat = "text"
	FormatHTML TemplateFormat = "html"
)

func (TemplateFormat) Extension

func (f TemplateFormat) Extension() string

Extension returns the file extension for a template format

type TemplateSource

type TemplateSource struct {
	Name string // Name of the template source
	FS   fs.FS  // File system for the templates
}

TemplateSource represents a source of templates

type TwoColumnData

type TwoColumnData struct {
	Rows []TwoColumnRow
}

TwoColumnData represents the data needed to render a two-column layout

type TwoColumnRow

type TwoColumnRow struct {
	Label string
	Value string
}

TwoColumnRow represents a row in a two-column layout

Directories

Path Synopsis
internal
providers

Jump to

Keyboard shortcuts

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