package main import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" "net/http" "time" ) const dateLayout = "2006-01-02 15:04:05" type silence struct { Matchers []matcher `json:"matchers"` StartsAt string `json:"startsAt"` EndsAt string `json:"endsAt"` CreatedBy string `json:"createdBy"` Comment string `json:"comment"` } type matcher struct { Name string `json:"name"` Value string `json:"value"` IsRegex bool `json:"isRegex"` IsEqual bool `json:"isEqual"` } type silenceBody struct { AlertManagerURL string `json:"alertmanagerURL"` Labels map[string]string `json:"labels"` } type silenceResponse struct { ID string `json:"silenceID"` } func (rcv *receiver) handleSilences(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.Method != http.MethodPost { http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed) rcv.logger.Debugf("silences: illegal HTTP method: expected %q, got %q", "POST", r.Method) return } b, err := io.ReadAll(r.Body) if err != nil { rcv.logger.Debugf("silences: %v", err) return } b, err = base64.StdEncoding.DecodeString(string(b)) if err != nil { rcv.logger.Debugf("silences: %v", err) return } var sb silenceBody err = json.Unmarshal(b, &sb) if err != nil { rcv.logger.Debugf("silences: %v", err) return } var matchers []matcher for key, value := range sb.Labels { m := matcher{ Name: key, Value: value, IsRegex: false, IsEqual: true, } matchers = append(matchers, m) } silence := &silence{ StartsAt: time.Now().UTC().Format(dateLayout), EndsAt: time.Now().Add(rcv.cfg.am.SilenceDuration).UTC().Format(dateLayout), CreatedBy: "ntfy-alertmanager", Comment: "", Matchers: matchers, } b, err = json.Marshal(silence) if err != nil { rcv.logger.Debugf("silences: %v", err) return } client := &http.Client{Timeout: time.Second * 3} url := sb.AlertManagerURL if rcv.cfg.am.URL != "" { url = rcv.cfg.am.URL } url += "/api/v2/silences" req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(b)) if err != nil { rcv.logger.Debugf("silences: %v", err) return } // Basic auth if rcv.cfg.am.User != "" && rcv.cfg.am.Password != "" { auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", rcv.cfg.am.User, rcv.cfg.am.Password))) req.Header.Set("Authorization", fmt.Sprintf("Basic %s", auth)) } req.Header.Add("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { rcv.logger.Debugf("silences: %v", err) return } defer resp.Body.Close() b, err = io.ReadAll(resp.Body) if err != nil { rcv.logger.Debugf("silences: %v", err) return } if resp.StatusCode != http.StatusOK { rcv.logger.Debugf("silences: received status code %d", resp.StatusCode) return } var id silenceResponse if err := json.Unmarshal(b, &id); err != nil { rcv.logger.Debugf("silences: %v", err) return } rcv.logger.Debugf("silences: created new silence %s", id.ID) }