diff --git a/.gitignore b/.gitignore index 7ef77d321a..9ef98b5e4a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,13 @@ *.yml *.yaml /.build -/.release +/release /.tarballs /vendor +/.idea +/release +/.idea + !.golangci.yml !/cli/testdata/*.yml diff --git a/compressing b/compressing new file mode 100644 index 0000000000..330f0b69c7 --- /dev/null +++ b/compressing @@ -0,0 +1 @@ +' assets' diff --git a/config/config.go b/config/config.go index be6c5adbf5..6e9a38173b 100644 --- a/config/config.go +++ b/config/config.go @@ -280,6 +280,12 @@ func resolveFilepaths(baseDir string, cfg *Config) { for _, cfg := range receiver.TelegramConfigs { cfg.HTTPConfig.SetDirectory(baseDir) } + for _, cfg := range receiver.ZeusTelegramConfigs { + cfg.HTTPConfig.SetDirectory(baseDir) + } + for _, cfg := range receiver.ZeusEmailConfigs { + cfg.HTTPConfig.SetDirectory(baseDir) + } for _, cfg := range receiver.DiscordConfigs { cfg.HTTPConfig.SetDirectory(baseDir) } @@ -563,6 +569,22 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { telegram.APIUrl = c.Global.TelegramAPIUrl } } + for _, zeusTelegram := range rcv.ZeusTelegramConfigs { + if zeusTelegram.HTTPConfig == nil { + zeusTelegram.HTTPConfig = c.Global.HTTPConfig + } + if zeusTelegram.APIURL == nil { + zeusTelegram.APIURL = c.Global.ZeusTelegramAPIUrl + } + } + for _, zeusEmail := range rcv.ZeusEmailConfigs { + if zeusEmail.HTTPConfig == nil { + zeusEmail.HTTPConfig = c.Global.HTTPConfig + } + if zeusEmail.APIURL == nil { + zeusEmail.APIURL = c.Global.ZeusEmailAPIUrl + } + } for _, discord := range rcv.DiscordConfigs { if discord.HTTPConfig == nil { discord.HTTPConfig = c.Global.HTTPConfig @@ -724,18 +746,20 @@ func DefaultGlobalConfig() GlobalConfig { defaultSMTPTLSConfig := commoncfg.TLSConfig{} return GlobalConfig{ - ResolveTimeout: model.Duration(5 * time.Minute), - HTTPConfig: &defaultHTTPConfig, - SMTPHello: "localhost", - SMTPRequireTLS: true, - SMTPTLSConfig: &defaultSMTPTLSConfig, - PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), - OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"), - WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), - VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), - TelegramAPIUrl: mustParseURL("https://api.telegram.org"), - WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), - RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"), + ResolveTimeout: model.Duration(5 * time.Minute), + HTTPConfig: &defaultHTTPConfig, + SMTPHello: "localhost", + SMTPRequireTLS: true, + SMTPTLSConfig: &defaultSMTPTLSConfig, + PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), + OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"), + WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), + VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), + TelegramAPIUrl: mustParseURL("https://api.telegram.org"), + ZeusTelegramAPIUrl: mustParseURL(getZeusApiUrl() + "/zeus/telegram/message/"), + ZeusEmailAPIUrl: mustParseURL(getZeusApiUrl() + "/zeus/mail/message/"), + WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), + RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"), } } @@ -828,6 +852,14 @@ func (hp HostPort) String() string { return fmt.Sprintf("%s:%s", hp.Host, hp.Port) } +func getZeusApiUrl() string { + if os.Getenv("ZEUS_API_URL") == "" { + return "http://localhost:8080" + } else { + return os.Getenv("ZEUS_API_URL") + } +} + // GlobalConfig defines configuration parameters that are valid globally // unless overwritten. type GlobalConfig struct { @@ -861,6 +893,8 @@ type GlobalConfig struct { VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` + ZeusTelegramAPIUrl *URL `yaml:"zeus_telegram_api_url,omitempty" json:"zeus_telegram_api_url,omitempty"` + ZeusEmailAPIUrl *URL `yaml:"zeus_email_api_url,omitempty" json:"zeus_email_api_url,omitempty"` WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` RocketchatAPIURL *URL `yaml:"rocketchat_api_url,omitempty" json:"rocketchat_api_url,omitempty"` RocketchatToken *Secret `yaml:"rocketchat_token,omitempty" json:"rocketchat_token,omitempty"` @@ -1005,22 +1039,24 @@ type Receiver struct { // A unique identifier for this receiver. Name string `yaml:"name" json:"name"` - DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"` - EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` - PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` - SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` - WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` - OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` - WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` - PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` - VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` - SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` - TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` - WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` - MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"` - MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` - JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"` - RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"` + DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"` + EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` + PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` + SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` + WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` + OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` + WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` + PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` + VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` + SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` + TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` + ZeusTelegramConfigs []*ZeusTelegramConfig `yaml:"zeus_telegram_configs,omitempty" json:"zeus_telegram_configs,omitempty"` + ZeusEmailConfigs []*ZeusEmailConfig `yaml:"zeus_email_configs,omitempty" json:"zeus_email_configs,omitempty"` + WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` + MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"` + MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` + JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"` + RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver. diff --git a/config/notifiers.go b/config/notifiers.go index 71e404e367..1eefdd13c8 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -178,6 +178,21 @@ var ( ParseMode: "HTML", } + DefaultZeusTelegramConfig = ZeusTelegramConfig{ + NotifierConfig: NotifierConfig{ + VSendResolved: true, + }, + Text: `{{ template "telegram.default.message" . }}`, + ParseMode: "HTML", + } + + DefaultZeusEmailConfig = ZeusEmailConfig{ + NotifierConfig: NotifierConfig{ + VSendResolved: false, + }, + Message: `{{ template "email.default.html" . }}`, + } + DefaultMSTeamsConfig = MSTeamsConfig{ NotifierConfig: NotifierConfig{ VSendResolved: true, @@ -839,6 +854,84 @@ func (c *TelegramConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } +type ZeusTelegramConfig struct { + NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + SensitiveData []string `yaml:"sensitive_data,omitempty" json:"sensitive_data,omitempty"` + SensitiveDataRegexPattern string `yaml:"sensitive_data_regex_pattern" json:"sensitive_data_regex_pattern,omitempty"` + EventID string `yaml:"event_id" json:"event_id,omitempty"` + EventStatus string `yaml:"event_status" json:"event_status,omitempty"` + Severity string `yaml:"severity" json:"severity,omitempty"` + Sender string `yaml:"sender" json:"sender,omitempty"` + APIURL *URL `yaml:"api_url" json:"api_url,omitempty"` + BotToken string `yaml:"bot_token,omitempty" json:"token,omitempty"` + ChatID int64 `yaml:"chat_id,omitempty" json:"chat,omitempty"` + Subject string `yaml:"subject,omitempty" json:"subject,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"` +} + +func (c *ZeusTelegramConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultZeusTelegramConfig + type plain ZeusTelegramConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + if c.BotToken == "" { + return errors.New("missing bot_token on zeus_telegram_config") + } + if c.ChatID == 0 { + return errors.New("missing chat_id on zeus_telegram_config") + } + if c.Text == "" { + return errors.New("missing text on zeus_telegram_config") + } + if c.Sender == "" { + return errors.New("missing sender on zeus_telegram_config") + } + if c.ParseMode != "" && + c.ParseMode != "Markdown" && + c.ParseMode != "MarkdownV2" && + c.ParseMode != "HTML" { + return errors.New("unknown parse_mode on zeus_telegram_config, must be Markdown, MarkdownV2, HTML or empty string") + } + return nil +} + +type ZeusEmailConfig struct { + NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + SensitiveData []string `yaml:"sensitive_data,omitempty" json:"sensitive_data,omitempty"` + SensitiveDataRegexPattern string `yaml:"sensitive_data_regex_pattern" json:"sensitive_data_regex_pattern,omitempty"` + EventID string `yaml:"event_id" json:"event_id,omitempty"` + EventStatus string `yaml:"event_status" json:"event_status,omitempty"` + Severity string `yaml:"severity" json:"severity,omitempty"` + Sender string `yaml:"sender" json:"sender,omitempty"` + APIURL *URL `yaml:"api_url" json:"api_url,omitempty"` + Recipients []string `yaml:"recipients" json:"recipients,omitempty"` + Subject string `yaml:"subject,omitempty" json:"subject,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` +} + +func (c *ZeusEmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultZeusEmailConfig + type plain ZeusEmailConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + if c.Message == "" { + return errors.New("missing message on zeus_email_config") + } + if c.Sender == "" { + return errors.New("missing sender on zeus_email_config") + } + return nil +} + type MSTeamsConfig struct { NotifierConfig `yaml:",inline" json:",inline"` HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` diff --git a/config/receiver/receiver.go b/config/receiver/receiver.go index d92a19a4c5..0ed6012ee9 100644 --- a/config/receiver/receiver.go +++ b/config/receiver/receiver.go @@ -14,6 +14,7 @@ package receiver import ( + "github.com/prometheus/alertmanager/notify/zeusemail" "log/slog" commoncfg "github.com/prometheus/common/config" @@ -37,6 +38,7 @@ import ( "github.com/prometheus/alertmanager/notify/webex" "github.com/prometheus/alertmanager/notify/webhook" "github.com/prometheus/alertmanager/notify/wechat" + "github.com/prometheus/alertmanager/notify/zeustelegram" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" ) @@ -91,6 +93,12 @@ func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logg for i, c := range nc.TelegramConfigs { add("telegram", i, c, func(l *slog.Logger) (notify.Notifier, error) { return telegram.New(c, tmpl, l, httpOpts...) }) } + for i, c := range nc.ZeusTelegramConfigs { + add("zeustelegram", i, c, func(l *slog.Logger) (notify.Notifier, error) { return zeustelegram.New(c, tmpl, l, httpOpts...) }) + } + for i, c := range nc.ZeusEmailConfigs { + add("zeusemail", i, c, func(l *slog.Logger) (notify.Notifier, error) { return zeusemail.New(c, tmpl, l, httpOpts...) }) + } for i, c := range nc.DiscordConfigs { add("discord", i, c, func(l *slog.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l, httpOpts...) }) } diff --git a/notify/msteams/msteams.go b/notify/msteams/msteams.go index f53f020b3b..648ca549eb 100644 --- a/notify/msteams/msteams.go +++ b/notify/msteams/msteams.go @@ -84,9 +84,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) if err != nil { return false, err } - n.logger.Debug("extracted group key", "key", key) - data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) tmpl := notify.TmplText(n.tmpl, data, &err) if err != nil { diff --git a/notify/notify.go b/notify/notify.go index 3973e7876b..0670196ac2 100644 --- a/notify/notify.go +++ b/notify/notify.go @@ -361,6 +361,8 @@ func (m *Metrics) InitializeFor(receiver map[string][]Integration) { "victorops", "sns", "telegram", + "zeustelegram", + "zeusemail", "discord", "webex", "msteams", diff --git a/notify/telegram/telegram.go b/notify/telegram/telegram.go index 4c8d0c0e80..37da3ed0b7 100644 --- a/notify/telegram/telegram.go +++ b/notify/telegram/telegram.go @@ -73,7 +73,6 @@ func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, err if n.conf.ParseMode == "HTML" { tmpl = notify.TmplHTML(n.tmpl, data, &err) } - key, ok := notify.GroupKey(ctx) if !ok { return false, fmt.Errorf("group key missing") diff --git a/notify/zeusemail/zeusemail.go b/notify/zeusemail/zeusemail.go new file mode 100644 index 0000000000..dc14ca3ebd --- /dev/null +++ b/notify/zeusemail/zeusemail.go @@ -0,0 +1,135 @@ +// Copyright 2022 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zeusemail + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" + commoncfg "github.com/prometheus/common/config" +) + +// Notifier implements a Notifier for telegram notifications. +type Notifier struct { + conf *config.ZeusEmailConfig + tmpl *template.Template + logger *slog.Logger + client *http.Client + retrier *notify.Retrier + postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) +} + +// New returns a new ZeusTelegram notification handler. +func New(conf *config.ZeusEmailConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + client, err := commoncfg.NewClientFromConfig(*conf.HTTPConfig, "zeustelegram", httpOpts...) + if err != nil { + return nil, err + } + + return &Notifier{ + conf: conf, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{}, + postJSONFunc: notify.PostJSON, + }, nil +} + +type zeusEmailMessage struct { + SensitiveData []string `yaml:"sensitive_data,omitempty"` + SensitiveDataRegexPattern string `yaml:"sensitive_data_regex_pattern"` + EventID string `yaml:"event_id"` + EventStatus string `yaml:"event_status"` + Severity string `yaml:"severity"` + Sender string `yaml:"sender"` + Recipients []string `yaml:"recipients"` + Subject string `yaml:"subject,omitempty"` + Message string `yaml:"message,omitempty"` +} + +func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) { + key, err := notify.ExtractGroupKey(ctx) + if err != nil { + return false, err + } + n.logger.Debug("Extracted group key", "key", key) + data := notify.GetTemplateData(ctx, n.tmpl, alert, n.logger) + tmpl := notify.TmplHTML(n.tmpl, data, &err) + var ( + apiURL = strings.TrimSpace(tmpl(n.conf.APIURL.String())) + sensitiveData = n.conf.SensitiveData + sensitiveDataRegexPattern = tmpl(n.conf.SensitiveDataRegexPattern) + eventID = tmpl(n.conf.EventID) + eventStatus = tmpl(n.conf.EventStatus) + severity = tmpl(n.conf.Severity) + sender = tmpl(n.conf.Sender) + recipients = n.conf.Recipients + subject = tmpl(n.conf.Subject) + message = tmpl(n.conf.Message) + ) + zeusEmailMessageBody := zeusEmailMessage{ + SensitiveData: sensitiveData, + SensitiveDataRegexPattern: sensitiveDataRegexPattern, + EventID: eventID, + EventStatus: eventStatus, + Severity: severity, + Sender: sender, + Recipients: recipients, + Subject: subject, + Message: message, + } + var bodyAsBuffers bytes.Buffer + if err = json.NewEncoder(&bodyAsBuffers).Encode(zeusEmailMessageBody); err != nil { + return false, err + } + response, err := n.postJSONFunc(ctx, n.client, apiURL, &bodyAsBuffers) + if err != nil { + return true, notify.RedactURL(err) + } + responseBody, err := getResponseBodyAsString(response) + if err != nil { + return true, notify.RedactURL(err) + } + if response.StatusCode/100 != 2 { + shouldRetry, err := n.retrier.Check(response.StatusCode, nil) + if err != nil { + return true, err + } + return shouldRetry, fmt.Errorf("unexpected status code %d: %s", response.StatusCode, responseBody) + } + n.logger.Debug("ZeusEmail response: " + responseBody) + defer notify.Drain(response) + return false, err +} + +func getResponseBodyAsString(resp *http.Response) (string, error) { + var buf bytes.Buffer + _, err := io.Copy(&buf, resp.Body) + if err != nil { + return "", err + } + defer resp.Body.Close() + return buf.String(), nil +} diff --git a/notify/zeustelegram/zeustelegram.go b/notify/zeustelegram/zeustelegram.go new file mode 100644 index 0000000000..e092b6f873 --- /dev/null +++ b/notify/zeustelegram/zeustelegram.go @@ -0,0 +1,144 @@ +// Copyright 2022 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zeustelegram + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" + commoncfg "github.com/prometheus/common/config" +) + +// Notifier implements a Notifier for telegram notifications. +type Notifier struct { + conf *config.ZeusTelegramConfig + tmpl *template.Template + logger *slog.Logger + client *http.Client + retrier *notify.Retrier + postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) +} + +// New returns a new ZeusTelegram notification handler. +func New(conf *config.ZeusTelegramConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + client, err := commoncfg.NewClientFromConfig(*conf.HTTPConfig, "zeustelegram", httpOpts...) + if err != nil { + return nil, err + } + + return &Notifier{ + conf: conf, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{}, + postJSONFunc: notify.PostJSON, + }, nil +} + +type zeusTelegramMessage struct { + SensitiveData []string `json:"sensitive_data"` + SensitiveDataRegexPattern string `json:"sensitive_data_regex_pattern"` + EventID string `json:"event_id"` + EventStatus string `json:"event_status"` + Severity string `json:"severity"` + Sender string `json:"sender"` + BotToken string `json:"bot_token"` + ChatID int64 `json:"chat_id"` + Subject string `json:"subject"` + Text string `json:"text"` + ParseMode string `json:"parse_mode"` +} + +func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) { + key, err := notify.ExtractGroupKey(ctx) + if err != nil { + return false, err + } + n.logger.Debug("Extracted group key", "key", key) + data := notify.GetTemplateData(ctx, n.tmpl, alert, n.logger) + tmpl := notify.TmplText(n.tmpl, data, &err) + if n.conf.ParseMode == "HTML" { + tmpl = notify.TmplHTML(n.tmpl, data, &err) + } + var ( + apiURL = strings.TrimSpace(tmpl(n.conf.APIURL.String())) + sensitiveData = n.conf.SensitiveData + sensitiveDataRegexPattern = tmpl(n.conf.SensitiveDataRegexPattern) + eventID = tmpl(n.conf.EventID) + eventStatus = tmpl(n.conf.EventStatus) + severity = tmpl(n.conf.Severity) + sender = tmpl(n.conf.Sender) + botToken = tmpl(n.conf.BotToken) + chatID = n.conf.ChatID + subject = tmpl(n.conf.Subject) + text = tmpl(n.conf.Text) + parseMode = tmpl(n.conf.ParseMode) + ) + zeusTelegramMessageBody := zeusTelegramMessage{ + SensitiveData: sensitiveData, + SensitiveDataRegexPattern: sensitiveDataRegexPattern, + EventID: eventID, + EventStatus: eventStatus, + Severity: severity, + Sender: sender, + BotToken: botToken, + ChatID: chatID, + Subject: subject, + Text: text, + ParseMode: parseMode, + } + var bodyAsBuffers bytes.Buffer + if err = json.NewEncoder(&bodyAsBuffers).Encode(zeusTelegramMessageBody); err != nil { + return false, err + } + response, err := n.postJSONFunc(ctx, n.client, apiURL, &bodyAsBuffers) + if err != nil { + return true, notify.RedactURL(err) + } + responseBody, err := getResponseBodyAsString(response) + if err != nil { + return true, notify.RedactURL(err) + } + if response.StatusCode/100 != 2 { + shouldRetry, err := n.retrier.Check(response.StatusCode, nil) + if err != nil { + return true, err + } + return shouldRetry, fmt.Errorf("unexpected status code %d: %s", response.StatusCode, responseBody) + } + n.logger.Debug("ZeusTelegram response: " + responseBody) + defer notify.Drain(response) + return false, err +} + +func getResponseBodyAsString(resp *http.Response) (string, error) { + var buf bytes.Buffer + _, err := io.Copy(&buf, resp.Body) + if err != nil { + return "", err + } + defer resp.Body.Close() + return buf.String(), nil +} diff --git a/ui/node_modules/.yarn-integrity b/ui/node_modules/.yarn-integrity new file mode 100644 index 0000000000..56c28ed948 --- /dev/null +++ b/ui/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "win32-x64-108", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/ui/react-app/src/index.tsx b/ui/react-app/src/index.tsx index 1611c331fb..c084e3a2e2 100644 --- a/ui/react-app/src/index.tsx +++ b/ui/react-app/src/index.tsx @@ -1,10 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import App from './App'; import { BrowserRouter } from 'react-router-dom'; import { QueryParamProvider } from 'use-query-params'; import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import App from './App'; function renderApp(container: Element | null) { if (container === null) { diff --git a/ui/yarn.lock b/ui/yarn.lock new file mode 100644 index 0000000000..fb57ccd13a --- /dev/null +++ b/ui/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +