Adds CRUD and preview APIs, RBAC permissions, seeded system templates, and migrates OTP email/SMS to template rendering. Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
4.4 KiB
Go
113 lines
4.4 KiB
Go
package emailtemplates
|
|
|
|
import (
|
|
"Yimaru-Backend/internal/domain"
|
|
"bytes"
|
|
)
|
|
|
|
var defaultTemplates = map[string]domain.EmailTemplate{
|
|
domain.EmailTemplateSlugOTP: {
|
|
Slug: domain.EmailTemplateSlugOTP,
|
|
Name: "One-Time Password",
|
|
Subject: "Yimaru - One Time Password",
|
|
BodyText: "Welcome to Yimaru Online Learning Platform{{if .FirstName}}, {{.FirstName}}{{end}}. Your OTP is {{.OTP}}. It expires in {{.ExpiresMinutes}} minutes. Please do not share it with anyone.",
|
|
BodyHTML: "<p>Welcome to Yimaru Online Learning Platform{{if .FirstName}}, <strong>{{.FirstName}}</strong>{{end}}.</p><p>Your one-time password is <strong>{{.OTP}}</strong>.</p><p>It expires in {{.ExpiresMinutes}} minutes. Please do not share it with anyone.</p>",
|
|
Variables: []string{"OTP", "FirstName", "ExpiresMinutes"},
|
|
Status: domain.EmailTemplateStatusActive,
|
|
},
|
|
domain.EmailTemplateSlugInvitation: {
|
|
Slug: domain.EmailTemplateSlugInvitation,
|
|
Name: "User Invitation",
|
|
Subject: "You are invited to join Yimaru",
|
|
BodyText: "Hi{{if .FirstName}} {{.FirstName}}{{end}}, you have been invited{{if .InviterName}} by {{.InviterName}}{{end}} to join Yimaru Online Learning Platform. Accept your invitation: {{.InviteLink}}",
|
|
BodyHTML: "<p>Hi{{if .FirstName}} <strong>{{.FirstName}}</strong>{{end}},</p><p>You have been invited{{if .InviterName}} by <strong>{{.InviterName}}</strong>{{end}} to join Yimaru Online Learning Platform.</p><p><a href=\"{{.InviteLink}}\">Accept your invitation</a></p>",
|
|
Variables: []string{"FirstName", "InviterName", "InviteLink"},
|
|
Status: domain.EmailTemplateStatusActive,
|
|
},
|
|
domain.EmailTemplateSlugPasswordReset: {
|
|
Slug: domain.EmailTemplateSlugPasswordReset,
|
|
Name: "Password Reset",
|
|
Subject: "Reset your Yimaru password",
|
|
BodyText: "Hi{{if .FirstName}} {{.FirstName}}{{end}}, use this link to reset your password: {{.ResetLink}}. The link expires in {{.ExpiresMinutes}} minutes.",
|
|
BodyHTML: "<p>Hi{{if .FirstName}} <strong>{{.FirstName}}</strong>{{end}},</p><p>Use the link below to reset your password. It expires in {{.ExpiresMinutes}} minutes.</p><p><a href=\"{{.ResetLink}}\">Reset your password</a></p>",
|
|
Variables: []string{"FirstName", "ResetLink", "ExpiresMinutes"},
|
|
Status: domain.EmailTemplateStatusActive,
|
|
},
|
|
domain.EmailTemplateSlugWelcome: {
|
|
Slug: domain.EmailTemplateSlugWelcome,
|
|
Name: "Welcome Email",
|
|
Subject: "Welcome to Yimaru",
|
|
BodyText: "Hi{{if .FirstName}} {{.FirstName}}{{end}}, welcome to Yimaru Online Learning Platform! Sign in at {{.LoginURL}} to get started.",
|
|
BodyHTML: "<p>Hi{{if .FirstName}} <strong>{{.FirstName}}</strong>{{end}},</p><p>Welcome to Yimaru Online Learning Platform!</p><p><a href=\"{{.LoginURL}}\">Sign in to get started</a></p>",
|
|
Variables: []string{"FirstName", "LoginURL"},
|
|
Status: domain.EmailTemplateStatusActive,
|
|
},
|
|
domain.EmailTemplateSlugCustomMessage: {
|
|
Slug: domain.EmailTemplateSlugCustomMessage,
|
|
Name: "Custom Message",
|
|
Subject: "{{.Subject}}",
|
|
BodyText: "{{.Message}}",
|
|
BodyHTML: "<p>{{.Message}}</p>",
|
|
Variables: []string{"Subject", "Message"},
|
|
Status: domain.EmailTemplateStatusActive,
|
|
},
|
|
}
|
|
|
|
func defaultTemplate(slug string) (domain.EmailTemplate, bool) {
|
|
tmpl, ok := defaultTemplates[slug]
|
|
return tmpl, ok
|
|
}
|
|
|
|
func renderTemplateFields(tmpl domain.EmailTemplate, data map[string]any) (domain.RenderedEmail, error) {
|
|
if data == nil {
|
|
data = map[string]any{}
|
|
}
|
|
|
|
subject, err := executeTextTemplate("subject:"+tmpl.Slug, tmpl.Subject, data)
|
|
if err != nil {
|
|
return domain.RenderedEmail{}, err
|
|
}
|
|
|
|
text, err := executeTextTemplate("text:"+tmpl.Slug, tmpl.BodyText, data)
|
|
if err != nil {
|
|
return domain.RenderedEmail{}, err
|
|
}
|
|
|
|
html, err := executeHTMLTemplate("html:"+tmpl.Slug, tmpl.BodyHTML, data)
|
|
if err != nil {
|
|
return domain.RenderedEmail{}, err
|
|
}
|
|
|
|
return domain.RenderedEmail{
|
|
Subject: subject,
|
|
Text: text,
|
|
HTML: html,
|
|
}, nil
|
|
}
|
|
|
|
func executeTextTemplate(name, content string, data map[string]any) (string, error) {
|
|
tmpl, err := newTextTemplate(name, content)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := tmpl.Execute(&buf, data); err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
func executeHTMLTemplate(name, content string, data map[string]any) (string, error) {
|
|
tmpl, err := newHTMLTemplate(name, content)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := tmpl.Execute(&buf, data); err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
}
|