readysite / pkg / application / emailers / resend.go
1.8 KB
resend.go
package emailers

import (
	"bytes"
	"embed"
	"encoding/json"
	"html/template"
	"net/http"

	"github.com/pkg/errors"
	"github.com/readysite/readysite/pkg/application"
)

// Resend sends emails via the Resend API
type Resend struct {
	application.BaseEmailer
	apiKey string
	from   string
}

// NewResend creates a new Resend emailer
//
// Example:
//
//	//go:embed all:emails
//	var emails embed.FS
//
//	emailer := emailers.NewResend(emails, apiKey, "noreply@example.com", nil)
func NewResend(emails embed.FS, apiKey, from string, funcs template.FuncMap) *Resend {
	r := &Resend{
		apiKey: apiKey,
		from:   from,
	}
	r.Init(emails, funcs)
	return r
}

// Send renders a template and sends it via Resend
func (r *Resend) Send(to, subject, templateName string, data map[string]any) error {
	body, err := r.Render(templateName, data)
	if err != nil {
		return errors.Wrap(err, "render email")
	}

	return r.send(to, subject, body)
}

// send sends an email via the Resend API
func (r *Resend) send(to, subject, html string) error {
	payload := map[string]any{
		"from":    r.from,
		"to":      []string{to},
		"subject": subject,
		"html":    html,
	}

	jsonBody, err := json.Marshal(payload)
	if err != nil {
		return errors.Wrap(err, "marshal payload")
	}

	req, err := http.NewRequest("POST", "https://api.resend.com/emails", bytes.NewReader(jsonBody))
	if err != nil {
		return errors.Wrap(err, "create request")
	}

	req.Header.Set("Authorization", "Bearer "+r.apiKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "send request")
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		var respBody bytes.Buffer
		respBody.ReadFrom(resp.Body)
		return errors.Errorf("resend API error: %s — %s", resp.Status, respBody.String())
	}

	return nil
}
← Back