From 9a90c8dc3a387da250163af457bc60b40c729950 Mon Sep 17 00:00:00 2001 From: landrigun Date: Sat, 15 Oct 2022 13:58:03 +0000 Subject: [PATCH] move structs in right packages + impl sender service to build and send e-mail --- config/smtp.go | 43 ++++++++++++++ mail/mail.go | 49 ++++++++++++++++ main.go | 143 ++++++++++++++++----------------------------- services/sender.go | 39 +++++++++++++ 4 files changed, 182 insertions(+), 92 deletions(-) create mode 100644 config/smtp.go create mode 100644 mail/mail.go create mode 100644 services/sender.go diff --git a/config/smtp.go b/config/smtp.go new file mode 100644 index 0000000..e4ca0f8 --- /dev/null +++ b/config/smtp.go @@ -0,0 +1,43 @@ +package config + +import ( + "errors" + "fmt" +) + +// mandatory parameters for the STMP server connection +type SMTPConfig struct { + User string + Password string + Url string + Port string +} + +func NewSMTPConfig(user, password, url, port string) (SMTPConfig, error) { + var config SMTPConfig + if user == "" { + return config, errors.New("SMTP user can't be empty") + } + config.User = user + + if password == "" { + return config, errors.New("SMTP password can't be empty") + } + config.Password = password + + if url == "" { + return config, errors.New("SMTP server url can't be empty") + } + config.Url = url + + if port == "" { + return config, errors.New("SMTP server port can't be empty") + } + config.Port = port + + return config, nil +} + +func (c SMTPConfig) GetFullUrl() string { + return fmt.Sprintf("%s:%s", c.Url, c.Port) +} diff --git a/mail/mail.go b/mail/mail.go new file mode 100644 index 0000000..768edef --- /dev/null +++ b/mail/mail.go @@ -0,0 +1,49 @@ +package mail + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "strings" +) + +type Email struct { + Sender string `json:"sender"` + Receivers []string `json:"receivers"` + Subject string `json:"subject"` + Content string `json:"content"` +} + +func NewEmail(sender string, receivers []string, subject, content string) Email { + return Email{ + Sender: sender, + Receivers: receivers, + Subject: subject, + Content: content, + } +} + +func FromJSON(path string) error { + content, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + var mail Email + if err := json.Unmarshal(content, &mail); err != nil { + return err + } + + return nil +} + +func (e Email) Generate() []byte { + mail := fmt.Sprintf( + "To: %s\nFrom: %s\nContent-Type: text/html\nSubject: %s\n\n%s", + strings.Join(e.Receivers, ","), + e.Sender, + e.Sender, + e.Content, + ) + return []byte(mail) +} diff --git a/main.go b/main.go index a5f602e..55e3c4e 100644 --- a/main.go +++ b/main.go @@ -3,79 +3,22 @@ package main import ( "errors" "fmt" - "net/smtp" "os" - "strings" + + cfg "mailsrv/config" + "mailsrv/mail" + srv "mailsrv/services" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" - "gopkg.in/ini.v1" + ini "gopkg.in/ini.v1" ) -// mandatory parameters to send an email -type SMTPConfig struct { - User string - Password string - Url string - Port string -} - -func NewSMTPConfig(user, password, url, port string) (SMTPConfig, error) { - var config SMTPConfig - if user == "" { - return config, errors.New("SMTP user can't be empty") - } - config.User = user - - if password == "" { - return config, errors.New("SMTP password can't be empty") - } - config.Password = password - - if url == "" { - return config, errors.New("SMTP server url can't be empty") - } - config.Url = url - - if port == "" { - return config, errors.New("SMTP server port can't be empty") - } - config.Port = port - - return config, nil -} - -func (c SMTPConfig) getFullUrl() string { - return fmt.Sprintf("%s:%s", c.Url, c.Port) -} - -type Email struct { - Sender string - Receivers []string - Subject string - Content string -} - -func NewEmail(sender string, receivers []string, subject, content string) Email { - return Email{ - Sender: sender, - Receivers: receivers, - Subject: subject, - Content: content, - } -} - -func (e Email) Generate() []byte { - mail := fmt.Sprintf( - "To: %s\nFrom: %s\nContent-Type: text/html\nSubject: %s\n\n%s", - strings.Join(e.Receivers, ","), - e.Sender, - e.Sender, - e.Content, - ) - return []byte(mail) -} +const ( + DefaultOutboxPath string = "outbox" +) +// simply collects binary arguments func GetConfigPath() (string, error) { switch len(os.Args) { case 1: @@ -87,21 +30,26 @@ func GetConfigPath() (string, error) { } } -func LoadConfig(configPath string) (SMTPConfig, error) { - var config SMTPConfig - +func LoadIni() (*ini.File, error) { configPath, err := GetConfigPath() if err != nil { - return config, fmt.Errorf("unable to get the .ini config path err=%v", err) + return nil, fmt.Errorf("unable to get the .ini config path err=%v", err) } - cfg, err := ini.Load(configPath) + ini, err := ini.Load(configPath) if err != nil { - return config, fmt.Errorf("unable to load the .ini config path err=%v", err) + return nil, fmt.Errorf("unable to load the .ini config path err=%v", err) } - section := cfg.Section("server") - config, err = NewSMTPConfig( + return ini, nil +} + +// collects mandatory SMTP parameters to send an e-mail from the `.ini` file +func LoadSMTPConfig(iniFile *ini.File) (cfg.SMTPConfig, error) { + var config cfg.SMTPConfig + + section := iniFile.Section("server") + config, err := cfg.NewSMTPConfig( section.Key("username").String(), section.Key("password").String(), section.Key("url").String(), @@ -114,37 +62,48 @@ func LoadConfig(configPath string) (SMTPConfig, error) { return config, nil } +// tries to get the outbox path from the `.ini` file and creates the directory +// if the path does not exist, create a default one: `outbox` next to the binary +func GetOutboxPath(iniFile *ini.File) (string, error) { + outboxPath := iniFile.Section("service").Key("outbox_path").String() + if outboxPath == "" { + outboxPath = DefaultOutboxPath + } + + if err := os.MkdirAll(outboxPath, 0750); err != nil && !os.IsExist(err) { + return "", err + } + + return outboxPath, nil +} + func main() { var logger log.Logger logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller, "service", "mailsrv") - configPath, err := GetConfigPath() + iniFile, err := LoadIni() if err != nil { - level.Error(logger).Log("msg", "unable to get the .ini config path", "err", err) - os.Exit(1) + level.Error(logger).Log("msg", "unable to load the .ini configuration file", "err", err) + return } - config, err := LoadConfig(configPath) + config, err := LoadSMTPConfig(iniFile) if err != nil { level.Error(logger).Log("msg", "unable to load the SMTP configuration", "err", err) - os.Exit(1) + return + } + + outboxPath, err := GetOutboxPath(iniFile) + if err != nil { + level.Error(logger).Log("msg", "unable to retrieve outputbox path", "err", err) + return } logger.Log("action", "send email test") - content := fmt.Sprintf("Hi!

This is an e-mail test, please do not reply.

Thegux Administrator
%s", `
visit the website: thegux.fr`) - email := NewEmail(config.User, []string{"receiver@receiver.com"}, "e-mail test", content) + email := mail.NewEmail(config.User, []string{"example@example.com"}, "e-mail test", content) - auth := smtp.PlainAuth("", config.User, config.Password, config.Url) - logger.Log("action", "authentication succeed") - - fmt.Println(config.getFullUrl()) - - if err := smtp.SendMail(config.getFullUrl(), auth, email.Sender, email.Receivers, email.Generate()); err != nil { - level.Error(logger).Log("msg", "error while sending email", "err", err) - os.Exit(1) - } - - logger.Log("msg", "mail send successfully") + sender := srv.NewSender(logger, config, outboxPath) + sender.SendMail(email) } diff --git a/services/sender.go b/services/sender.go new file mode 100644 index 0000000..d1bb035 --- /dev/null +++ b/services/sender.go @@ -0,0 +1,39 @@ +package services + +import ( + cfg "mailsrv/config" + "mailsrv/mail" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "net/smtp" +) + +type Sender struct { + SMTPConfig cfg.SMTPConfig + logger log.Logger + // fetch this directory to collect `.json` e-mail format + OutboxPath string +} + +func NewSender(logger log.Logger, config cfg.SMTPConfig, outboxPath string) Sender { + logger = log.With(logger, "actor", "sender") + return Sender{ + SMTPConfig: config, + logger: logger, + OutboxPath: outboxPath, + } +} + +func (s Sender) SendMail(email mail.Email) error { + auth := smtp.PlainAuth("", s.SMTPConfig.User, s.SMTPConfig.Password, s.SMTPConfig.Url) + s.logger.Log("action", "authentication succeed") + + if err := smtp.SendMail(s.SMTPConfig.GetFullUrl(), auth, email.Sender, email.Receivers, email.Generate()); err != nil { + level.Error(s.logger).Log("msg", "error while sending email", "err", err) + return err + } + + s.logger.Log("msg", "mail send successfully") + return nil +}