mirror of
https://github.com/offen/docker-volume-backup.git
synced 2024-11-10 00:30:29 +01:00
131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
// Copyright 2022 - Offen Authors <hioffen@posteo.de>
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"text/template"
|
|
"time"
|
|
|
|
sTypes "github.com/containrrr/shoutrrr/pkg/types"
|
|
"github.com/offen/docker-volume-backup/internal/errwrap"
|
|
)
|
|
|
|
//go:embed notifications.tmpl
|
|
var defaultNotifications string
|
|
|
|
// NotificationData data to be passed to the notification templates
|
|
type NotificationData struct {
|
|
Error error
|
|
Config *Config
|
|
Stats *Stats
|
|
}
|
|
|
|
// notify sends a notification using the given title and body templates.
|
|
// Automatically creates notification data, adding the given error
|
|
func (s *script) notify(titleTemplate string, bodyTemplate string, err error) error {
|
|
params := NotificationData{
|
|
Error: err,
|
|
Stats: s.stats,
|
|
Config: s.c,
|
|
}
|
|
|
|
titleBuf := &bytes.Buffer{}
|
|
if err := s.template.ExecuteTemplate(titleBuf, titleTemplate, params); err != nil {
|
|
return errwrap.Wrap(err, fmt.Sprintf("error executing %s template", titleTemplate))
|
|
}
|
|
|
|
bodyBuf := &bytes.Buffer{}
|
|
if err := s.template.ExecuteTemplate(bodyBuf, bodyTemplate, params); err != nil {
|
|
return errwrap.Wrap(err, fmt.Sprintf("error executing %s template", bodyTemplate))
|
|
}
|
|
|
|
if err := s.sendNotification(titleBuf.String(), bodyBuf.String()); err != nil {
|
|
return errwrap.Wrap(err, "error sending notification")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// notifyFailure sends a notification about a failed backup run
|
|
func (s *script) notifyFailure(err error) error {
|
|
return s.notify("title_failure", "body_failure", err)
|
|
}
|
|
|
|
// notifyFailure sends a notification about a successful backup run
|
|
func (s *script) notifySuccess() error {
|
|
return s.notify("title_success", "body_success", nil)
|
|
}
|
|
|
|
// sendNotification sends a notification to all configured third party services
|
|
func (s *script) sendNotification(title, body string) error {
|
|
var errs []error
|
|
for _, result := range s.sender.Send(body, &sTypes.Params{"title": title}) {
|
|
if result != nil {
|
|
errs = append(errs, result)
|
|
}
|
|
}
|
|
if len(errs) != 0 {
|
|
return errwrap.Wrap(errors.Join(errs...), "error sending message")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var templateHelpers = template.FuncMap{
|
|
"formatTime": func(t time.Time) string {
|
|
return t.Format(time.RFC3339)
|
|
},
|
|
"formatBytesDec": func(bytes uint64) string {
|
|
return formatBytes(bytes, true)
|
|
},
|
|
"formatBytesBin": func(bytes uint64) string {
|
|
return formatBytes(bytes, false)
|
|
},
|
|
"env": os.Getenv,
|
|
"toJson": toJson,
|
|
"toPrettyJson": toPrettyJson,
|
|
}
|
|
|
|
// formatBytes converts an amount of bytes in a human-readable representation
|
|
// the decimal parameter specifies if using powers of 1000 (decimal) or powers of 1024 (binary)
|
|
func formatBytes(b uint64, decimal bool) string {
|
|
unit := uint64(1024)
|
|
format := "%.1f %ciB"
|
|
if decimal {
|
|
unit = uint64(1000)
|
|
format = "%.1f %cB"
|
|
}
|
|
if b < unit {
|
|
return fmt.Sprintf("%d B", b)
|
|
}
|
|
div, exp := unit, 0
|
|
for n := b / unit; n >= unit; n /= unit {
|
|
div *= unit
|
|
exp++
|
|
}
|
|
return fmt.Sprintf(format, float64(b)/float64(div), "kMGTPE"[exp])
|
|
}
|
|
|
|
func toJson(v interface{}) string {
|
|
var bytes []byte
|
|
var err error
|
|
if bytes, err = json.Marshal(v); err != nil {
|
|
return fmt.Sprintf("failed to marshal JSON in notification template: %v", err)
|
|
}
|
|
return string(bytes)
|
|
}
|
|
|
|
func toPrettyJson(v interface{}) string {
|
|
var bytes []byte
|
|
var err error
|
|
if bytes, err = json.MarshalIndent(v, "", " "); err != nil {
|
|
return fmt.Sprintf("failed to marshal indent JSON in notification template: %v", err)
|
|
}
|
|
return string(bytes)
|
|
}
|