package notify import ( "bytes" "context" "encoding/json" "fmt" "net/http" "net/url" "os/exec" "strings" "time" "github.com/backupper-unraid/backupper/internal/model" ) type SecretGetter interface{ Get(string) (string, error) } type Sender struct { Secrets SecretGetter Client *http.Client NotifyPath string RunCommand func(context.Context, string, ...string) error } func (s *Sender) Send(ctx context.Context, target model.NotificationTarget, title, message, severity string) error { if !target.Enabled { return nil } switch target.Type { case "unraid": importance := "normal" if severity == "failed" || severity == "error" { importance = "alert" } else if severity == "warning" { importance = "warning" } path := s.NotifyPath if path == "" { path = "/usr/local/emhttp/webGui/scripts/notify" } args := []string{"-e", "URBM", "-s", title, "-d", message, "-i", importance, "-l", "/Tools/Backupper"} if s.RunCommand != nil { return s.RunCommand(ctx, path, args...) } return exec.CommandContext(ctx, path, args...).Run() case "gotify": token, err := s.Secrets.Get(target.TokenRef) if err != nil { return err } endpoint, err := url.Parse(strings.TrimRight(target.URL, "/") + "/message") if err != nil { return err } query := endpoint.Query() query.Set("token", token) endpoint.RawQuery = query.Encode() priority := 2 if severity == "warning" { priority = 5 } else if severity == "failed" || severity == "error" { priority = 8 } payload, err := json.Marshal(map[string]any{"title": title, "message": message, "priority": priority}) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), bytes.NewReader(payload)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") client := s.Client if client == nil { client = &http.Client{Timeout: 15 * time.Second} } response, err := client.Do(req) if err != nil { return err } defer response.Body.Close() if response.StatusCode < 200 || response.StatusCode >= 300 { return fmt.Errorf("Gotify returned %s", response.Status) } return nil default: return fmt.Errorf("unsupported notification type %q", target.Type) } }