Skip to content

Commit

Permalink
Add webhook notification method
Browse files Browse the repository at this point in the history
  • Loading branch information
crazy-max committed Mar 28, 2019
1 parent f805b68 commit 5027bfc
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 54 deletions.
17 changes: 9 additions & 8 deletions internal/app/ftpgrab.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"github.com/ftpgrab/ftpgrab/internal/config"
"github.com/ftpgrab/ftpgrab/internal/db"
"github.com/ftpgrab/ftpgrab/internal/journal"
"github.com/ftpgrab/ftpgrab/internal/mail"
"github.com/ftpgrab/ftpgrab/internal/model"
"github.com/ftpgrab/ftpgrab/internal/notif"
"github.com/ftpgrab/ftpgrab/internal/server"
"github.com/ftpgrab/ftpgrab/internal/server/ftp"
"github.com/ftpgrab/ftpgrab/internal/server/sftp"
Expand All @@ -27,6 +27,7 @@ type FtpGrab struct {
cfg *config.Configuration
srv *server.Client
db *db.Client
notif *notif.Client
jnl *journal.Client
locker uint32
}
Expand Down Expand Up @@ -91,6 +92,11 @@ func (fg *FtpGrab) Run() {
log.Fatal().Err(err).Msg("Cannot open database")
}

// Notification client
if fg.notif, err = notif.New(fg.cfg.Notif, fg.cfg.App, fg.srv.Common()); err != nil {
log.Fatal().Err(err).Msg("Cannot create notifiers")
}

// Iterate sources
for _, src := range fg.srv.Common().Sources {
log.Info().Msg("########")
Expand All @@ -116,13 +122,8 @@ func (fg *FtpGrab) Run() {
return
}

// Send email report
if fg.cfg.Mail.Enable {
if err := mail.Send(fg.jnl, fg.cfg.App, fg.srv.Common(), fg.cfg.Mail); err != nil {
log.Error().Err(err).Msg("Cannot send email")
return
}
}
// Send notifications
fg.notif.Send(*fg.jnl)
}

// Close closes ftpgrab (ftp and db connection)
Expand Down
35 changes: 22 additions & 13 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Configuration struct {
Server model.Server `yaml:"server,omitempty"`
Db model.Db `yaml:"db,omitempty"`
Download model.Download `yaml:"download,omitempty"`
Mail model.Mail `yaml:"mail,omitempty"`
Notif model.Notif `yaml:"notif,omitempty"`
File os.FileInfo
}

Expand Down Expand Up @@ -75,12 +75,19 @@ func Load(fl model.Flags, version string) (*Configuration, error) {
HideSkipped: false,
CreateBasedir: false,
},
Mail: model.Mail{
Enable: false,
Host: "localhost",
Port: 25,
SSL: false,
InsecureSkipVerify: false,
Notif: model.Notif{
Mail: model.Mail{
Enable: false,
Host: "localhost",
Port: 25,
SSL: false,
InsecureSkipVerify: false,
},
Webhook: model.Webhook{
Enable: false,
Method: "GET",
Timeout: 10,
},
},
}

Expand Down Expand Up @@ -133,11 +140,11 @@ func (cfg *Configuration) Check() error {
}
}

