mirror of
https://github.com/tizbac/proxmoxbackupclient_go.git
synced 2025-10-26 11:19:03 +00:00
feat: add template engine to mail
This commit also introduced the capability to define multiple mail recipients using a comma separated list of addresses
This commit is contained in:
parent
1fb01da341
commit
0b20763a53
18
config.go
18
config.go
@ -12,6 +12,11 @@ type MailSendConfig struct {
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
type MailTemplate struct {
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type SMTPConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
@ -19,6 +24,7 @@ type SMTPConfig struct {
|
||||
Password string `json:"password"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Mails []MailSendConfig `json:"mails"`
|
||||
Template *MailTemplate `json:"template"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@ -73,8 +79,10 @@ func loadConfig() *Config {
|
||||
mailInsecureFlag := flag.Bool("mail-insecure", false, "mail notification system: allow insecure communications(optional)")
|
||||
mailFromFlag := flag.String("mail-from", "", "mail notification system: sender mail(optional)")
|
||||
mailToFlag := flag.String("mail-to", "", "mail notification system: receiver mail(optional)")
|
||||
mailSubjectTemplateFlag := flag.String("mail-subject-template", "", "mail notification system: mail subject template(optional)")
|
||||
mailBodyTemplateFlag := flag.String("mail-body-template", "", "mail notification system: mail body template(optional)")
|
||||
|
||||
configFile := flag.String("config", "", "Path to JSON config file. If this flag is provided all the others are ignored")
|
||||
configFile := flag.String("config", "", "Path to JSON config file. If this flag is provided all the others will override the loaded config file")
|
||||
|
||||
// Parse command line flags
|
||||
flag.Parse()
|
||||
@ -145,6 +153,14 @@ func loadConfig() *Config {
|
||||
initMailConfsIfNeeded()
|
||||
config.SMTP.Mails[0].To = *mailToFlag
|
||||
}
|
||||
if *mailSubjectTemplateFlag != "" {
|
||||
initSmtpConfigIfNeeded()
|
||||
config.SMTP.Template.Subject = *mailSubjectTemplateFlag
|
||||
}
|
||||
if *mailBodyTemplateFlag != "" {
|
||||
initSmtpConfigIfNeeded()
|
||||
config.SMTP.Template.Body = *mailBodyTemplateFlag
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@ -13,6 +13,11 @@
|
||||
"port": "465",
|
||||
"username": "my-user@example.com",
|
||||
"password": "my-password",
|
||||
"insecure": false,
|
||||
"template": {
|
||||
"subject": "{{if not .Success}}[FAILED]{{else}}[SUCCESS]{{end}} Backup report for {{.Datastore}}",
|
||||
"body": "Backup {{if .Success}}completed{{else}}ended with errors{{end}} on host {{.Hostname}} (took {{.FromattedDuration}})\n{{if .Success}}Chunks New {{.NewChunks}}, Reused {{.ReusedChunks}}.{{else}}Error occurred while working, backup may be not completed.\nLast error is: {{.ErrorStr}}{{end}}"
|
||||
},
|
||||
"mails": [{
|
||||
"from": "sender1@example.com",
|
||||
"to": "receiver1@example.com
|
||||
|
||||
58
mail.go
58
mail.go
@ -4,7 +4,10 @@ import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type unencryptedAuth struct {
|
||||
@ -76,7 +79,12 @@ func sendMail(from, to, subject, body string, c *smtp.Client) error {
|
||||
// Setup headers
|
||||
headers := make(map[string]string)
|
||||
headers["From"] = from
|
||||
headers["To"] = to
|
||||
recipients := strings.Split(to, ",")
|
||||
recipientsStr := make([]string, 0)
|
||||
for i := range recipients {
|
||||
recipientsStr = append(recipientsStr, fmt.Sprintf("<%s>", recipients[i]))
|
||||
}
|
||||
headers["To"] = strings.Join(recipientsStr, ",")
|
||||
headers["Subject"] = subject
|
||||
|
||||
// Setup message
|
||||
@ -91,7 +99,7 @@ func sendMail(from, to, subject, body string, c *smtp.Client) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Rcpt(to); err != nil {
|
||||
if err := c.Rcpt(strings.Join(recipientsStr, ",")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -113,3 +121,49 @@ func sendMail(from, to, subject, body string, c *smtp.Client) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type mailCtx struct {
|
||||
NewChunks uint64
|
||||
ReusedChunks uint64
|
||||
Datastore string
|
||||
Error error
|
||||
Hostname string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
func (m *mailCtx) Duration() time.Duration {
|
||||
return m.EndTime.Sub(m.StartTime)
|
||||
}
|
||||
|
||||
func (m *mailCtx) FromattedDuration() string {
|
||||
return m.Duration().String()
|
||||
}
|
||||
|
||||
func (m *mailCtx) ErrorStr() string {
|
||||
if m.Error != nil {
|
||||
return m.Error.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *mailCtx) Success() bool {
|
||||
return m.Error == nil
|
||||
}
|
||||
|
||||
func (m *mailCtx) Status() string {
|
||||
if m.Success() {
|
||||
return "Success"
|
||||
}
|
||||
return "Failed"
|
||||
}
|
||||
|
||||
func (m *mailCtx) buildStr(txt string) (string, error) {
|
||||
tmpl, err := template.New("mail").Parse(txt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
strBuff := &strings.Builder{}
|
||||
err = tmpl.Execute(strBuff, m)
|
||||
return strBuff.String(), err
|
||||
}
|
||||
|
||||
62
main.go
62
main.go
@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cornelk/hashmap"
|
||||
"github.com/gen2brain/beeep"
|
||||
@ -18,6 +19,11 @@ import (
|
||||
"github.com/tawesoft/golib/v2/dialog"
|
||||
)
|
||||
|
||||
var defaultMailSubjectTemplate = "Backup {{.Status}}"
|
||||
var defaultMailBodyTemplate = `{{if .Success}}Backup complete ({{.FromattedDuration}})
|
||||
Chunks New {{.NewChunks}}, Reused {{.ReusedChunks}}.{{else}}Error occurred while working, backup may be not completed.
|
||||
Last error is: {{.ErrorStr}}{{end}}`
|
||||
|
||||
var didxMagic = []byte{28, 145, 78, 165, 25, 186, 179, 205}
|
||||
|
||||
type ChunkState struct {
|
||||
@ -94,15 +100,41 @@ func main() {
|
||||
BackupID: cfg.BackupID,
|
||||
},
|
||||
}
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to retrieve hostname:", err)
|
||||
hostname = "unknown"
|
||||
}
|
||||
|
||||
err := backup(client, newchunk, reusechunk, cfg.PxarOut, cfg.BackupSourceDir)
|
||||
begin := time.Now()
|
||||
err = backup(client, newchunk, reusechunk, cfg.PxarOut, cfg.BackupSourceDir)
|
||||
end := time.Now()
|
||||
|
||||
fmt.Printf("New %d , Reused %d\n", newchunk.Load(), reusechunk.Load())
|
||||
mailCtx := mailCtx{
|
||||
NewChunks: newchunk.Load(),
|
||||
ReusedChunks: reusechunk.Load(),
|
||||
Error: err,
|
||||
Hostname: hostname,
|
||||
Datastore: cfg.Datastore,
|
||||
StartTime: begin,
|
||||
EndTime: end,
|
||||
}
|
||||
|
||||
mailBodyTemplate := defaultMailBodyTemplate
|
||||
if cfg.SMTP != nil && cfg.SMTP.Template != nil && cfg.SMTP.Template.Body != "" {
|
||||
mailBodyTemplate = cfg.SMTP.Template.Body
|
||||
}
|
||||
|
||||
fmt.Printf("New %d, Reused %d, backup took %s.\n", newchunk.Load(), reusechunk.Load(), end.Sub(begin))
|
||||
var msg string
|
||||
if err == nil {
|
||||
msg = fmt.Sprintf("Backup complete\nChunks New %d , Reused %d\n", newchunk.Load(), reusechunk.Load())
|
||||
} else {
|
||||
msg = fmt.Sprintf("Error occurred while working, backup may be not completed.\nLast error is: %s\n", err.Error())
|
||||
msg, err = mailCtx.buildStr(mailBodyTemplate)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot use custom mail body: " + err.Error())
|
||||
msg, err = mailCtx.buildStr(defaultMailBodyTemplate)
|
||||
if err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
systray.Quit()
|
||||
@ -110,10 +142,20 @@ func main() {
|
||||
}
|
||||
if cfg.SMTP != nil {
|
||||
var subject string
|
||||
if err == nil {
|
||||
subject = "Backup complete"
|
||||
} else {
|
||||
subject = "Backup error"
|
||||
|
||||
mailSubjectTemplate := defaultMailSubjectTemplate
|
||||
if cfg.SMTP.Template != nil && cfg.SMTP.Template.Subject != "" {
|
||||
mailSubjectTemplate = cfg.SMTP.Template.Subject
|
||||
}
|
||||
|
||||
subject, err = mailCtx.buildStr(mailSubjectTemplate)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot use custom mail subject: " + err.Error())
|
||||
msg, err = mailCtx.buildStr(defaultMailSubjectTemplate)
|
||||
if err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
client, err := setupClient(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.Username, cfg.SMTP.Password, cfg.SMTP.Insecure)
|
||||
if err != nil {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user