diff --git a/Makefile b/Makefile index f401c18..41ea654 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ sqlc: sqlc generate test: - go test -v -cover ./... + go test -v -cover -short ./... server: go run main.go diff --git a/go.mod b/go.mod index 673fcfd..68cbd4d 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 github.com/hibiken/asynq v0.25.1 + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/lib/pq v1.10.9 github.com/o1egl/paseto v1.0.0 github.com/rakyll/statik v0.1.7 diff --git a/go.sum b/go.sum index 59f0d85..ec48aa0 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw= github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg= +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..fbac3a9 --- /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" + smtpServerAddrss = "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, fromEmailAddress, 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: %v", f, err) + } + } + + smtpAuth := smtp.PlainAuth("", sender.fromEmailAddress, sender.fromEmailPassword, smtpAuthAddress) + return e.Send(smtpServerAddrss, smtpAuth) +} \ No newline at end of file diff --git a/mail/sender_test.go b/mail/sender_test.go new file mode 100644 index 0000000..62028da --- /dev/null +++ b/mail/sender_test.go @@ -0,0 +1,37 @@ +package mail + +import ( + "testing" + + "github.com/Samudra-G/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 := "Test Email" + content := ` +

Hello, test email

+

This is a test email sent from the Simple Bank application by Samudra G

+ ` + to := []string{"samudramukhar@gmail.com"} + attachFiles := []string{"../README.md"} + + err = sender.SendEmail( + subject, + content, + to, + nil, + nil, + attachFiles, + ) + require.NoError(t, err, "Failed to send email") +} \ No newline at end of file diff --git a/util/config.go b/util/config.go index 4dc5f9a..ebc0efa 100644 --- a/util/config.go +++ b/util/config.go @@ -19,6 +19,9 @@ type Config struct { TokenSymmetricKey string `mapstructure:"TOKEN_SYMMETRIC_KEY"` AccessTokenDuration time.Duration `mapstructure:"ACCESS_TOKEN_DURATION"` RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"` + EmailSenderName string `mapstructure:"EMAIL_SENDER_NAME"` + EmailSenderAddress string `mapstructure:"EMAIL_SENDER_ADDRESS"` + EmailSenderPassword string `mapstructure:"EMAIL_SENDER_PASSWORD"` } //LoadConfig reads configuration from a file or environment variables