if cfg.Mail.Enable {
if _, err := mail.ParseAddress(cfg.Mail.From); err != nil {
if cfg.Notif.Mail.Enable {
if _, err := mail.ParseAddress(cfg.Notif.Mail.From); err != nil {
return fmt.Errorf("cannot sender mail address, %v", err)
}
if _, err := mail.ParseAddress(cfg.Mail.To); err != nil {
if _, err := mail.ParseAddress(cfg.Notif.Mail.To); err != nil {
return fmt.Errorf("cannot recipient mail address, %v", err)
}
}
Expand Down Expand Up @@ -194,9 +201,11 @@ func (cfg *Configuration) Display() {
Key: "********",
},
},
Mail: model.Mail{
Username: "********",
Password: "********",
Notif: model.Notif{
Mail: model.Mail{
Username: "********",
Password: "********",
},
},
}
if err := mergo.Merge(&out, cfg); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/journal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (

// Client represents an active journal object
type Client struct {
*model.Journal
model.Journal
}

// New creates new journal instance
func New() *Client {
return &Client{&model.Journal{}}
return &Client{model.Journal{}}
}

// AddEntry adds an entry in the journal
Expand Down
38 changes: 27 additions & 11 deletions internal/model/journal.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
package model

import "time"
import (
"encoding/json"
"time"

"github.com/hako/durafmt"
)

// Journal holds ftpgrab entries and status
type Journal struct {
Entries []Entry
Entries []Entry `json:"entries,omitempty"`
Count struct {
Success int
Error int
Skip int
}
Status string
Duration time.Duration
Success int `json:"success,omitempty"`
Error int `json:"error,omitempty"`
Skip int `json:"skip,omitempty"`
} `json:"count,omitempty"`
Status string `json:"status,omitempty"`
Duration time.Duration `json:"duration,omitempty"`
}

// Entry represents a journal entry
type Entry struct {
File string
StatusType string
StatusText string
File string `json:"file,omitempty"`
StatusType string `json:"status_type,omitempty"`
StatusText string `json:"status_text,omitempty"`
}

// EntryStatus represents entry status
type EntryStatus string

func (j Journal) MarshalJSON() ([]byte, error) {
type Alias Journal
return json.Marshal(&struct {
Alias
Duration string `json:"duration,omitempty"`
}{
Alias: (Alias)(j),
Duration: durafmt.ParseShort(j.Duration).String(),
})
}
7 changes: 7 additions & 0 deletions internal/model/notif.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model

// Notif holds data necessary for notification configuration
type Notif struct {
Mail Mail `yaml:"mail,omitempty"`
Webhook Webhook `yaml:"webhook,omitempty"`
}
10 changes: 10 additions & 0 deletions internal/model/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package model

// Webhook holds webhook notification configuration details
type Webhook struct {
Enable bool `yaml:"enable,omitempty"`
Endpoint string `yaml:"endpoint,omitempty"`
Method string `yaml:"method,omitempty"`
Headers map[string]string `yaml:"headers,omitempty"`
Timeout int `yaml:"timeout,omitempty"`
}
49 changes: 49 additions & 0 deletions internal/notif/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package notif

import (
"github.com/ftpgrab/ftpgrab/internal/journal"
"github.com/ftpgrab/ftpgrab/internal/model"
"github.com/ftpgrab/ftpgrab/internal/notif/mail"
"github.com/ftpgrab/ftpgrab/internal/notif/notifier"
"github.com/ftpgrab/ftpgrab/internal/notif/webhook"
"github.com/rs/zerolog/log"
)

// Client represents an active webhook notification object
type Client struct {
cfg model.Notif
app model.App
cmn model.Common
notifiers []notifier.Notifier
}

// New creates a new notification instance
func New(config model.Notif, app model.App, cmn model.Common) (*Client, error) {
var c = &Client{
cfg: config,
app: app,
cmn: cmn,
notifiers: []notifier.Notifier{},
}

// Add notifiers
if config.Mail.Enable {
c.notifiers = append(c.notifiers, mail.New(config.Mail, app, cmn))
}
if config.Webhook.Enable {
c.notifiers = append(c.notifiers, webhook.New(config.Webhook, app, cmn))
}

log.Debug().Msgf("%d notifier(s) created", len(c.notifiers))
return c, nil
}

// Send creates and sends notifications to notifiers
func (c *Client) Send(jnl journal.Client) {
for _, n := range c.notifiers {
log.Debug().Msgf("Sending %s notification...", n.Name())
if err := n.Send(jnl); err != nil {
log.Error().Err(err).Msgf("%s notification failed", n.Name())
}
}
}
65 changes: 45 additions & 20 deletions internal/mail/send.go → internal/notif/mail/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,49 @@ import (

"github.com/ftpgrab/ftpgrab/internal/journal"
"github.com/ftpgrab/ftpgrab/internal/model"
"github.com/ftpgrab/ftpgrab/internal/notif/notifier"
"github.com/go-gomail/gomail"
"github.com/hako/durafmt"
"github.com/matcornic/hermes/v2"
)

// Send creates and sends an email with journal entries
func Send(jnl *journal.Client, app model.App, cmn model.Common, mail model.Mail) error {
// Client represents an active mail notification object
type Client struct {
*notifier.Notifier
cfg model.Mail
app model.App
cmn model.Common
}

// New creates a new mail notification instance
func New(config model.Mail, app model.App, cmn model.Common) notifier.Notifier {
return notifier.Notifier{
Handler: &Client{
cfg: config,
app: app,
cmn: cmn,
},
}
}

// Name returns notifier's name
func (c *Client) Name() string {
return "mail"
}

// Send creates and sends an email notification with journal entries
func (c *Client) Send(jnl journal.Client) error {
h := hermes.Hermes{
Theme: new(Theme),
Product: hermes.Product{
Name: app.Name,
Name: c.app.Name,
Link: "https://ftpgrab.github.io",
Logo: "https://ftpgrab.github.io/img/logo.png",
Copyright: fmt.Sprintf("%s © 2014 - %d %s %s",
app.Author,
c.app.Author,
time.Now().Year(),
app.Name,
app.Version),
c.app.Name,
c.app.Version),
},
}

Expand All @@ -40,13 +65,13 @@ func Send(jnl *journal.Client, app model.App, cmn model.Common, mail model.Mail)

email := hermes.Email{
Body: hermes.Body{
Title: fmt.Sprintf("%s report", app.Name),
Title: fmt.Sprintf("%s report", c.app.Name),
FreeMarkdown: hermes.Markdown(fmt.Sprintf(
`**%d** files have been download successfully, **%d** have been skipped and **%d** errors occurred in %s.`,
jnl.Count.Success,
jnl.Count.Skip,
jnl.Count.Error,
durafmt.ParseShort(jnl.Duration).String())),
durafmt.ParseShort(time.Duration(jnl.Duration)).String())),
Table: hermes.Table{
Data: entriesData,
Columns: hermes.Columns{
Expand All @@ -59,7 +84,7 @@ func Send(jnl *journal.Client, app model.App, cmn model.Common, mail model.Mail)
},
},
},
Signature: "Thanks for your support,",
Signature: "Thanks for your support",
},
}

