Files
worker/internal/sender/webhook.go
Gleb Tv 2c884c5612
Некоторые проверки не удались
CI / test (push) Successful in 2m5s
Docker / Build and publish worker image (push) Failing after 31s
refactor: adopt worker module path
2026-07-13 17:56:12 +03:00

100 строки
2.6 KiB
Go

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
}