multipart for text fallback

alpha
alex 2024-01-21 23:09:20 +01:00
parent 4605629545
commit 0e632d00f7
10 changed files with 152 additions and 83 deletions

View File

@ -21,14 +21,14 @@ func Init() {
func NewMail(mail structs.Mail) error {
gocnjhelper.LogDebug("NEW MAIL")
body, err := mail.RenderTemplate()
htmlBody, textBody, err := mail.RenderTemplate()
if err != nil {
gocnjhelper.LogErrorf("Failed to render template, err: %s", err)
return err
}
if err = mail.Send(body); err != nil {
if err = mail.Send(htmlBody, textBody); err != nil {
gocnjhelper.LogErrorf("Failed to send mail, err: %s", err)
return err
}

View File

@ -31,7 +31,13 @@ func loadTemplateFiles() {
gocnjhelper.LogDebug("STARTING IMPORTING TEMPLATE FILES")
for templateName := range cache.Templates.Templates {
data, err := os.ReadFile(config.Cfg.Templates.FolderPath + templateName + ".html")
dataHtml, err := os.ReadFile(config.Cfg.Templates.FolderPath + templateName + ".html")
if err != nil {
gocnjhelper.LogFatalf("Failed to read file, err: %s", err)
}
dataTxt, err := os.ReadFile(config.Cfg.Templates.FolderPath + templateName + ".txt")
if err != nil {
gocnjhelper.LogFatalf("Failed to read file, err: %s", err)
@ -45,7 +51,11 @@ func loadTemplateFiles() {
// cache.BodyTemplates[templateName] = []byte(minifiedHtml)
cache.BodyTemplates[templateName] = []byte(data)
//cache.BodyTemplates[templateName] = []byte(data)
cache.BodyTemplates[templateName] = cache.BodyContentTemplate{
HTML: dataHtml,
PlainText: dataTxt,
}
}
gocnjhelper.LogDebug("FINISHED IMPORTING TEMPLATE FILES")

View File

@ -6,7 +6,12 @@ type templates struct {
Templates map[string]map[string]map[string]string
}
type BodyContentTemplate struct {
HTML []byte
PlainText []byte // fallback if html is not supported
}
var Templates templates
var BodyTemplates = make(map[string][]byte)
var BodyTemplates = make(map[string]BodyContentTemplate)
var SmtpAuth smtp.Auth

View File

@ -24,16 +24,30 @@ type Mail struct {
BodyData interface{}
}
func (m *Mail) Send(body string) error {
func (m *Mail) Send(htmlBody, textBody string) error {
cfg := config.Cfg.Mail
// Generate unique message ID
messageID := uuid.New().String() + "@" + strings.Split(cfg.FromEmail, "@")[1]
// Generate unique boundary string
boundary := "boundary" + uuid.New().String()
// Create MIME message with multipart/alternative content type
msg := "From: " + cfg.FromName + " <" + cfg.FromEmail + ">\n" +
"To: " + strings.Join(m.To, ",") + "\n" +
"Subject: " + m.Subject + "\n" +
"Date: " + time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700") + "\n" +
"Message-ID: <" + uuid.New().String() + "@" + strings.Split(cfg.FromEmail, "@")[1] + ">\n" +
"MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" +
body
"Message-ID: <" + messageID + ">\n" +
"MIME-version: 1.0;\n" +
"Content-Type: multipart/alternative; boundary=" + boundary + "\n\n" +
"--" + boundary + "\n" +
"Content-Type: text/plain; charset=\"UTF-8\"\n\n" +
textBody + "\n\n" +
"--" + boundary + "\n" +
"Content-Type: text/html; charset=\"UTF-8\"\n\n" +
htmlBody + "\n\n" +
"--" + boundary + "--"
err := smtp.SendMail(cfg.Host+":"+cfg.Port, cache.SmtpAuth, cfg.FromEmail, m.To, []byte(msg))
@ -46,6 +60,94 @@ func (m *Mail) Send(body string) error {
return nil
}
/*
func (m *Mail) Send(body string) error {
cfg := config.Cfg.Mail
msg := "From: " + cfg.FromName + " <" + cfg.FromEmail + ">\n" +
"To: " + strings.Join(m.To, ",") + "\n" +
"Subject: " + m.Subject + "\n" +
"Date: " + time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700") + "\n" +
"Message-ID: <" + uuid.New().String() + "@" + strings.Split(cfg.FromEmail, "@")[1] + ">\n" +
"MIME-version: 1.0;\nContent-Type: multipart/alternative; charset=\"UTF-8\";\n\n" +
body
err := smtp.SendMail(cfg.Host+":"+cfg.Port, cache.SmtpAuth, cfg.FromEmail, m.To, []byte(msg))
if err != nil {
gocnjhelper.LogErrorf("smtp error: %s", err)
return err
}
gocnjhelper.LogDebugf("SEND MAIL %s", msg)
return nil
} */
func (m *Mail) RenderTemplate() (htmlBody, textBody string, err error) {
body := cache.BodyTemplates[m.TemplateId]
for templateName, templateData := range cache.Templates.Templates {
// Skipping templates if the template ID is not the expected one
if templateName != m.TemplateId {
continue
}
gocnjhelper.LogDebugf("RENDER TEMPLATE %s %s", templateName, templateData)
m.Subject = templateData["mailSubject"][m.LanguageId]
// occurs if the requested language code does not exist
if m.Subject == "" {
m.Subject = templateData["mailSubject"][config.Cfg.DefaultLanguageCode]
}
// replace body %values% with values in templates config
for key, value := range templateData {
if key == "mailSubject" {
continue
}
v := value[m.LanguageId]
// occurs if the requested language code does not exist
if v == "" {
v = value[config.Cfg.DefaultLanguageCode]
}
// escaping so that umlauts are displayed correctly
if m.LanguageId == "de" {
v = escaper.EscapeHtmlEntites(v)
}
body.HTML = []byte(strings.Replace(string(body.HTML), "%"+key+"%", v, -1))
}
}
// The subject line of an email is an independent header and utf 8 must be set for it
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
if m.LanguageId == "de" {
m.Subject = EncodeBase64(m.Subject)
}
// Separate rendering for HTML and text bodies
htmlTemplate := template.Must(template.New("").Parse(string(body.HTML)))
textTemplate := template.Must(template.New("").Parse(string(body.PlainText)))
htmlBuf := new(bytes.Buffer)
textBuf := new(bytes.Buffer)
if err := htmlTemplate.Execute(htmlBuf, m.BodyData); err != nil {
return "", "", err
}
if err := textTemplate.Execute(textBuf, m.BodyData); err != nil {
return "", "", err
}
return htmlBuf.String(), textBuf.String(), nil
}
/*
func (m *Mail) RenderTemplate() (string, error) {
body := cache.BodyTemplates[m.TemplateId]
@ -82,19 +184,17 @@ func (m *Mail) RenderTemplate() (string, error) {
v = escaper.EscapeHtmlEntites(v)
}
body = []byte(strings.Replace(string(body), "%"+key+"%", v, -1))
body.HTML = []byte(strings.Replace(string(body.HTML), "%"+key+"%", v, -1))
}
}
// The subject line of an email is an independent header and utf 8 must be set for it
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
if m.LanguageId == "de" {
//m.Subject = "=?utf-8?B?" + base64.StdEncoding.EncodeToString([]byte(m.Subject)) + "?="
m.Subject = EncodeBase64(m.Subject)
}
t := template.Must(template.New("").Parse(string(body)))
t := template.Must(template.New("").Parse(string(body.HTML)))
buf := new(bytes.Buffer)
@ -103,7 +203,7 @@ func (m *Mail) RenderTemplate() (string, error) {
}
return buf.String(), nil
}
} */
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
func EncodeBase64(s string) string {

View File

@ -1,7 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<h1>%header%</h1>
<p>%informationText%</p>
</body>
</html>

View File

@ -1,9 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<h1>%header%</h1>
<p>%informationText%</p>
<p><b>{{.activation_code}}</b></p>
<p>%codeUse%</p>
</body>
</html>

View File

@ -0,0 +1,12 @@
%title%
%header%
%text1%
{{.verifyURL}}
%cancelText%
%yourAppointment%
%appointment1%
%appointment2%
%appointment3%
{{.address}}
%footer%
%dsgvo%

View File

@ -0,0 +1,11 @@
%title%
%header%
%text1%
%yourAppointment%
%appointment1%
%appointment2%
%appointment3%
{{.address}}
%dsgvo%

View File

@ -1,7 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<h1>%header%</h1>
<p>%informationText%</p>
</body>
</html>

View File

@ -1,23 +1,5 @@
{
"templates": {
"emailVerification": {
"mailSubject": {
"en": "Your email confirmation code",
"de": "Ihr E-Mail-Bestätigungscode"
},
"header": {
"en": "Good day,",
"de": "Guten Tag,"
},
"informationText": {
"en": "to confirm your identity we need to verify your email.",
"de": "um deine Identität zu bestätigen müssen wir deine E-Mail verifizieren."
},
"codeUse": {
"en": "Please enter this code in the app",
"de": "Bitte gebe diesen Code in der App ein"
}
},
"emailVerifyFailed": {
"mailSubject": {
"de": "Ihre Buchung wurde storniert"
@ -89,34 +71,6 @@
"dsgvo": {
"de": "Datenschutzerklärung"
}
},
"newUserSignIn": {
"mailSubject": {
"en": "A new sign-in was detected",
"de": "Neue Anmeldung wurde festgestellt"
},
"header": {
"en": "Good day,",
"de": "Guten Tag,"
},
"informationText": {
"en": "a new sign-in on <b>{{.device}}</b> was detected",
"de": "eine neue Anmeldung auf <b>{{.device}}</b> wurde festgestellt"
}
},
"SignUpSecondStep": {
"mailSubject": {
"en": "Welcome",
"de": "Willkommen"
},
"header": {
"en": "Good day {{.account_name}},",
"de": "Guten Tag {{.account_name}},"
},
"informationText": {
"en": "nice that you are here! Your account is created and you can start right away",
"de": "schön das du da bist! Dein Account ist erstellt und du kannst sofort loslegen"
}
}
}
}