package mail
import (
"bytes"
"crypto/tls"
"fmt"
"text/template"
"time"
"github.com/crazy-max/diun/internal/model"
"github.com/crazy-max/diun/internal/notif/notifier"
"github.com/crazy-max/diun/pkg/utl"
"github.com/go-gomail/gomail"
"github.com/matcornic/hermes/v2"
"github.com/rs/zerolog/log"
)
// Client represents an active mail notification object
type Client struct {
*notifier.Notifier
cfg model.Mail
app model.App
}
// New creates a new mail notification instance
func New(config model.Mail, app model.App) notifier.Notifier {
return notifier.Notifier{
Handler: &Client{
cfg: config,
app: app,
},
}
}
// Name returns notifier's name
func (c *Client) Name() string {
return "mail"
}
// Send creates and sends an email notification with an entry
func (c *Client) Send(entry model.NotifEntry) error {
h := hermes.Hermes{
Theme: new(Theme),
Product: hermes.Product{
Name: c.app.Name,
Link: "https://github.com/crazy-max/diun",
Logo: "https://raw.githubusercontent.com/crazy-max/diun/master/.res/diun.png",
Copyright: fmt.Sprintf("%s © %d %s %s",
c.app.Author,
time.Now().Year(),
c.app.Name,
c.app.Version),
},
}
// Subject
subject := fmt.Sprintf("Image update for %s", entry.Image.String())
if entry.Status == model.ImageStatusNew {
subject = fmt.Sprintf("New image %s has been added", entry.Image.String())
}
// Body
var emailBuf bytes.Buffer
emailTpl := template.Must(template.New("email").Parse(`
Docker 🐳 tag **{{ .Image.Domain }}/{{ .Image.Path }}:{{ .Image.Tag }}** which you subscribed to through **{{ .Provider }}** provider has been {{ if (eq .Status "new") }}newly added{{ else }}updated{{ end }}.
This image has been {{ if (eq .Status "new") }}created{{ else }}updated{{ end }} at {{ .Manifest.Created }} with digest {{ .Manifest.Digest }} for {{ .Manifest.Os }}/{{ .Manifest.Architecture }} platform.
Need help, or have questions? Go to https://github.com/crazy-max/diun and leave an issue.
`))
if err := emailTpl.Execute(&emailBuf, entry); err != nil {
return err
}
email := hermes.Email{
Body: hermes.Body{
Title: fmt.Sprintf("%s 🔔 notification", c.app.Name),
FreeMarkdown: hermes.Markdown(emailBuf.String()),
Signature: "Thanks for your support",
},
}
// Generate an HTML email with the provided contents (for modern clients)
htmlpart, err := h.GenerateHTML(email)
if err != nil {
return fmt.Errorf("hermes: %v", err)
}
// Generate the plaintext version of the e-mail (for clients that do not support xHTML)
textpart, err := h.GeneratePlainText(email)
if err != nil {
return fmt.Errorf("hermes: %v", err)
}
msg := gomail.NewMessage()
msg.SetHeader("From", fmt.Sprintf("%s <%s>", c.app.Name, c.cfg.From))
msg.SetHeader("To", c.cfg.To)
msg.SetHeader("Subject", subject)
msg.SetBody("text/plain", textpart)
msg.AddAlternative("text/html", htmlpart)
var tlsConfig *tls.Config
if c.cfg.InsecureSkipVerify {
tlsConfig = &tls.Config{
InsecureSkipVerify: c.cfg.InsecureSkipVerify,
}
}
username, err := utl.GetSecret(c.cfg.Username, c.cfg.UsernameFile)
if err != nil {
log.Warn().Err(err).Msg("Cannot retrieve username secret for mail notifier")
}
password, err := utl.GetSecret(c.cfg.Password, c.cfg.PasswordFile)
if err != nil {
log.Warn().Err(err).Msg("Cannot retrieve password secret for mail notifier")
}
dialer := &gomail.Dialer{
Host: c.cfg.Host,
Port: c.cfg.Port,
Username: username,
Password: password,
SSL: c.cfg.SSL,
TLSConfig: tlsConfig,
}
return dialer.DialAndSend(msg)
}