diff --git a/bookings b/bookings new file mode 100755 index 0000000..90706e4 Binary files /dev/null and b/bookings differ diff --git a/cmd/web/main.go b/cmd/web/main.go index 57f3ce5..2bb20eb 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -32,6 +32,9 @@ func main() { } defer db.SQL.Close() + defer close(app.MailChan) + listenForMail() + fmt.Println(fmt.Sprintf("Starting application on port %s", portNumber)) // _ = http.ListenAndServe(portNumber, nil) @@ -50,6 +53,9 @@ func run() (*driver.DB, error) { gob.Register(models.Room{}) gob.Register(models.Restriction{}) + mailChan := make(chan models.MailData) + app.MailChan = mailChan + // change this to true when in production app.InProduction = false diff --git a/cmd/web/send-mail.go b/cmd/web/send-mail.go new file mode 100644 index 0000000..43a87c2 --- /dev/null +++ b/cmd/web/send-mail.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "strings" + "time" + + "github.com/abneed/bookings/internal/models" + mail "github.com/xhit/go-simple-mail/v2" +) + +func listenForMail() { + go func() { + for { + msg := <-app.MailChan + sendMsg(msg) + } + }() +} + +func sendMsg(m models.MailData) { + server := mail.NewSMTPClient() + server.Host = "localhost" + server.Port = 1025 + server.KeepAlive = false + server.ConnectTimeout = 10 * time.Second + server.SendTimeout = 10 * time.Second + + client, err := server.Connect() + if err != nil { + errorLog.Println(err) + } + email := mail.NewMSG() + email.SetFrom(m.From).AddTo(m.To).SetSubject(m.Subject) + if m.Template == "" { + email.SetBody(mail.TextHTML, m.Content) + } else { + data, err := ioutil.ReadFile(fmt.Sprintf("./email-templates/%s", m.Template)) + if err != nil { + app.ErrorLog.Println(err) + } + + mailTemplate := string(data) + msgToSend := strings.Replace(mailTemplate, "[%body%]", m.Content, 1) + email.SetBody(mail.TextHTML, msgToSend) + } + + err = email.Send(client) + if err != nil { + log.Println(err) + } else { + log.Println("Email sent!") + } +} diff --git a/email-templates/basic.html b/email-templates/basic.html new file mode 100644 index 0000000..e9e55ba --- /dev/null +++ b/email-templates/basic.html @@ -0,0 +1,1884 @@ + + + + + + + Title + + + + + + + + + + + +
+
+ + + + + + +
 
+ + + + + + +
+ + + + + + +
+ + + + + +
+
+
+ + + + + + +
+ + + + + + +
 
+
+ + + + + + +
 
+ + + + + + +
+ + + + + +
+

Fort Smythe

+
+
+
+ + + + + + +
+ + + + + +
+

+ [%body%] +

+
+
+ + + + + + + +
+
+
+ + + \ No newline at end of file diff --git a/go.mod b/go.mod index 8a7c4d7..c8eeb88 100644 --- a/go.mod +++ b/go.mod @@ -67,6 +67,8 @@ require ( github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect github.com/spf13/cobra v1.4.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect + github.com/xhit/go-simple-mail/v2 v2.11.0 // indirect golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect diff --git a/go.sum b/go.sum index ea3f655..d5df3f8 100644 --- a/go.sum +++ b/go.sum @@ -469,7 +469,11 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM= +github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xhit/go-simple-mail/v2 v2.11.0 h1:o/056V50zfkO3Mm5tVdo9rG3ryg4ZmJ2XW5GMinHfVs= +github.com/xhit/go-simple-mail/v2 v2.11.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/internal/config/config.go b/internal/config/config.go index 980725c..9ad74a8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,6 +4,7 @@ import ( "html/template" "log" + "github.com/abneed/bookings/internal/models" "github.com/alexedwards/scs/v2" ) @@ -15,4 +16,5 @@ type AppConfig struct { ErrorLog *log.Logger InProduction bool Session *scs.SessionManager + MailChan chan models.MailData } diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 3f88031..448024e 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -2,6 +2,7 @@ package handlers import ( "encoding/json" + "fmt" "net/http" "strconv" "strings" @@ -180,6 +181,38 @@ func (m *Repository) PostReservation(w http.ResponseWriter, r *http.Request) { return } + // send notifications - first to guest + htmlMessage := fmt.Sprintf(` + Reservation Confirmation
+ Dear %s,
+ This is to confirm your reservation from %s to %s. + `, reservation.FirstName, reservation.StartDate.Format("2006-01-02"), reservation.EndDate.Format("2006-01-02")) + + msg := models.MailData{ + To: reservation.Email, + From: "me@here.com", + Subject: "Reservation Confirmation", + Content: htmlMessage, + Template: "basic.html", + } + + m.App.MailChan <- msg + + // send notifications to property owner + htmlMessage = fmt.Sprintf(` + Reservation Notifcation
+ A reservation has been made for %s from %s to %s. + `, reservation.Room.RoomName, reservation.StartDate.Format("2006-01-02"), reservation.EndDate.Format("2006-01-02")) + + msg = models.MailData{ + To: "me@here.com", + From: "me@here.com", + Subject: "Reservation Notification", + Content: htmlMessage, + } + + m.App.MailChan <- msg + m.App.Session.Put(r.Context(), "reservation", reservation) http.Redirect(w, r, "/reservation-summary", http.StatusSeeOther) diff --git a/internal/handlers/setup_test.go b/internal/handlers/setup_test.go index fe87d18..8d1d474 100644 --- a/internal/handlers/setup_test.go +++ b/internal/handlers/setup_test.go @@ -45,6 +45,12 @@ func TestMain(m *testing.M) { app.Session = session + mailChan := make(chan models.MailData) + app.MailChan = mailChan + defer close(mailChan) + + listenForMail() + tc, err := CreateTestTemplateCache() if err != nil { log.Fatal("cannot create template cache") @@ -60,6 +66,14 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +func listenForMail() { + go func() { + for { + _ = <-app.MailChan + } + }() +} + func getRoutes() http.Handler { mux := chi.NewRouter() diff --git a/internal/models/models.go b/internal/models/models.go index 77fc632..a9356f9 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -1,6 +1,8 @@ package models -import "time" +import ( + "time" +) // User is the user model type User struct { @@ -59,3 +61,12 @@ type RoomRestriction struct { Reservation Reservation Restriction Restriction } + +// MailData holds an email message +type MailData struct { + To string + From string + Subject string + Content string + Template string +}