2024-02-26 12:30:53 +00:00
|
|
|
package mailer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"embed"
|
|
|
|
"errors"
|
|
|
|
"io/fs"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
)
|
|
|
|
|
|
|
|
const EMAIL_FS_PREFIX = "templates"
|
|
|
|
|
2024-02-26 13:03:30 +00:00
|
|
|
const TPL_PASSWORD_RESET = "password_reset.tpl"
|
|
|
|
const TPL_VERIFY_EMAIL = "verify_email.tpl"
|
2024-02-26 12:30:53 +00:00
|
|
|
|
|
|
|
type EmailTemplate struct {
|
|
|
|
Subject *template.Template
|
|
|
|
Body *template.Template
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:embed templates/*
|
|
|
|
var templateFS embed.FS
|
|
|
|
|
|
|
|
var ErrTemplateNotFound = errors.New("template not found")
|
|
|
|
|
|
|
|
type TemplateRegistry struct {
|
|
|
|
templates map[string]EmailTemplate
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewTemplateRegistry() *TemplateRegistry {
|
|
|
|
return &TemplateRegistry{
|
|
|
|
templates: make(map[string]EmailTemplate),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tr *TemplateRegistry) loadTemplates() error {
|
|
|
|
subjectTemplates, err := fs.Glob(templateFS, EMAIL_FS_PREFIX+"*_subject*")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, subjectTemplate := range subjectTemplates {
|
|
|
|
templateName := strings.TrimSuffix(strings.TrimPrefix(subjectTemplate, EMAIL_FS_PREFIX), "_subject.tpl")
|
|
|
|
bodyTemplate := strings.TrimSuffix(subjectTemplate, "_subject.tpl") + "_body.tpl"
|
|
|
|
|
|
|
|
subjectContent, err := fs.ReadFile(templateFS, templateName+"_subject.tpl")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
subjectTmpl, err := template.New(templateName).Parse(string(subjectContent))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bodyContent, err := fs.ReadFile(templateFS, bodyTemplate)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bodyTmpl, err := template.New(templateName).Parse(string(bodyContent))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.templates[templateName] = EmailTemplate{
|
|
|
|
Subject: subjectTmpl,
|
|
|
|
Body: bodyTmpl,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tr *TemplateRegistry) RenderTemplate(templateName string, subjectVars TemplateData, bodyVars TemplateData) (*Email, error) {
|
|
|
|
tmpl, ok := tr.templates[templateName]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrTemplateNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
var subjectBuilder strings.Builder
|
|
|
|
err := tmpl.Subject.Execute(&subjectBuilder, subjectVars)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var bodyBuilder strings.Builder
|
|
|
|
err = tmpl.Body.Execute(&bodyBuilder, bodyVars)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewEmail(subjectBuilder.String(), bodyBuilder.String()), nil
|
|
|
|
}
|