package sender import ( "bytes" "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "io" "net/http" "time" "rocketgit.ru/rsmon/worker/app/models" "rocketgit.ru/rsmon/worker/config/application" ) const envProduction = "production" // RunWebhook provides functionality. func RunWebhook(message *models.Message) (*string, error) { if application.Env != envProduction { return nil, errors.New("not sending in env " + application.Env) } body, err := json.Marshal(message) if err != nil { return nil, err } resp, err := httpClient.Post( message.Contact.Value, "application/json", bytes.NewBuffer(body), ) if err != nil { return nil, err } defer resp.Body.Close() //nolint:errcheck body, err = io.ReadAll(resp.Body) if err != nil { return nil, err } str := string(body) return &str, nil } // SendWebhookWithCredential delivers a webhook notification using the // provided wire credential block. Phase 1 ships an empty signing secret; // phase 4 will plumb the per-account secret through Contact.Data. The // signature header is X-RSMon-Signature (hex-encoded HMAC-SHA256 of the // body), matching what webhook consumers in the existing fleet expect. // // The payload matches the wire.NotificationTask shape so customers // receiving the legacy Message JSON see one fewer breaking change. func SendWebhookWithCredential(payload []byte, contactValue, signingSecret string) (*string, error) { return SendWebhookWithCredentialContext(context.Background(), payload, contactValue, signingSecret) } func SendWebhookWithCredentialContext(ctx context.Context, payload []byte, contactValue, signingSecret string) (*string, error) { if application.Env != envProduction { return nil, errors.New("not sending in env " + application.Env) } if contactValue == "" { return nil, errors.New("webhook contact value is empty") } req, err := http.NewRequestWithContext(ctx, http.MethodPost, contactValue, bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "rsmon-worker/1") if signingSecret != "" { mac := hmac.New(sha256.New, []byte(signingSecret)) mac.Write(payload) req.Header.Set("X-RSMon-Signature", "sha256="+hex.EncodeToString(mac.Sum(nil))) } client := &http.Client{Timeout: 60 * time.Second} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() //nolint:errcheck respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode >= 500 { return nil, errors.New("webhook upstream " + resp.Status) } str := string(respBody) return &str, nil }