From 43ca5dedb7a855ae808a2ee37127782736a61665 Mon Sep 17 00:00:00 2001 From: eizyc Date: Fri, 28 Jun 2024 23:39:08 -0500 Subject: [PATCH] feat(email): send email with gmail --- Makefile | 2 +- app.env | 5 +++- go.mod | 1 + go.sum | 2 ++ mail/sender.go | 65 +++++++++++++++++++++++++++++++++++++++++++++ mail/sender_test.go | 30 +++++++++++++++++++++ util/config.go | 3 +++ 7 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 mail/sender.go create mode 100644 mail/sender_test.go diff --git a/Makefile b/Makefile index 68d39f4..3300577 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ sqlc: sqlc generate test: - go test -v -cover ./... + go test -v -cover -short ./... server: go run main.go diff --git a/app.env b/app.env index 13b8614..8c5977e 100644 --- a/app.env +++ b/app.env @@ -6,4 +6,7 @@ GRPC_SERVER_ADDRESS=0.0.0.0:9090 TOKEN_SYMMETRIC_KEY=12345678901234567890123456789012 ACCESS_TOKEN_DURATION=15m REFRESH_TOKEN_DURATION=24h -REDIS_ADDRESS=0.0.0.0:6379 \ No newline at end of file +REDIS_ADDRESS=0.0.0.0:6379 +EMAIL_SENDER_NAME=Simple Bank +EMAIL_SENDER_ADDRESS=eizyc66@gmail.com +EMAIL_SENDER_PASSWORD=prgctnuzmwqlkcrn \ No newline at end of file diff --git a/go.mod b/go.mod index 064ab68..bd3fb52 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 github.com/jackc/pgx/v5 v5.6.0 + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/redis/go-redis/v9 v9.0.3 github.com/stretchr/testify v1.9.0 google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 diff --git a/go.sum b/go.sum index 662d4cb..18752bc 100644 --- a/go.sum +++ b/go.sum @@ -97,6 +97,8 @@ github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= diff --git a/mail/sender.go b/mail/sender.go new file mode 100644 index 0000000..7881c17 --- /dev/null +++ b/mail/sender.go @@ -0,0 +1,65 @@ +package mail + +import ( + "fmt" + "net/smtp" + + "github.com/jordan-wright/email" +) + +const ( + smtpAuthAddress = "smtp.gmail.com" + smtpServerAddress = "smtp.gmail.com:587" +) + +type EmailSender interface { + SendEmail( + subject string, + content string, + to []string, + cc []string, + bcc []string, + attachFiles []string, + ) error +} + +type GmailSender struct { + name string + fromEmailAddress string + fromEmailPassword string +} + +func NewGmailSender(name string, fromEmailAddress string, fromEmailPassword string) EmailSender { + return &GmailSender{ + name: name, + fromEmailAddress: fromEmailAddress, + fromEmailPassword: fromEmailPassword, + } +} + +func (sender *GmailSender) SendEmail( + subject string, + content string, + to []string, + cc []string, + bcc []string, + attachFiles []string, +) error { + e := email.NewEmail() + e.From = fmt.Sprintf("%s <%s>", sender.name, sender.fromEmailAddress) + e.Subject = subject + e.HTML = []byte(content) + e.To = to + e.Cc = cc + e.Bcc = bcc + + for _, f := range attachFiles { + _, err := e.AttachFile(f) + if err != nil { + return fmt.Errorf("failed to attach file %s: %w", f, err) + } + } + + smtpAuth := smtp.PlainAuth("", sender.fromEmailAddress, sender.fromEmailPassword, smtpAuthAddress) + return e.Send(smtpServerAddress, smtpAuth) +} diff --git a/mail/sender_test.go b/mail/sender_test.go new file mode 100644 index 0000000..08d9f34 --- /dev/null +++ b/mail/sender_test.go @@ -0,0 +1,30 @@ +package mail + +import ( + "testing" + + "github.com/eizyc/simplebank/util" + "github.com/stretchr/testify/require" +) + +func TestSendEmailWithGmail(t *testing.T) { + if testing.Short() { + t.Skip() + } + + config, err := util.LoadConfig("..") + require.NoError(t, err) + + sender := NewGmailSender(config.EmailSenderName, config.EmailSenderAddress, config.EmailSenderPassword) + + subject := "A test email" + content := ` +

Hello world

+

This is a test message from Eizyc

+ ` + to := []string{"eizyc66@gmail.com"} + attachFiles := []string{"../README.md"} + + err = sender.SendEmail(subject, content, to, nil, nil, attachFiles) + require.NoError(t, err) +} diff --git a/util/config.go b/util/config.go index 072db91..2d49382 100644 --- a/util/config.go +++ b/util/config.go @@ -19,6 +19,9 @@ type Config struct { AccessTokenDuration time.Duration `mapstructure:"ACCESS_TOKEN_DURATION"` RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"` RedisAddress string `mapstructure:"REDIS_ADDRESS"` + EmailSenderName string `mapstructure:"EMAIL_SENDER_NAME"` + EmailSenderAddress string `mapstructure:"EMAIL_SENDER_ADDRESS"` + EmailSenderPassword string `mapstructure:"EMAIL_SENDER_PASSWORD"` } // LoadConfig reads configuration from file or environment variables.