/* Copyright (C) 2019 Monomax Software Pty Ltd * * This file is part of Dnote. * * Dnote is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Dnote is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Dnote. If not, see . */ // Package mailer provides a functionality to send emails package mailer import ( "bytes" "fmt" "html/template" "os" "path" "github.com/aymerick/douceur/inliner" "github.com/pkg/errors" "gopkg.in/gomail.v2" ) // Email represents email to be sent out type Email struct { from string to []string subject string Body string } var ( // T is a map of templates T = map[string]*template.Template{} // EmailTypeWeeklyDigest represents a weekly digest email EmailTypeWeeklyDigest = "weekly_digest" // EmailTypeEmailVerification represents an email verification email EmailTypeEmailVerification = "email_verification" ) func getTemplatePath(templateDirPath, filename string) string { return path.Join(templateDirPath, fmt.Sprintf("%s.html", filename)) } // initTemplate returns a template instance by parsing the template with the // given name along with partials func initTemplate(templateDirPath, templateFileName string) (*template.Template, error) { templatePath := getTemplatePath(templateDirPath, templateFileName) headerPath := getTemplatePath(templateDirPath, "header") footerPath := getTemplatePath(templateDirPath, "footer") t, err := template.ParseFiles(templatePath, headerPath, footerPath) if err != nil { return nil, errors.Wrap(err, "parseing template") } return t, nil } // InitTemplates initializes templates func InitTemplates(templateDirPath string) { weeklyDigestTmpl, err := initTemplate(templateDirPath, EmailTypeWeeklyDigest) if err != nil { panic(errors.Wrap(err, "initializing template")) } emailVerificationTmpl, err := initTemplate(templateDirPath, EmailTypeEmailVerification) if err != nil { panic(errors.Wrap(err, "initializing template")) } T[EmailTypeWeeklyDigest] = weeklyDigestTmpl T[EmailTypeEmailVerification] = emailVerificationTmpl } // NewEmail returns a pointer to an Email struct with the given data func NewEmail(from string, to []string, subject string) *Email { return &Email{ from: from, to: to, subject: subject, } } // isWhitelisted checks if the email is safe to send in non production env func isWhitelisted(emails []string) bool { return false // return len(emails) == 1 && emails[0] == "mikeswcho@gmail.com" } // Send sends the email func (e *Email) Send() error { // If not production, never actually send an email if os.Getenv("GO_ENV") != "PRODUCTION" { fmt.Println("Not sending email because not production") fmt.Println(e.subject, e.to, e.from) // fmt.Println("Body", e.Body) return nil } smtpPort := 465 m := gomail.NewMessage() m.SetHeader("From", e.from) m.SetHeader("To", e.to...) m.SetHeader("Subject", e.subject) m.SetBody("text/html", e.Body) d := gomail.NewPlainDialer(os.Getenv("SmtpHost"), smtpPort, os.Getenv("SmtpUsername"), os.Getenv("SmtpPassword")) if err := d.DialAndSend(m); err != nil { return err } return nil } // ParseTemplate sets the email body by parsing the file at the given path, // evaluating all partials and inlining CSS rules func (e *Email) ParseTemplate(templateName string, data interface{}) error { t := T[templateName] if t == nil { return errors.Errorf("unsupported template '%s'", templateName) } buf := new(bytes.Buffer) if err := t.Execute(buf, data); err != nil { return err } html, err := inliner.Inline(buf.String()) if err != nil { return err } e.Body = html return nil }