Expand All @@ -77,29 +102,29 @@ func Send(jnl *journal.Client, app model.App, cmn model.Common, mail model.Mail)

hostname, _ := os.Hostname()
msg := gomail.NewMessage()
msg.SetHeader("From", fmt.Sprintf("%s <%s>", app.Name, mail.From))
msg.SetHeader("To", mail.To)
msg.SetHeader("From", fmt.Sprintf("%s <%s>", c.app.Name, c.cfg.From))
msg.SetHeader("To", c.cfg.To)
msg.SetHeader("Subject", fmt.Sprintf("%s report for %s on %s",
app.Name,
cmn.Host,
c.app.Name,
c.cmn.Host,
hostname,
))
msg.SetBody("text/plain", textpart)
msg.AddAlternative("text/html", htmlpart)

var tlsConfig *tls.Config
if mail.InsecureSkipVerify {
if c.cfg.InsecureSkipVerify {
tlsConfig = &tls.Config{
InsecureSkipVerify: mail.InsecureSkipVerify,
InsecureSkipVerify: c.cfg.InsecureSkipVerify,
}
}

dialer := &gomail.Dialer{
Host: mail.Host,
Port: mail.Port,
Username: mail.Username,
Password: mail.Password,
SSL: mail.SSL,
Host: c.cfg.Host,
Port: c.cfg.Port,
Username: c.cfg.Username,
Password: c.cfg.Password,
SSL: c.cfg.SSL,
TLSConfig: tlsConfig,
}

Expand Down
File renamed without changes.
14 changes: 14 additions & 0 deletions internal/notif/notifier/notifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package notifier

import "github.com/ftpgrab/ftpgrab/internal/journal"

// Handler is a notifier interface
type Handler interface {
Name() string
Send(jnl journal.Client) error
}

// Notifier represents an active notifier object
type Notifier struct {
Handler
}
Loading

0 comments on commit 5027bfc

Please sign in to comment.