multipart for text fallback
parent
4605629545
commit
0e632d00f7
|
@ -21,14 +21,14 @@ func Init() {
|
||||||
func NewMail(mail structs.Mail) error {
|
func NewMail(mail structs.Mail) error {
|
||||||
gocnjhelper.LogDebug("NEW MAIL")
|
gocnjhelper.LogDebug("NEW MAIL")
|
||||||
|
|
||||||
body, err := mail.RenderTemplate()
|
htmlBody, textBody, err := mail.RenderTemplate()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gocnjhelper.LogErrorf("Failed to render template, err: %s", err)
|
gocnjhelper.LogErrorf("Failed to render template, err: %s", err)
|
||||||
return 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)
|
gocnjhelper.LogErrorf("Failed to send mail, err: %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,13 @@ func loadTemplateFiles() {
|
||||||
gocnjhelper.LogDebug("STARTING IMPORTING TEMPLATE FILES")
|
gocnjhelper.LogDebug("STARTING IMPORTING TEMPLATE FILES")
|
||||||
|
|
||||||
for templateName := range cache.Templates.Templates {
|
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 {
|
if err != nil {
|
||||||
gocnjhelper.LogFatalf("Failed to read file, err: %s", err)
|
gocnjhelper.LogFatalf("Failed to read file, err: %s", err)
|
||||||
|
@ -45,7 +51,11 @@ func loadTemplateFiles() {
|
||||||
|
|
||||||
// cache.BodyTemplates[templateName] = []byte(minifiedHtml)
|
// 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")
|
gocnjhelper.LogDebug("FINISHED IMPORTING TEMPLATE FILES")
|
||||||
|
|
|
@ -6,7 +6,12 @@ type templates struct {
|
||||||
Templates map[string]map[string]map[string]string
|
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 Templates templates
|
||||||
var BodyTemplates = make(map[string][]byte)
|
var BodyTemplates = make(map[string]BodyContentTemplate)
|
||||||
|
|
||||||
var SmtpAuth smtp.Auth
|
var SmtpAuth smtp.Auth
|
||||||
|
|
|
@ -24,16 +24,30 @@ type Mail struct {
|
||||||
BodyData interface{}
|
BodyData interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mail) Send(body string) error {
|
func (m *Mail) Send(htmlBody, textBody string) error {
|
||||||
cfg := config.Cfg.Mail
|
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" +
|
msg := "From: " + cfg.FromName + " <" + cfg.FromEmail + ">\n" +
|
||||||
"To: " + strings.Join(m.To, ",") + "\n" +
|
"To: " + strings.Join(m.To, ",") + "\n" +
|
||||||
"Subject: " + m.Subject + "\n" +
|
"Subject: " + m.Subject + "\n" +
|
||||||
"Date: " + time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700") + "\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" +
|
"Message-ID: <" + messageID + ">\n" +
|
||||||
"MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" +
|
"MIME-version: 1.0;\n" +
|
||||||
body
|
"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))
|
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
|
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) {
|
func (m *Mail) RenderTemplate() (string, error) {
|
||||||
body := cache.BodyTemplates[m.TemplateId]
|
body := cache.BodyTemplates[m.TemplateId]
|
||||||
|
|
||||||
|
@ -82,19 +184,17 @@ func (m *Mail) RenderTemplate() (string, error) {
|
||||||
v = escaper.EscapeHtmlEntites(v)
|
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
|
// 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/
|
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
|
||||||
if m.LanguageId == "de" {
|
if m.LanguageId == "de" {
|
||||||
//m.Subject = "=?utf-8?B?" + base64.StdEncoding.EncodeToString([]byte(m.Subject)) + "?="
|
|
||||||
|
|
||||||
m.Subject = EncodeBase64(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)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
@ -103,7 +203,7 @@ func (m *Mail) RenderTemplate() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
} */
|
||||||
|
|
||||||
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
|
// https://ncona.com/2011/06/using-utf-8-characters-on-an-e-mail-subject/
|
||||||
func EncodeBase64(s string) string {
|
func EncodeBase64(s string) string {
|
||||||
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
%title%
|
||||||
|
%header%
|
||||||
|
%text1%
|
||||||
|
{{.verifyURL}}
|
||||||
|
%cancelText%
|
||||||
|
%yourAppointment%
|
||||||
|
%appointment1%
|
||||||
|
%appointment2%
|
||||||
|
%appointment3%
|
||||||
|
{{.address}}
|
||||||
|
%footer%
|
||||||
|
%dsgvo%
|
|
@ -0,0 +1,11 @@
|
||||||
|
%title%
|
||||||
|
%header%
|
||||||
|
%text1%
|
||||||
|
|
||||||
|
%yourAppointment%
|
||||||
|
%appointment1%
|
||||||
|
%appointment2%
|
||||||
|
%appointment3%
|
||||||
|
{{.address}}
|
||||||
|
|
||||||
|
%dsgvo%
|
|
@ -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>
|
|
|
@ -1,23 +1,5 @@
|
||||||
{
|
{
|
||||||
"templates": {
|
"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": {
|
"emailVerifyFailed": {
|
||||||
"mailSubject": {
|
"mailSubject": {
|
||||||
"de": "Ihre Buchung wurde storniert"
|
"de": "Ihre Buchung wurde storniert"
|
||||||
|
@ -89,34 +71,6 @@
|
||||||
"dsgvo": {
|
"dsgvo": {
|
||||||
"de": "Datenschutzerklärung"
|
"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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue