-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
implement send command
Showing
10 changed files
with
317 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/pPrecel/PKUP/internal/logo" | ||
"github.com/pPrecel/PKUP/pkg/config" | ||
"github.com/pPrecel/PKUP/pkg/period" | ||
"github.com/pPrecel/PKUP/pkg/report" | ||
"github.com/pPrecel/PKUP/pkg/send" | ||
"github.com/pterm/pterm" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
func NewSendCommand(opts *Options) *cli.Command { | ||
_, until := period.GetCurrentPKUP() | ||
actionOpts := &sendActionOpts{ | ||
Options: opts, | ||
reportTimestamp: *cli.NewTimestamp(until), | ||
} | ||
return &cli.Command{ | ||
Name: "send", | ||
Usage: "Send emails with generated reports based on the config", | ||
UsageText: "pkup send --config .pkupcompose.yaml", | ||
Aliases: []string{"s"}, | ||
Before: func(_ *cli.Context) error { | ||
// print logo before any action | ||
fmt.Printf("%s\n\n", logo.Build(opts.BuildVersion)) | ||
|
||
return nil | ||
}, | ||
Flags: []cli.Flag{ | ||
&cli.StringFlag{ | ||
Name: "config", | ||
Value: ".pkupcompose.yaml", | ||
Destination: &actionOpts.config, | ||
Action: func(_ *cli.Context, path string) error { | ||
path, err := filepath.Abs(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
actionOpts.config = path | ||
return nil | ||
}, | ||
}, | ||
&cli.TimestampFlag{ | ||
Name: "timestamp", | ||
Usage: "timestamp used to create zip file suffix base on month and year" + report.PeriodFormat, | ||
Layout: report.PeriodFormat, | ||
Timezone: time.Local, | ||
Action: func(_ *cli.Context, t *time.Time) error { | ||
actionOpts.reportTimestamp.SetTimestamp(t.Add(time.Hour*24 - time.Second)) | ||
return nil | ||
}, | ||
}, | ||
&cli.BoolFlag{ | ||
Name: "v", | ||
Usage: "verbose log mode", | ||
DisableDefaultText: true, | ||
Category: loggingCategory, | ||
Action: func(_ *cli.Context, _ bool) error { | ||
opts.Log.Level = pterm.LogLevelDebug | ||
return nil | ||
}, | ||
}, | ||
&cli.BoolFlag{ | ||
Name: "vv", | ||
Usage: "trace log mode", | ||
DisableDefaultText: true, | ||
Category: loggingCategory, | ||
Action: func(_ *cli.Context, _ bool) error { | ||
opts.Log.Level = pterm.LogLevelTrace | ||
return nil | ||
}, | ||
}, | ||
}, | ||
Action: func(_ *cli.Context) error { | ||
return sendCommandAction(actionOpts) | ||
}, | ||
} | ||
} | ||
|
||
func sendCommandAction(opts *sendActionOpts) error { | ||
opts.Log.Info("sending reports for", opts.Log.Args( | ||
"config", opts.config, | ||
)) | ||
|
||
config, err := config.Read(opts.config) | ||
if err != nil { | ||
return fmt.Errorf("failed to read config from path '%s': %s", opts.config, err.Error()) | ||
} | ||
|
||
zipSuffix := opts.reportTimestamp.Value().Format("_01_2006") | ||
return send.New(opts.Log).ForConfig(config, zipSuffix) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package send | ||
|
||
import ( | ||
"github.com/pterm/pterm" | ||
"gopkg.in/mail.v2" | ||
) | ||
|
||
type dialer struct { | ||
logger *pterm.Logger | ||
sender mail.SendCloser | ||
} | ||
|
||
func NewDialer(logger *pterm.Logger, address string, port int, username, password string) (*dialer, error) { | ||
d := mail.NewDialer(address, port, username, password) | ||
d.StartTLSPolicy = mail.MandatoryStartTLS | ||
|
||
logger.Trace("dialing to server", logger.Args("address", address, "port", port, "username", username)) | ||
sendCloser, err := d.Dial() | ||
d.DialAndSend() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &dialer{ | ||
logger: logger, | ||
sender: sendCloser, | ||
}, nil | ||
} | ||
|
||
func (d *dialer) Close() { | ||
d.logger.Trace("closing dialer") | ||
d.sender.Close() | ||
} | ||
|
||
func (d *dialer) SendMail(from, subject, destination, body, attachmentPath string) error { | ||
message := mail.NewMessage() | ||
message.SetHeader("From", from) | ||
message.SetHeader("To", destination) | ||
message.SetHeader("Subject", subject) | ||
message.SetBody("text/html", body) | ||
message.Attach(attachmentPath) | ||
|
||
return mail.Send(d.sender, message) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package send | ||
|
||
import ( | ||
"os" | ||
"time" | ||
|
||
"github.com/pPrecel/PKUP/pkg/config" | ||
"github.com/pterm/pterm" | ||
) | ||
|
||
type Sender interface { | ||
ForConfig(*config.Config, string) error | ||
} | ||
|
||
type sender struct { | ||
logger *pterm.Logger | ||
} | ||
|
||
func New(logger *pterm.Logger) Sender { | ||
return &sender{ | ||
logger: logger, | ||
} | ||
} | ||
|
||
func (s *sender) ForConfig(config *config.Config, zipSuffix string) error { | ||
body := "" | ||
if config.Send.HTMLBodyPath != "" { | ||
s.logger.Debug("reading body", s.logger.Args("path", config.Send.HTMLBodyPath)) | ||
bodyBytes, err := os.ReadFile(config.Send.HTMLBodyPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
body = string(bodyBytes) | ||
} | ||
|
||
dialer, err := NewDialer(s.logger, config.Send.ServerAddress, config.Send.ServerPort, config.Send.Username, config.Send.Password) | ||
if err != nil { | ||
return err | ||
} | ||
defer dialer.Close() | ||
|
||
zipper := NewZipper(s.logger) | ||
|
||
for i, report := range config.Reports { | ||
s.logger.Debug("zipping report", s.logger.Args("dir", report.OutputDir, "suffix", zipSuffix)) | ||
reportFile, err := zipper.Do(report.OutputDir, zipSuffix) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
s.logger.Info("sending report", s.logger.Args("from", config.Send.From, "to", report.Email, "attachmentPath", reportFile)) | ||
err = dialer.SendMail(config.Send.From, config.Send.Subject, report.Email, body, reportFile) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if config.Send.Delay != nil && i < len(config.Reports)-1 { | ||
s.logger.Debug("waiting...", s.logger.Args("delay", config.Send.Delay.String())) | ||
time.Sleep(*config.Send.Delay) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package send | ||
|
||
import ( | ||
"archive/zip" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/pterm/pterm" | ||
) | ||
|
||
type zipper struct { | ||
logger *pterm.Logger | ||
} | ||
|
||
func NewZipper(logger *pterm.Logger) *zipper { | ||
return &zipper{ | ||
logger: logger, | ||
} | ||
} | ||
|
||
func (z *zipper) Do(zipFilename, zipSuffix string) (string, error) { | ||
fullZipPath := fmt.Sprintf("%s%s.%s", zipFilename, zipSuffix, "zip") | ||
|
||
z.logger.Debug("creating zip file", z.logger.Args( | ||
"path", fullZipPath, | ||
)) | ||
outFile, err := os.Create(fullZipPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer outFile.Close() | ||
|
||
w := zip.NewWriter(outFile) | ||
defer w.Close() | ||
|
||
baseInZip := fmt.Sprintf("%s%s", filepath.Base(zipFilename), zipSuffix) | ||
if err := z.addFilesToZip(w, zipFilename, baseInZip); err != nil { | ||
return "", err | ||
} | ||
|
||
return fullZipPath, nil | ||
} | ||
|
||
func (z *zipper) addFilesToZip(w *zip.Writer, basePath, baseInZip string) error { | ||
files, err := os.ReadDir(basePath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, file := range files { | ||
fullFilepath := filepath.Join(basePath, file.Name()) | ||
z.logger.Trace("adding item to zip", z.logger.Args( | ||
"path", fullFilepath, | ||
"baseInZip", baseInZip, | ||
)) | ||
|
||
if file.IsDir() { | ||
if err := z.addFilesToZip(w, fullFilepath, filepath.Join(baseInZip, file.Name())); err != nil { | ||
return err | ||
} | ||
} else if file.Type().IsRegular() { | ||
dat, err := os.ReadFile(fullFilepath) | ||
if err != nil { | ||
return err | ||
} | ||
f, err := w.Create(filepath.Join(baseInZip, file.Name())) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = f.Write(dat) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
return nil | ||
} |