readysite / pkg / application / emailer.go
1.6 KB
emailer.go
package application

import (
	"bytes"
	"embed"
	"errors"
	"html/template"
	"io/fs"
	"log"
	"path/filepath"
	"strings"

	pkgerrors "github.com/pkg/errors"
)

// Emailer is the interface for sending templated emails
type Emailer interface {
	Send(to, subject, templateName string, data map[string]any) error
}

// BaseEmailer provides common email template rendering functionality.
// Embed this in your emailer implementations.
type BaseEmailer struct {
	emails *template.Template
}

// Init initializes the base emailer with templates.
// Call this in your emailer's constructor.
func (b *BaseEmailer) Init(emails embed.FS, funcs template.FuncMap) {
	b.emails = template.New("")
	if funcs != nil {
		b.emails = b.emails.Funcs(funcs)
	}

	err := fs.WalkDir(emails, "emails", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() || !strings.HasSuffix(path, ".html") {
			return nil
		}

		content, err := emails.ReadFile(path)
		if err != nil {
			return err
		}

		name := filepath.Base(path)
		_, err = b.emails.New(name).Parse(string(content))
		return err
	})

	if err != nil {
		log.Printf("Warning: failed to parse email templates: %v", err)
		b.emails = template.New("")
	}
}

// Render renders an email template to a string
func (b *BaseEmailer) Render(name string, data map[string]any) (string, error) {
	if b.emails == nil {
		return "", errors.New("email templates not initialized")
	}

	var buf bytes.Buffer
	if err := b.emails.ExecuteTemplate(&buf, name, data); err != nil {
		return "", pkgerrors.Wrapf(err, "execute template %s", name)
	}

	return buf.String(), nil
}
← Back