Skip to content

Commit

Permalink
feat: Update email configuration in config.go and add email sender test
Browse files Browse the repository at this point in the history
  • Loading branch information
arya2004 committed Aug 14, 2024
1 parent a746bdb commit 91c6764
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/google/uuid v1.6.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0
github.com/hibiken/asynq v0.24.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/rs/zerolog v1.33.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts=
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=
Expand Down
148 changes: 148 additions & 0 deletions mail/sender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package mail

import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"html/template"
"io/ioutil"
"mime"
"mime/multipart"
"net/smtp"
"net/textproto"
"path/filepath"
"strings"
)

const (
smtpAuthAddress = "smtp.office365.com"
smtpServerAddress = "smtp.office365.com:587"
)

type EmailSender interface {
SendEmail(subject string, templatePath string, to []string, cc []string, bcc []string, attachFiles []string, data map[string]string) error
}

type GmailSender struct {
name string
fromEmail string
password string
}

func NewGmailSender(name string, fromEmail string, password string) EmailSender {
return &GmailSender{name, fromEmail, password}
}

// Function to read and apply a template with data
func loadTemplate(templatePath string, data map[string]string) (string, error) {
tmplContent, err := ioutil.ReadFile(templatePath)
if err != nil {
return "", err
}

tmpl, err := template.New("email").Parse(string(tmplContent))
if err != nil {
return "", err
}

var content bytes.Buffer
err = tmpl.Execute(&content, data)
if err != nil {
return "", err
}

return content.String(), nil
}

func (sender *GmailSender) SendEmail(subject string, templatePath string, to []string, cc []string, bcc []string, attachFiles []string, data map[string]string) error {
// Load the email content from the template with data
content, err := loadTemplate(templatePath, data)
if err != nil {
return err
}

// Build the email body
var msg bytes.Buffer
writer := multipart.NewWriter(&msg)

// Set up the main headers
msg.WriteString(fmt.Sprintf("From: %s\r\n", sender.fromEmail))
msg.WriteString(fmt.Sprintf("To: %s\r\n", strings.Join(to, ", ")))
if len(cc) > 0 {
msg.WriteString(fmt.Sprintf("Cc: %s\r\n", strings.Join(cc, ", ")))
}
msg.WriteString(fmt.Sprintf("Subject: %s\r\n", subject))
msg.WriteString("MIME-Version: 1.0\r\n")
msg.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", writer.Boundary()))

// Add the HTML content as part of the multipart message
htmlPart, err := writer.CreatePart(textproto.MIMEHeader{
"Content-Type": {"text/html; charset=UTF-8"},
})
if err != nil {
return err
}
htmlPart.Write([]byte(content))

// Attach files
for _, file := range attachFiles {
if err := attachFile(writer, file); err != nil {
return err
}
}

writer.Close()

// Send the email
auth := LoginAuth(sender.fromEmail, sender.password)
return smtp.SendMail(smtpServerAddress, auth, sender.fromEmail, append(to, cc...), msg.Bytes())
}

// Function to attach a file to the email
func attachFile(writer *multipart.Writer, filePath string) error {
fileData, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}

_, fileName := filepath.Split(filePath)
part, err := writer.CreatePart(textproto.MIMEHeader{
"Content-Type": {mime.TypeByExtension(filepath.Ext(filePath))},
"Content-Disposition": {fmt.Sprintf("attachment; filename=\"%s\"", fileName)},
"Content-Transfer-Encoding": {"base64"},
})
if err != nil {
return err
}

part.Write([]byte(base64.StdEncoding.EncodeToString(fileData)))

return nil
}

type loginAuth struct {
username, password string
}

func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}

func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte{}, nil
}

func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("Unknown fromServer")
}
}
return nil, nil
}
38 changes: 38 additions & 0 deletions mail/sender_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package mail

import (
"fmt"
"testing"

"github.com/arya2004/xyfin/utils"
)



func TestSendEmailWithGmail(t *testing.T) {

config, err := utils.LoadConfig("..")

if err != nil {
fmt.Println("Failed to load config:", err)

}

sender := NewGmailSender("Xphyrus", config.EmailSenderAddress, config.EmailSenderPassword)
to := []string{"[email protected]"}
cc := []string{}
bcc := []string{}
attachFiles := []string{} // Add file paths if you have attachments

data := map[string]string{
"Name": "John Doe",
}

err = sender.SendEmail("Welcome to Our Service!", "./welcome_template.html", to, cc, bcc, attachFiles, data)
if err != nil {
fmt.Println("Failed to send email:", err)
} else {
fmt.Println("Email sent successfully!")
}

}
47 changes: 47 additions & 0 deletions mail/welcome_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
line-height: 1.6;
}
.container {
width: 80%;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #007BFF;
}
p {
margin-bottom: 20px;
}
.footer {
font-size: 0.9em;
color: #777;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to Our Service!</h1>
<p>Dear {{.Name}},</p>
<p>Thank you for joining us. We're excited to have you on board!</p>
<p>If you have any questions, feel free to reach out to our support team at any time.</p>
<p>Best regards,</p>
<p>The Team</p>
<div class="footer">
<p>If you didn't sign up for this account, please ignore this email or contact support.</p>
</div>
</div>
</body>
</html>
3 changes: 3 additions & 0 deletions utils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type Configuration 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"`
}

func LoadConfig(path string) (config Configuration, err error) {
Expand Down

0 comments on commit 91c6764

Please sign in to comment.