123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- package hook
- import (
- "bytes"
- "crypto/hmac"
- "crypto/sha256"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "net/http"
- "time"
- )
- type HookClient struct {
- BaseURL string
- Domain string
- SecretKey string
- HTTPClient *http.Client
- }
- type HookPayload struct {
- Email string `json:"email"`
- Domain string `json:"domain"`
- Action string `json:"action"`
- Timestamp time.Time `json:"timestamp"`
- }
- func NewHookClient(baseURL, domain, secretKey string) *HookClient {
- return &HookClient{
- BaseURL: baseURL,
- Domain: domain,
- SecretKey: secretKey,
- HTTPClient: &http.Client{
- Timeout: 10 * time.Second,
- },
- }
- }
- func (c *HookClient) SendWebhook(email, action string) error {
- payload := HookPayload{
- Email: email,
- Domain: c.Domain,
- Action: action,
- Timestamp: time.Now(),
- }
- payloadBytes, err := json.Marshal(payload)
- if err != nil {
- return fmt.Errorf("erreur de marshalling: %w", err)
- }
- // Calcul de la signature
- signature := c.calculateHMAC(payloadBytes)
- // Création de la requête
- req, err := http.NewRequest(
- "POST",
- fmt.Sprintf("%s/webhook/user-sync", c.BaseURL),
- bytes.NewBuffer(payloadBytes),
- )
- if err != nil {
- return fmt.Errorf("erreur de création de requête: %w", err)
- }
- // Ajout des headers
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("X-Webhook-Signature", signature)
- req.Header.Set("X-Webhook-Domain", c.Domain)
- // Envoi de la requête avec retry
- var lastErr error
- for retry := 0; retry < 3; retry++ {
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- lastErr = err
- time.Sleep(time.Duration(retry+1) * time.Second)
- continue
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- return nil
- }
- if resp.StatusCode == http.StatusTooManyRequests {
- // Attendre avant de réessayer
- time.Sleep(time.Duration(retry+1) * time.Second)
- continue
- }
- // Erreur définitive
- if resp.StatusCode >= 400 && resp.StatusCode != http.StatusTooManyRequests {
- return fmt.Errorf("erreur HTTP %d", resp.StatusCode)
- }
- }
- return fmt.Errorf("échec après 3 tentatives: %w", lastErr)
- }
- func (c *HookClient) calculateHMAC(message []byte) string {
- mac := hmac.New(sha256.New, []byte(c.SecretKey))
- mac.Write(message)
- return hex.EncodeToString(mac.Sum(nil))
- }
- func (c *HookClient) Close() error {
- if c.HTTPClient != nil {
- // Close idle connections
- if transport, ok := c.HTTPClient.Transport.(*http.Transport); ok {
- transport.CloseIdleConnections()
- }
- // Clear the client
- c.HTTPClient = nil
- }
- return nil
- }
|